refactor: consolidate shared mutation option adapters - #158
Merged
Conversation
The three mutation hooks carried copies of the same two pieces of logic:
- resolving the public `mutationFn` option (an Observable or a function
returning one) to its source Observable — duplicated verbatim in
useMutation$, useSwitchMutation$ and useConcatMutation$.
- adapting the user-facing onMutate/onSuccess/onError/onSettled callbacks
to the wrapped `{ variables, ... }` envelope the inner mutation runs
with — duplicated between useSwitchMutation$ and useConcatMutation$.
Extract both into src/lib/queries/mutationOptions.ts (internal, not
re-exported from the package index) and point the three hooks at it.
Behavior-preserving: same resolution branch, same conditional onMutate
wrapping, same optional-chaining for the other callbacks. All 134 tests
pass and the built bundle shrinks from 20.00 kB to 19.20 kB.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiNQdVnmenWmgf24TfCV9b
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consolidation: shared mutation option adapters
What was duplicated
The three mutation hooks in
src/lib/queries/carried copies of the same two pieces of logic:mutationFnsource resolution — thetypeof mutationFn === "function" ? mutationFn(variables) : mutationFnbranch that resolves the public option (an Observable or a function returning one) to its source Observable. Duplicated verbatim, 3×:src/lib/queries/useMutation$.tssrc/lib/queries/useSwitchMutation$.tssrc/lib/queries/useConcatMutation$.tsCallback envelope adaptation —
useSwitchMutation$anduseConcatMutation$both run their inner mutation with the user variables wrapped in an envelope ({ variables, abort }/{ variables, ready$ }), and both reimplemented the same four wrappers that unwrap{ variables }before forwarding to the user-facingonMutate/onSuccess/onError/onSettled(including the same conditionalonMutatehandling). Duplicated 2×:src/lib/queries/useSwitchMutation$.tssrc/lib/queries/useConcatMutation$.tsWhat it became
A new internal module
src/lib/queries/mutationOptions.ts(deliberately not re-exported fromsrc/index.ts, so no public API change — same placement pattern as the existing sharedcreateObservableQueryFn.ts) exporting:resolveMutationFnSource(mutationFn, variables)— used by all three hooks.adaptCallbacksToWrappedVariables(options)— used by both wrapper hooks; its return type keys are required so spreading it after...optionsoverrides (rather than unions with) the raw-variables callbacks.Why they are truly the same concept
All three hooks implement the same public
UseMutation$Options.mutationFncontract, and both wrapper hooks implement the same "inner mutation runs with enveloped variables, user callbacks see raw variables" bridge. A fix to either behavior (e.g. the unwrap logic or a new callback) previously had to be applied in two or three places in lockstep.LOC / size delta
dist/index.cjs), objective confirmation that duplicated code was removed.Behavior preservation
No behavior change: same resolution branch, same conditional
onMutatewrapping, same optional-chaining semantics for the other callbacks, same object-spread ordering (explicitmutationFnand adapted callbacks override the raw options).Gates (baseline was fully green; identical after the change):
npm run check(biome) ✅npm run build(tsc + vite) ✅npm run test:ci— 23 files, 134/134 tests ✅Reviewed and skipped (for future runs)
QueriesOptions$/QueriesResults$recursive types inuseQueries$.ts: near-identical shape, but they deliberately mirror TanStack's upstreamQueriesOptions/QueriesResultspair — merging them would obscure the correspondence.createLocalStorageAdapter/createLocalforageAdapter: both JSON-wrap a backing store, but the localforage adapter's no-opremoveItem/clearlook like divergent behavior, not a copy — unifying would change semantics or need mode flags.*.test.tsx): intentional per-test explicitness; low value to consolidate.Generated by Claude Code