authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-28 15:24:00-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-28 18:51:55-08:00
logde7e1ab0f88b02444249d69a59c5fed7c981b6c9
treedf9542e7a2599e14d6b9ade21edb5d9f27b34088
parentb222f6adf6944284280e7ff42bdb6bb2242bce84
signaturelock-open Commit is signed but in an unrecognized format.

feat: bikeshedding and success messages


9 files changed, 289 insertions(+), 170 deletions(-)

example/src/App.tsx+3-3
......@@ -3,7 +3,7 @@ import {
33 createMutationButton,
44 MutationClient,
55 queryClientOptimisticHelpers,
6 useMutation,
6 useMutate,
77} from "@clo/react-mutation";
88import { queryOptions as queryOptions } from "@tanstack/react-query";
99import { useSuspenseQuery } from "@tanstack/react-query";
......@@ -49,7 +49,7 @@ const queryCounter = queryOptions({
4949// await client.invalidateQueries(queryCounter);
5050// },
5151// });
52const mutIncrement = mutationClient.defineBatched({
52const mutIncrement = mutationClient.defineDebounced({
5353 mode: "debounce",
5454 time: 200,
5555
......@@ -92,7 +92,7 @@ const MutationButton = createMutationButton(CustomButton);
9292
9393function Counter() {
9494 const { data: { count } } = useSuspenseQuery(queryCounter);
95 const mutation = useMutation(mutIncrement);
95 const mutation = useMutate(mutIncrement);
9696
9797 return (
9898 <div className="counter-card">
readme.md+74-28
......@@ -10,7 +10,7 @@ patterns and verbose code that is hard to review.
1010
1111The primary gains React Mutation provides are
1212
13- **Automatic error handling**. If a `useMutation` hook does not observe
13- **Automatic error handling**. If a `useMutate` hook does not observe
1414 `isError`, unhandled errors will be propagated to a global handler, which can
1515 display a UI toast. Otherwise, the component can display the error locally.
1616- Optimistic helpers allow defining rollbacks and refetching logic independant
......@@ -22,13 +22,13 @@ The primary gains React Mutation provides are
2222This library declares two kinds of mutations. Each kind has different behavior
2323around concurrent operations.
2424
25- [**Queued Mutations**](#Queued-Mutations): A mutation blocks the UI until it
25- [**Blocking Mutations**](#Blocking-Mutations): A mutation blocks the UI until it
2626 is complete. You press a button, a pending state appears, then it completes.
2727 This works great for forms, creations and deletions, and is similar to React
2828 Query's mutation system.
29- [**Batched Mutations**](#Batched-Mutations): Each call to the mutation applies
30 new optimistic state, and after a debounce or throttle, the new optimistic
31 state is committed to the API. UI never shows a pending state for batches.
29- [**Debounced Mutations**](#Debounced-Mutations): Each call to the mutation applies
30 new optimistic state, and after a debounce (or throttle) the new optimistic
31 state is committed to the API. UI never shows a pending state for these.
3232 This works great for auto-saving input fields, follow buttons, and is
3333 preferred whenever possible.
3434
......@@ -37,7 +37,7 @@ React Mutation starts with a `MutationClient`, which shares global state for an
3737```ts
3838const queryClient = new QueryClient();
3939export const mutations = new MutationClient({
40 // All properties in `context` are available within mutation functions.
40 // All properties in `context` are available within every function.
4141 context: {
4242 client: queryClient,
4343 // Can add any easy helpers for your codebase.
......@@ -45,29 +45,40 @@ export const mutations = new MutationClient({
4545 get: (k: QueryKey) => client.getQueryData(k),
4646 },
4747
48 // Optimistic helpers are a second type of context, only available
49 // within optimistic update functions. The built in React Query helpers
50 // add many query cache mutating operations that automatically
48 // Optimistic helpers are a second type of context, only available within
49 // optimistic update functions. These functions are bound to each mutation,
50 // which means they can handle automatic rollbacks and query invalidation.
5151 getOptimisticHelpers: queryClientOptimisticHelpers(queryClient),
5252
5353 // When call sites do not opt into handling errors, or a pending
5454 // mutation hook is unmounted, errors are sent to this function.
5555 // An example is to bind this to global a UI toast.
56 reportError(description: string, error: unknown) {
57 console.error("Mutation error:", error);
56 reportError(userFriendlyErrorMessage: string, error: unknown) {
57 showToastUI("error", userFriendlyErrorMessage);
58 console.error(error); // or send to telemetry
59 },
60
61 // Similarly, when call sites do opt into handling success.
62 reportSuccess(userFriendlySuccessMessage: string) {
63 showToastUI("success", userFriendlyErrorMessage);
5864 },
5965})
6066
6167```
6268
63### Queued Mutations
69### Blocking Mutations
6470
65A queued mutation is defined with `mutations.defineQueued`.
71A blocking mutation is defined with `mutations.defineBlocking`. Example use cases:
72
73- A form to create a new resource.
74- Button operations such as deleting or resyncing.
75- Any case where it is unclear what the optimistic state should be.
6676
6777```tsx
6878const queryItemList = queryOptions({ ... });
6979const queryItem = (id: string) => queryOptions({ ... });
7080
81// The convention is to name handlers starting with `mut`
7182const mutDeleteItem = mutations.defineQueued({
7283 // `mutate` comes first, is only worried about syncing with the backend.
7384 async mutate(id: string) {
......@@ -76,23 +87,31 @@ const mutDeleteItem = mutations.defineQueued({
7687 },
7788
7889 optimistic({ client, get, helpers, args: [id] }) {
90 // Remove the matching items, but restore and refetch them on failure.
7991 helpers.arrayRemove(queryItemList, (item) => item === id);
92 // Remove this query from the client, but restore as stale and refetch it on failure.
8093 helpers.removeQuery(queryItem);
8194 },
8295
96 // Example: `Could not {description}`
8397 describe({ get, args: [id] }) {
8498 const title = get(queryItem().queryKey)?.title ?? "Unknown Item";
85 return `delete '${title}'`;
99 return `Delete '${get(queryItem(id))?.title ?? 'unknown'}'`;
86100 },
101 // Example: `Successfully {description}`
102 describeResult: ({ get, args: [id] }) =>
103 `Deleted '${get(queryItem(id))?.title ?? 'Unknown Item'}'`,
87104
88 // since the optimistic handler is perfect, there is no need
105 // Since the optimistic handler is perfect, there is no need
89106 // to refetch any data once a success case is hit.
90107 refetchOnSuccess: false,
91108});
92109
110// React example. Since `error` and `result` are not destructed, messages are
111// indicated through UI toasts from the mutation client.
93112export function Example({ id }: { id: string }) {
94113 const { data: list } = useSuspenseQuery(queryItemList);
95 const { run } = useMutation(mutDeleteItem);
114 const { run } = useMutate(mutDeleteItem);
96115
97116 return list.map((id) => <li key={id}>
98117 <Item id={id} />
......@@ -101,27 +120,25 @@ export function Example({ id }: { id: string }) {
101120}
102121```
103122
104### Batched Mutations
123### Debounced Mutations
105124
106A batched mutation is defined with `mutations.defineBatched`.
125A debounced mutation is defined with `mutations.defineBatched`.
107126
108127```tsx
109const mutSetItemName = mutationClient.defineBatched({
110 mode: "debounce",
111 time: 200,
112
113 // start by mutating the optimistic state
128const mutSetItemName = mutationClient.defineDebounced({
129 // Think of your mutator in terms of how it applies optimistic state.
114130 optimistic({ helpers }, id: string, name: string) {
115131 helpers.objSet(queryItem(id), ["title"], name);
116132 },
117 // a value is snapshot before calling `optimistic` and after the
118 // timer. if the snapshots differ, the `commit` function is called.
133 // A value is snapshotted *before* calling `optimistic`, and then again after
134 // the timer. If the snapshots differ, then `commit` function is called.
119135 getValue: ({ get }) => get(queryCounter)?.title ?? "",
120136
121 // batch the same `id`s together
137 // Split different `id`s into their own debounces.
122138 key: ({ args: [id] }) => id,
123139
124 // commit the result to the backend
140 // Commit the result to the backend. Here, you can observe the two snapshotted
141 // values and form an API request.
125142 async commit({ initial, current, args: [id] }) {
126143 const response = await fetch(`/items/${id}`, {
127144 method: "patch",
......@@ -130,6 +147,35 @@ const mutSetItemName = mutationClient.defineBatched({
130147 if (!response.ok) throw new Error(`HTTP ${response.status}`);
131148 },
132149
133 describe: ({ get }) => `rename '${get(queryItem())?.title ?? 'unknown'}'`,
150 describe: ({ get, args: [id] }) =>
151 `Rename '${get(queryItem())?.title ?? 'Unknown Item'}'`,
152 describeResult: ({ get, args: [id] }) =>
153 `Renamed '${get(queryItem(id))?.title ?? 'Unknown Item'}'`,
134154});
155
156// React example. Since the error and result are read in this hook,
157// the success and failure states will be driven through the component UI.
158function Item({ id }: { id: string }) {
159 const { data: item } = useSuspenseQuery(queryItem(id));
160 const { run, isSuccess, errorMessage } = useMutate(mutDeleteItem);
161
162 // TODO: test this pattern. maybe introduce another hook for doing good input
163 // fields that hook could also support an "Undo" button.
164 return <>
165 <input
166 value={item.title}
167 onChange={(e) => {
168 run(e.target.value);
169 }}
170 />
171 {
172 isSuccess
173 ? "Saved"
174 : errorMessage
175 ? "Error: " + errorMessage : null
176 }
177 </>
178}
135179```
180
181###
src/batch.ts+47-5
......@@ -23,7 +23,10 @@ export interface BatchMutationOptions<
2323 */
2424 getValue: (context: Config["context"], ...args: Args) => Optimistic;
2525
26 mode: "debounce" | "throttle";
26 /**
27 * @default "debounce"
28 */
29 mode?: "debounce" | "throttle";
2730 /**
2831 * Milliseconds
2932 * @default 200
......@@ -50,6 +53,17 @@ export interface BatchMutationOptions<
5053 | ((
5154 context: BatchCommitContext<NoInfer<Args>, Optimistic, Config>,
5255 ) => string);
56 /**
57 * Used in success messages.
58 * Phrase it as a complete success message, e.g., "Renamed item successfully"
59 * Set to null to suppress success reporting.
60 */
61 describeResult?:
62 | string
63 | ((
64 context: BatchCommitContext<NoInfer<Args>, Optimistic, Config> & { result: Result },
65 ) => string)
66 | null;
5367 /**
5468 * Refetch all of the data this mutation could have affected.
5569 */
......@@ -97,6 +111,7 @@ interface BatchChannel<Args extends unknown[], Result, Optimistic> {
97111 args: Args;
98112 resolve: (result: Result) => void;
99113 reject: (error: unknown) => void;
114 reportSuccessGlobally?: boolean;
100115 }>;
101116}
102117
......@@ -197,15 +212,33 @@ export class BatchMutation<
197212 return describe;
198213 }
199214
200 /** Calling the mutation in a global scope. Errors are turned into UI toasts. */
215 describeResult(args: Args, initial: Optimistic, current: Optimistic, result: Result): string | undefined {
216 const { describeResult } = this.#options;
217 if (describeResult === null || describeResult === undefined) return undefined;
218 return typeof describeResult === "function"
219 ? describeResult({
220 ...this.#client.context,
221 args,
222 initial,
223 current,
224 result,
225 })
226 : describeResult;
227 }
228
229 /** Calling the mutation in a global scope. Errors and successes are turned into UI toasts. */
201230 run(...args: Args): void {
202 this.runAndReturn(...args).catch((error) => {
231 this.#runAndReturn(args, true).catch((error) => {
203232 this.#client.reportError(error);
204233 });
205234 }
206235
207236 /** Calls the mutation, treating the errors as promise rejection. */
208237 runAndReturn(...args: Args): Promise<Result> {
238 return this.#runAndReturn(args, false);
239 }
240
241 #runAndReturn(args: Args, reportSuccessGlobally: boolean): Promise<Result> {
209242 const key = this.key(args);
210243 const channel = this.#getOrPutChannel(key);
211244
......@@ -260,7 +293,7 @@ export class BatchMutation<
260293
261294 // Create promise for this caller
262295 const { promise, resolve, reject } = Promise.withResolvers<Result>();
263 channel.pending.push({ args, resolve, reject });
296 channel.pending.push({ args, resolve, reject, reportSuccessGlobally });
264297
265298 // Set status to waiting and notify
266299 if (channel.status === "idle") {
......@@ -280,7 +313,7 @@ export class BatchMutation<
280313 ) {
281314 const time = this.#options.time ?? 200;
282315
283 if (this.#options.mode === "debounce") {
316 if (this.#options.mode !== "throttle") {
284317 // Debounce: reset timer on each call
285318 if (channel.timer !== null) {
286319 clearTimeout(channel.timer);
......@@ -364,6 +397,15 @@ export class BatchMutation<
364397 // Resolve all pending promises
365398 pendingItems.forEach(({ resolve }) => resolve(result));
366399
400 // Report success globally if any of the pending items requested it
401 const shouldReportSuccess = pendingItems.some((item) => item.reportSuccessGlobally);
402 if (shouldReportSuccess) {
403 const message = this.describeResult(firstArgs, initial, current, result);
404 if (message && this.#client.reportSuccess) {
405 this.#client.reportSuccess(message);
406 }
407 }
408
367409 // Record commit time for throttle mode
368410 channel.lastCommitTime = Date.now();
369411
src/client.ts+5-2
......@@ -22,6 +22,7 @@ export interface MutationClientOptions<
2222 events: OptimisticEvents,
2323 ) => OptimisticHelpers;
2424 reportError: (error: unknown) => void;
25 reportSuccess?: (message: string) => void;
2526 /**
2627 * Compare two values for deep equality. Used by BatchMutation to determine
2728 * if the optimistic state has changed from the initial snapshot.
......@@ -42,12 +43,14 @@ export class MutationClient<
4243 context: Context;
4344 getOptimisticHelpers: (event: OptimisticEvents) => OptimisticHelpers;
4445 reportError: (error: unknown) => void;
46 reportSuccess?: (message: string) => void;
4547 deepEquals: (a: unknown, b: unknown) => boolean;
4648
4749 constructor(options: MutationClientOptions<Context, OptimisticHelpers>) {
4850 this.context = options.context;
4951 this.getOptimisticHelpers = options.getOptimisticHelpers;
5052 this.reportError = options.reportError;
53 this.reportSuccess = options.reportSuccess;
5154 this.deepEquals = options.deepEquals ?? defaultDeepEquals;
5255 }
5356
......@@ -56,7 +59,7 @@ export class MutationClient<
5659 * You press a button, a pending state appears, then it completes. This works
5760 * great for forms, and is similar to React Query's mutation system.
5861 */
59 defineQueued<const Args extends unknown[], Result>(
62 defineBlocking<const Args extends unknown[], Result>(
6063 options: MutationOptions<
6164 Args,
6265 Result,
......@@ -77,7 +80,7 @@ export class MutationClient<
7780 * works great for auto-saving input fields, follow buttons, and is preferred
7881 * whenever possible.
7982 */
80 defineBatched<const Args extends unknown[], Result, Optimistic>(
83 defineDebounced<const Args extends unknown[], Result, Optimistic>(
8184 options: BatchMutationOptions<
8285 Args,
8386 Result,
src/mod.ts+6-6
......@@ -14,10 +14,10 @@ export type { Mutation, MutationEvent } from "./types.ts";
1414export {
1515 createMutationButton,
1616 type MutationButtonProps,
17 useMutation,
18 type UseMutationError,
19 type UseMutationIdle,
20 type UseMutationResult,
21 type UseMutationResultBase,
22 type UseMutationSuccess,
17 useMutate,
18 type UseMutateError,
19 type UseMutateIdle,
20 type UseMutateResult,
21 type UseMutateResultBase,
22 type UseMutateSuccess,
2323} from "./react.tsx";
src/queued.ts+26-3
......@@ -19,13 +19,22 @@ export interface MutationOptions<
1919 * params type is used to allow type inference. Place this function first to
2020 * ensure TypeScript correctly infers the argument type for the rest of the
2121 * functions.
22 *
23 * In practice, optimistic context is never needed in this function, but it
24 * is provided as the `this` value if you truly desire it.
2225 */
23 mutate: (context: Config["context"], ...args: Args) => Promise<Result>;
26 mutate: (this: Config["context"], ...args: Args) => Promise<Result>;
2427 /**
2528 * Used in error messages and debug tools.
2629 * Phrase it considering the template `Failed to ${describe(...)}`
2730 */
2831 describe: string | ((context: Config["context"] & { args: Args }) => string);
32 /**
33 * Used in success messages.
34 * Phrase it as a complete success message, e.g., "Deleted item successfully"
35 * Set to null to suppress success reporting.
36 */
37 describeResult?: string | ((context: Config["context"] & { args: Args; result: Result }) => string) | null;
2938 /**
3039 * Specifying the optimistic strategy is required. To disable, pass an empty
3140 * function with a comment to document why it isn't needed.
......@@ -33,6 +42,7 @@ export interface MutationOptions<
3342 optimistic: (context: OptimisticContext<Args, Result, Config>) => void;
3443 /**
3544 * Refetch all of the data this mutation could have affected.
45 * Normally, optimistic helpers will perform
3646 * This is called automatically on errors.
3747 */
3848 refetch?: (context: Config["context"] & { args: Args }) => Promise<void>;
......@@ -154,9 +164,22 @@ export class QueuedMutation<
154164 : describe;
155165 }
156166
167 describeResult(args: Args, result: Result): string | undefined {
168 const { describeResult } = this.#options;
169 if (describeResult === null || describeResult === undefined) return undefined;
170 return typeof describeResult === "function"
171 ? describeResult({ ...this.#client.context, args, result })
172 : describeResult;
173 }
174
157175 /** Calling the mutation in a global scope. Errors are turned into UI toasts. */
158176 run(...args: Args) {
159 this.runAndReturn(...args).catch((error) => {
177 this.runAndReturn(...args).then((result) => {
178 const message = this.describeResult(args, result);
179 if (message && this.#client.reportSuccess) {
180 this.#client.reportSuccess(message);
181 }
182 }).catch((error) => {
160183 this.#client.reportError(error);
161184 });
162185 }
......@@ -244,7 +267,7 @@ export class QueuedMutation<
244267 channel.status = "mutating";
245268 this.#notify(channel, "mutating");
246269
247 this.#options.mutate(this.#client.context, ...args).then((result) => {
270 this.#options.mutate.call(this.#client.context, ...args).then((result) => {
248271 // remove rollbacks and apply optimistic success handlers
249272 channel.rollbacks.splice(0, item.rollbacks);
250273 onSuccess.forEach((cb) => cb(result));
src/react.tsx+24-21
......@@ -13,12 +13,12 @@ import type { Mutation } from "./types.ts";
1313 * Subscribe to a mutation's status, as well as accessing a local `run` method.
1414 * The resulting mutation can be used directly or passed to a {@link createMutationButton|mutation button}.
1515 */
16export function useMutation<
16export function useMutate<
1717 Args extends unknown[],
1818 Result,
1919>(
2020 mutation: Mutation<Args, Result> | null,
21): UseMutationResult<Args, Result> {
21): UseMutateResult<Args, Result> {
2222 const [_, setRerender] = useState(0);
2323 const [observer] = useState(() => new Observer<Args, Result>(setRerender));
2424 useEffect(() => () => void observer.reset(), []);
......@@ -29,20 +29,20 @@ export function useMutation<
2929 return observer.binding;
3030}
3131
32export type UseMutationResult<Args extends unknown[], Result> =
33 & UseMutationResultBase<Args>
32export type UseMutateResult<Args extends unknown[], Result> =
33 & UseMutateResultBase<Args>
3434 & (
35 | UseMutationSuccess<Result>
36 | UseMutationError
37 | UseMutationIdle
35 | UseMutateSuccess<Result>
36 | UseMutateError
37 | UseMutateIdle
3838 );
3939
40export interface UseMutationResultBase<Args extends unknown[]> {
40export interface UseMutateResultBase<Args extends unknown[]> {
4141 run: (...args: Args) => void;
4242 clear: () => void;
4343}
4444
45export interface UseMutationSuccess<Result> {
45export interface UseMutateSuccess<Result> {
4646 status: "success";
4747 result: Result;
4848 error: undefined;
......@@ -57,7 +57,7 @@ export interface UseMutationSuccess<Result> {
5757 /** `true` when there is optimistic state applied. */
5858 isOptimisticData: boolean;
5959}
60export interface UseMutationError {
60export interface UseMutateError {
6161 status: "error";
6262 result: undefined;
6363 error: unknown;
......@@ -72,7 +72,7 @@ export interface UseMutationError {
7272 /** `true` when there is optimistic state applied. */
7373 isOptimisticData: boolean;
7474}
75export interface UseMutationIdle {
75export interface UseMutateIdle {
7676 status: "idle" | "mutating";
7777 result: undefined;
7878 error: undefined;
......@@ -90,7 +90,7 @@ export interface UseMutationIdle {
9090
9191type AnyMutationState<Result> =
9292 & Omit<
93 UseMutationIdle,
93 UseMutateIdle,
9494 "status" | "result" | "error" | "isSuccess" | "isError"
9595 >
9696 & {
......@@ -147,7 +147,7 @@ class Observer<Args extends unknown[], Result> {
147147 this.state = initialState();
148148 }
149149
150 binding: UseMutationResult<Args, Result> = ((self: this) => ({
150 binding: UseMutateResult<Args, Result> = ((self: this) => ({
151151 run(...args: Args) {
152152 const mutation = self.mutation;
153153 if (!mutation) return;
......@@ -189,10 +189,13 @@ class Observer<Args extends unknown[], Result> {
189189 },
190190 );
191191 }
192 // use global error handling if this usage of the hook doesnt check for
193 // errors this makes it act pretty awesome in terms of defaults. you don't
194 // have to worry about the errors, they'll surface exactly once.
195 if (self.watched.has("isError") || self.watched.has("error")) {
192 // use global error/success handling if this usage of the hook doesn't check for
193 // errors or success. This makes it act pretty awesome in terms of defaults.
194 // You don't have to worry about the errors/successes, they'll surface exactly once.
195 if (
196 self.watched.has("isError") || self.watched.has("error") ||
197 self.watched.has("isSuccess") || self.watched.has("result")
198 ) {
196199 mutation.runAndReturn(...args).catch(() => {
197200 // caught in event listener
198201 });
......@@ -243,7 +246,7 @@ class Observer<Args extends unknown[], Result> {
243246 self.watched.add("isOptimisticData");
244247 return self.state.isOptimisticData;
245248 },
246 } as UseMutationResult<Args, Result>))(this);
249 } as UseMutateResult<Args, Result>))(this);
247250}
248251
249252interface BaseButtonProps {
......@@ -263,7 +266,7 @@ interface MutationButtonComponent<Props> {
263266export interface MutationButtonProps<Args extends unknown[], Result> {
264267 mutation:
265268 | Mutation<Args, Result>
266 | Pick<UseMutationResult<Args, Result>, "run" | "status" | "isPending">;
269 | Pick<UseMutateResult<Args, Result>, "run" | "status" | "isPending">;
267270 /** Preventing default will interrupt the mutation */
268271 args: Args | ((e: MouseEvent) => Args | null);
269272 /** Preventing default will interrupt the mutation */
......@@ -274,7 +277,7 @@ export interface MutationButtonProps<Args extends unknown[], Result> {
274277 * Wraps a custom button component with logic to execute a mutation. The wrapped
275278 * component must accept `onClick` and an `isPending` property. When the inner
276279 * component emits `onClick`, that will begin the mutation. This is a trival
277 * abstraction on top of `useMutation`, but with type gymnastics to allow safe
280 * abstraction on top of `useMutate`, but with type gymnastics to allow safe
278281 * types.
279282 */
280283export function createMutationButton<Props>(
......@@ -315,7 +318,7 @@ function GenericMutationButton<
315318 const { mutation, args, onClick, ...forwarded } = props;
316319 forwarded satisfies Omit<Props, keyof MutationButtonProps<Args, Result>>;
317320
318 const localHook = useMutation("subscribe" in mutation ? mutation : null);
321 const localHook = useMutate("subscribe" in mutation ? mutation : null);
319322 const state = "subscribe" in mutation ? localHook : mutation;
320323
321324 return (
test/batch.test.ts+30-28
......@@ -65,7 +65,7 @@ test("BatchMutation - basic mutation success with debounce", async () => {
6565 let commitCallCount = 0;
6666 let refetchCallCount = 0;
6767
68 const mutation = client.defineBatched({
68 const mutation = client.defineDebounced({
6969 optimistic({ helpers }, amount: number) {
7070 helpers.increment("counter", amount);
7171 },
......@@ -98,7 +98,7 @@ test("BatchMutation - run() catches errors", async () => {
9898 testStore.clear();
9999 testStore.set("counter", 0);
100100
101 const mutation = client.defineBatched({
101 const mutation = client.defineDebounced({
102102 optimistic({ helpers }, amount: number) {
103103 helpers.increment("counter", amount);
104104 },
......@@ -125,7 +125,7 @@ test("BatchMutation - runAndReturn() rejects on error", async () => {
125125 testStore.clear();
126126 testStore.set("counter", 0);
127127
128 const mutation = client.defineBatched({
128 const mutation = client.defineDebounced({
129129 optimistic({ helpers }, amount: number) {
130130 helpers.increment("counter", amount);
131131 },
......@@ -159,7 +159,7 @@ test("BatchMutation - debounce batches rapid calls", async () => {
159159 let commitCallCount = 0;
160160 const commitArgs: Array<{ initial: number; current: number }> = [];
161161
162 const mutation = client.defineBatched({
162 const mutation = client.defineDebounced({
163163 optimistic({ helpers }, amount: number) {
164164 helpers.increment("counter", amount);
165165 },
......@@ -201,7 +201,7 @@ test("BatchMutation - debounce resets timer on each call", async () => {
201201
202202 let commitCallCount = 0;
203203
204 const mutation = client.defineBatched({
204 const mutation = client.defineDebounced({
205205 optimistic({ helpers }, amount: number) {
206206 helpers.increment("counter", amount);
207207 },
......@@ -250,7 +250,7 @@ test("BatchMutation - debounce separates batches after timeout", async () => {
250250 let commitCallCount = 0;
251251 const commitArgs: Array<{ initial: number; current: number }> = [];
252252
253 const mutation = client.defineBatched({
253 const mutation = client.defineDebounced({
254254 optimistic({ helpers }, amount: number) {
255255 helpers.increment("counter", amount);
256256 },
......@@ -295,7 +295,7 @@ test("BatchMutation - throttle commits immediately on first call", async () => {
295295 let commitTime = 0;
296296 const startTime = Date.now();
297297
298 const mutation = client.defineBatched({
298 const mutation = client.defineDebounced({
299299 optimistic({ helpers }, amount: number) {
300300 helpers.increment("counter", amount);
301301 },
......@@ -325,7 +325,7 @@ test("BatchMutation - throttle batches calls within time window", async () => {
325325 let commitCallCount = 0;
326326 const commitArgs: Array<{ initial: number; current: number }> = [];
327327
328 const mutation = client.defineBatched({
328 const mutation = client.defineDebounced({
329329 optimistic({ helpers }, amount: number) {
330330 helpers.increment("counter", amount);
331331 },
......@@ -377,7 +377,7 @@ test("BatchMutation - throttle allows new batch after time window", async () =>
377377
378378 let commitCallCount = 0;
379379
380 const mutation = client.defineBatched({
380 const mutation = client.defineDebounced({
381381 optimistic({ helpers }, amount: number) {
382382 helpers.increment("counter", amount);
383383 },
......@@ -420,7 +420,7 @@ test("BatchMutation - skips commit when value unchanged", async () => {
420420
421421 let commitCallCount = 0;
422422
423 const mutation = client.defineBatched({
423 const mutation = client.defineDebounced({
424424 optimistic({ helpers }, amount: number) {
425425 helpers.increment("counter", amount);
426426 },
......@@ -479,7 +479,7 @@ test("BatchMutation - uses deepEquals for comparison", async () => {
479479
480480 let commitCallCount = 0;
481481
482 const mutation = client.defineBatched({
482 const mutation = client.defineDebounced({
483483 optimistic({ helpers }, count: number) {
484484 helpers.setCount(count);
485485 },
......@@ -531,7 +531,7 @@ test("BatchMutation - custom deepEquals function", async () => {
531531 testStore.clear();
532532 testStore.set("counter", 0);
533533
534 const mutation = client.defineBatched({
534 const mutation = client.defineDebounced({
535535 optimistic({ helpers }, amount: number) {
536536 helpers.increment("counter", amount);
537537 },
......@@ -546,7 +546,9 @@ test("BatchMutation - custom deepEquals function", async () => {
546546 async refetch() {},
547547 });
548548
549 await mutation.runAndReturn(5);
549 await mutation.runAndReturn(5).catch(() => {
550 // Expected to fail due to commit error
551 });
550552 await delay(30);
551553
552554 // Custom deepEquals should have been called
......@@ -562,7 +564,7 @@ test("BatchMutation - rollback on commit error", async () => {
562564 testStore.clear();
563565 testStore.set("counter", 10);
564566
565 const mutation = client.defineBatched({
567 const mutation = client.defineDebounced({
566568 optimistic({ helpers }, amount: number) {
567569 helpers.increment("counter", amount);
568570 },
......@@ -594,7 +596,7 @@ test("BatchMutation - error event includes error details", async () => {
594596
595597 const tracker = createEventTracker<number>();
596598
597 const mutation = client.defineBatched({
599 const mutation = client.defineDebounced({
598600 optimistic({ helpers }, amount: number) {
599601 helpers.increment("counter", amount);
600602 },
......@@ -629,7 +631,7 @@ test("BatchMutation - key() returns JSON stringified key", () => {
629631 const { client } = createTestClient();
630632 testStore.clear();
631633
632 const mutation = client.defineBatched({
634 const mutation = client.defineDebounced({
633635 optimistic(_ctx, _id: string) {},
634636 mode: "debounce",
635637 time: 20,
......@@ -649,7 +651,7 @@ test("BatchMutation - key() can return array", () => {
649651 const { client } = createTestClient();
650652 testStore.clear();
651653
652 const mutation = client.defineBatched({
654 const mutation = client.defineDebounced({
653655 optimistic(_ctx, _id: string) {},
654656 mode: "debounce",
655657 time: 20,
......@@ -676,7 +678,7 @@ test("BatchMutation - different keys create separate batches", async () => {
676678
677679 let commitCallCount = 0;
678680
679 const mutation = client.defineBatched({
681 const mutation = client.defineDebounced({
680682 optimistic({ helpers }, key: string, amount: number) {
681683 helpers.increment(`counter-${key}`, amount);
682684 },
......@@ -713,7 +715,7 @@ test("BatchMutation - describe() with string", () => {
713715 const { client } = createTestClient();
714716 testStore.clear();
715717
716 const mutation = client.defineBatched({
718 const mutation = client.defineDebounced({
717719 optimistic(_ctx, _amount: number) {},
718720 mode: "debounce",
719721 time: 20,
......@@ -733,7 +735,7 @@ test("BatchMutation - describe() with function", () => {
733735 const { client } = createTestClient();
734736 testStore.clear();
735737
736 const mutation = client.defineBatched({
738 const mutation = client.defineDebounced({
737739 optimistic(_ctx, _amount: number) {},
738740 mode: "debounce",
739741 time: 20,
......@@ -758,7 +760,7 @@ test("BatchMutation - all pending promises resolve with same result", async () =
758760 testStore.clear();
759761 testStore.set("counter", 0);
760762
761 const mutation = client.defineBatched({
763 const mutation = client.defineDebounced({
762764 optimistic({ helpers }, amount: number) {
763765 helpers.increment("counter", amount);
764766 },
......@@ -794,7 +796,7 @@ test("BatchMutation - all pending promises reject with same error", async () =>
794796 testStore.clear();
795797 testStore.set("counter", 0);
796798
797 const mutation = client.defineBatched({
799 const mutation = client.defineDebounced({
798800 optimistic({ helpers }, amount: number) {
799801 helpers.increment("counter", amount);
800802 },
......@@ -837,7 +839,7 @@ test("BatchMutation - handles empty getValue result", async () => {
837839
838840 let commitCallCount = 0;
839841
840 const mutation = client.defineBatched({
842 const mutation = client.defineDebounced({
841843 optimistic({ helpers }, amount: number) {
842844 helpers.setValue("nonexistent", amount);
843845 },
......@@ -865,7 +867,7 @@ test("BatchMutation - channel cleanup after idle with no listeners", async () =>
865867 testStore.clear();
866868 testStore.set("counter", 0);
867869
868 const mutation = client.defineBatched({
870 const mutation = client.defineDebounced({
869871 optimistic({ helpers }, amount: number) {
870872 helpers.increment("counter", amount);
871873 },
......@@ -900,7 +902,7 @@ test("BatchMutation - default time is 200ms", async () => {
900902 let commitTime: number | null = null;
901903 const startTime = Date.now();
902904
903 const mutation = client.defineBatched({
905 const mutation = client.defineDebounced({
904906 optimistic({ helpers }, amount: number) {
905907 helpers.increment("counter", amount);
906908 },
......@@ -931,7 +933,7 @@ test("BatchMutation - context is passed to getValue", async () => {
931933
932934 let receivedUserId: string | undefined;
933935
934 const mutation = client.defineBatched({
936 const mutation = client.defineDebounced({
935937 optimistic({ helpers }, amount: number) {
936938 helpers.increment("counter", amount);
937939 },
......@@ -962,7 +964,7 @@ test("BatchMutation - context is passed to commit", async () => {
962964
963965 let receivedUserId: string | undefined;
964966
965 const mutation = client.defineBatched({
967 const mutation = client.defineDebounced({
966968 optimistic({ helpers }, amount: number) {
967969 helpers.increment("counter", amount);
968970 },
......@@ -991,7 +993,7 @@ test("BatchMutation - first args are used for commit", async () => {
991993
992994 let receivedArgs: [string, number] | undefined;
993995
994 const mutation = client.defineBatched({
996 const mutation = client.defineDebounced({
995997 optimistic({ helpers }, _label: string, amount: number) {
996998 helpers.increment("counter", amount);
997999 },
test/queued.test.ts+74-74
......@@ -45,8 +45,8 @@ test("QueuedMutation - basic mutation success", async () => {
4545 let mutateCallCount = 0;
4646 let refetchCallCount = 0;
4747
48 const mutation = client.defineQueued({
49 async mutate(_, value: string) {
48 const mutation = client.defineBlocking({
49 async mutate(value: string) {
5050 mutateCallCount++;
5151 await delay(10);
5252 return `result-${value}`;
......@@ -73,8 +73,8 @@ test("QueuedMutation - basic mutation success", async () => {
7373test("QueuedMutation - run() catches errors", async () => {
7474 const { client, errors } = createTestClient();
7575
76 const mutation = client.defineQueued({
77 async mutate(_, _value: string) {
76 const mutation = client.defineBlocking({
77 async mutate(_value: string) {
7878 throw new Error("mutation failed");
7979 },
8080 describe: "failing mutation",
......@@ -92,8 +92,8 @@ test("QueuedMutation - run() catches errors", async () => {
9292test("QueuedMutation - runAndReturn() rejects on error", async () => {
9393 const { client } = createTestClient();
9494
95 const mutation = client.defineQueued({
96 async mutate(_, _value: string) {
95 const mutation = client.defineBlocking({
96 async mutate(_value: string) {
9797 throw new Error("mutation failed");
9898 },
9999 describe: "failing mutation",
......@@ -112,8 +112,8 @@ test("QueuedMutation - optimistic updates are applied immediately", async () =>
112112 const { client } = createTestClient();
113113 testStore.clear();
114114
115 const mutation = client.defineQueued({
116 async mutate(_, _key: string, value: string) {
115 const mutation = client.defineBlocking({
116 async mutate(_key: string, value: string) {
117117 await delay(50);
118118 return value;
119119 },
......@@ -139,8 +139,8 @@ test("QueuedMutation - rollback on error", async () => {
139139 const { client } = createTestClient();
140140 testStore.clear();
141141
142 const mutation = client.defineQueued({
143 async mutate(_, _key: string, _value: string) {
142 const mutation = client.defineBlocking({
143 async mutate(_key: string, _value: string) {
144144 await delay(10);
145145 throw new Error("mutation failed");
146146 },
......@@ -162,8 +162,8 @@ test("QueuedMutation - onSuccess callback is called", async () => {
162162 const { client } = createTestClient();
163163 const successResults: string[] = [];
164164
165 const mutation = client.defineQueued({
166 async mutate(_, value: string) {
165 const mutation = client.defineBlocking({
166 async mutate(value: string) {
167167 return `result-${value}`;
168168 },
169169 describe: "test mutation",
......@@ -184,8 +184,8 @@ test("QueuedMutation - mutations with same key execute serially", async () => {
184184 const { client } = createTestClient();
185185 const executionOrder: string[] = [];
186186
187 const mutation = client.defineQueued({
188 async mutate(_, id: string) {
187 const mutation = client.defineBlocking({
188 async mutate(id: string) {
189189 executionOrder.push(`start-${id}`);
190190 await delay(20);
191191 executionOrder.push(`end-${id}`);
......@@ -215,8 +215,8 @@ test("QueuedMutation - mutations with different keys execute in parallel", async
215215 const { client } = createTestClient();
216216 const executionOrder: string[] = [];
217217
218 const mutation = client.defineQueued({
219 async mutate(_, id: string) {
218 const mutation = client.defineBlocking({
219 async mutate(id: string) {
220220 executionOrder.push(`start-${id}`);
221221 await delay(20);
222222 executionOrder.push(`end-${id}`);
......@@ -244,8 +244,8 @@ test("QueuedMutation - mutations with different keys execute in parallel", async
244244test("QueuedMutation - key() returns JSON stringified key", () => {
245245 const { client } = createTestClient();
246246
247 const mutation = client.defineQueued({
248 async mutate(_, id: string) {
247 const mutation = client.defineBlocking({
248 async mutate(id: string) {
249249 return id;
250250 },
251251 describe: "test mutation",
......@@ -263,8 +263,8 @@ test("QueuedMutation - key() returns JSON stringified key", () => {
263263test("QueuedMutation - key() defaults to 'shared' when no key function", () => {
264264 const { client } = createTestClient();
265265
266 const mutation = client.defineQueued({
267 async mutate(_, id: string) {
266 const mutation = client.defineBlocking({
267 async mutate(id: string) {
268268 return id;
269269 },
270270 describe: "test mutation",
......@@ -278,8 +278,8 @@ test("QueuedMutation - key() defaults to 'shared' when no key function", () => {
278278test("QueuedMutation - key() can return array", () => {
279279 const { client } = createTestClient();
280280
281 const mutation = client.defineQueued({
282 async mutate(_, _userId: string, _itemId: string) {
281 const mutation = client.defineBlocking({
282 async mutate(_userId: string, _itemId: string) {
283283 return "result";
284284 },
285285 describe: "test mutation",
......@@ -300,8 +300,8 @@ test("QueuedMutation - key() can return array", () => {
300300test("QueuedMutation - describe() with string", () => {
301301 const { client } = createTestClient();
302302
303 const mutation = client.defineQueued({
304 async mutate(_, value: string) {
303 const mutation = client.defineBlocking({
304 async mutate(value: string) {
305305 return value;
306306 },
307307 describe: "create item",
......@@ -315,8 +315,8 @@ test("QueuedMutation - describe() with string", () => {
315315test("QueuedMutation - describe() with function", () => {
316316 const { client } = createTestClient();
317317
318 const mutation = client.defineQueued({
319 async mutate(_, id: string) {
318 const mutation = client.defineBlocking({
319 async mutate(id: string) {
320320 return id;
321321 },
322322 describe({ args }) {
......@@ -333,8 +333,8 @@ test("QueuedMutation - describe() with function", () => {
333333test("QueuedMutation - describe() receives context", () => {
334334 const { client } = createTestClient();
335335
336 const mutation = client.defineQueued({
337 async mutate(_, id: string) {
336 const mutation = client.defineBlocking({
337 async mutate(id: string) {
338338 return id;
339339 },
340340 describe({ userId, args }) {
......@@ -355,8 +355,8 @@ test("QueuedMutation - subscribe() tracks mutation events", async () => {
355355 const { client } = createTestClient();
356356 const tracker = createEventTracker<string>();
357357
358 const mutation = client.defineQueued({
359 async mutate(_, value: string) {
358 const mutation = client.defineBlocking({
359 async mutate(value: string) {
360360 await delay(10);
361361 return `result-${value}`;
362362 },
......@@ -384,8 +384,8 @@ test("QueuedMutation - unsubscribe stops receiving events", async () => {
384384 const { client } = createTestClient();
385385 const tracker = createEventTracker<string>();
386386
387 const mutation = client.defineQueued({
388 async mutate(_, value: string) {
387 const mutation = client.defineBlocking({
388 async mutate(value: string) {
389389 await delay(10);
390390 return value;
391391 },
......@@ -411,8 +411,8 @@ test("QueuedMutation - refetchOnSuccess can be disabled", async () => {
411411 const { client } = createTestClient();
412412 let refetchCallCount = 0;
413413
414 const mutation = client.defineQueued({
415 async mutate(_, _value: string) {
414 const mutation = client.defineBlocking({
415 async mutate(_value: string) {
416416 return _value;
417417 },
418418 describe: "test mutation",
......@@ -432,8 +432,8 @@ test("QueuedMutation - refetch is called on error", async () => {
432432 const { client } = createTestClient();
433433 let refetchCallCount = 0;
434434
435 const mutation = client.defineQueued({
436 async mutate(_, _value: string) {
435 const mutation = client.defineBlocking({
436 async mutate(_value: string) {
437437 throw new Error("mutation failed");
438438 },
439439 describe: "failing mutation",
......@@ -452,8 +452,8 @@ test("QueuedMutation - queued mutations are cancelled on error", async () => {
452452 const { client } = createTestClient();
453453 const executionOrder: string[] = [];
454454
455 const mutation = client.defineQueued({
456 async mutate(_, id: string) {
455 const mutation = client.defineBlocking({
456 async mutate(id: string) {
457457 executionOrder.push(`start-${id}`);
458458 await delay(10);
459459 if (id === "1") {
......@@ -486,8 +486,8 @@ test("QueuedMutation - rollbacks are called in reverse order on error", async ()
486486 const { client } = createTestClient();
487487 const rollbackOrder: number[] = [];
488488
489 const mutation = client.defineQueued({
490 async mutate(_, _value: string) {
489 const mutation = client.defineBlocking({
490 async mutate(_value: string) {
491491 throw new Error("mutation failed");
492492 },
493493 describe: "failing mutation",
......@@ -509,8 +509,8 @@ test("QueuedMutation - multiple mutations: rollbacks only affect failed mutation
509509 const { client } = createTestClient();
510510 const rollbackOrder: string[] = [];
511511
512 const mutation = client.defineQueued({
513 async mutate(_, id: string) {
512 const mutation = client.defineBlocking({
513 async mutate(id: string) {
514514 await delay(10);
515515 if (id === "fail") {
516516 throw new Error("mutation failed");
......@@ -542,8 +542,8 @@ test("QueuedMutation - onRestore throws error if called after optimistic phase",
542542 const { client } = createTestClient();
543543 let capturedOnRestore: ((cb: () => void) => void) | null = null;
544544
545 const mutation = client.defineQueued({
546 async mutate(_, _value: string) {
545 const mutation = client.defineBlocking({
546 async mutate(_value: string) {
547547 return "result";
548548 },
549549 describe: "test mutation",
......@@ -573,8 +573,8 @@ test("QueuedMutation - onSuccess throws error if called after optimistic phase",
573573 const { client } = createTestClient();
574574 let capturedOnSuccess: ((cb: (result: string) => void) => void) | null = null;
575575
576 const mutation = client.defineQueued({
577 async mutate(_, _value: string) {
576 const mutation = client.defineBlocking({
577 async mutate(_value: string) {
578578 return "result";
579579 },
580580 describe: "test mutation",
......@@ -603,8 +603,8 @@ test("QueuedMutation - onSuccess throws error if called after optimistic phase",
603603test("QueuedMutation - error during optimistic update is rejected immediately", async () => {
604604 const { client } = createTestClient();
605605
606 const mutation = client.defineQueued({
607 async mutate(_, _value: string) {
606 const mutation = client.defineBlocking({
607 async mutate(_value: string) {
608608 return "result";
609609 },
610610 describe: "test mutation",
......@@ -625,8 +625,8 @@ test("QueuedMutation - error during optimistic update rolls back registered call
625625 const { client } = createTestClient();
626626 const rollbackOrder: number[] = [];
627627
628 const mutation = client.defineQueued({
629 async mutate(_, _value: string) {
628 const mutation = client.defineBlocking({
629 async mutate(_value: string) {
630630 return "result";
631631 },
632632 describe: "test mutation",
......@@ -648,8 +648,8 @@ test("QueuedMutation - error during optimistic update rolls back registered call
648648test("QueuedMutation - refetch errors are reported but don't fail mutation", async () => {
649649 const { client, errors } = createTestClient();
650650
651 const mutation = client.defineQueued({
652 async mutate(_, value: string) {
651 const mutation = client.defineBlocking({
652 async mutate(value: string) {
653653 return value;
654654 },
655655 describe: "test mutation",
......@@ -674,8 +674,8 @@ test("QueuedMutation - optimistic function receives args and helpers", async ()
674674 let receivedArgs: unknown[] | undefined;
675675 let receivedHelpers: unknown | undefined;
676676
677 const mutation = client.defineQueued({
678 async mutate(_, _value: string) {
677 const mutation = client.defineBlocking({
678 async mutate(_value: string) {
679679 return "result";
680680 },
681681 describe: "test mutation",
......@@ -697,8 +697,8 @@ test("QueuedMutation - refetch receives context and args", async () => {
697697 let receivedUserId: string | undefined;
698698 let receivedArgs: unknown[] | undefined;
699699
700 const mutation = client.defineQueued({
701 async mutate(_, _id: string, value: string) {
700 const mutation = client.defineBlocking({
701 async mutate(_id: string, value: string) {
702702 return value;
703703 },
704704 describe: "test mutation",
......@@ -719,8 +719,8 @@ test("QueuedMutation - notifies error on mutation failure", async () => {
719719 const { client } = createTestClient();
720720 const tracker = createEventTracker<string>();
721721
722 const mutation = client.defineQueued({
723 async mutate(_, _value: string) {
722 const mutation = client.defineBlocking({
723 async mutate(_value: string) {
724724 await delay(10);
725725 throw new Error("mutation failed");
726726 },
......@@ -747,8 +747,8 @@ test("QueuedMutation - multiple subscribers receive events", async () => {
747747 const tracker1 = createEventTracker<string>();
748748 const tracker2 = createEventTracker<string>();
749749
750 const mutation = client.defineQueued({
751 async mutate(_, value: string) {
750 const mutation = client.defineBlocking({
751 async mutate(value: string) {
752752 await delay(5);
753753 return value;
754754 },
......@@ -774,8 +774,8 @@ test("QueuedMutation - onSuccess is called before mutation resolves", async () =
774774 const { client } = createTestClient();
775775 const callOrder: string[] = [];
776776
777 const mutation = client.defineQueued({
778 async mutate(_, value: string) {
777 const mutation = client.defineBlocking({
778 async mutate(value: string) {
779779 return value;
780780 },
781781 describe: "test mutation",
......@@ -804,8 +804,8 @@ test("QueuedMutation - result is passed to notification on success", async () =>
804804 const { client } = createTestClient();
805805 const tracker = createEventTracker<string>();
806806
807 const mutation = client.defineQueued({
808 async mutate(_, value: string) {
807 const mutation = client.defineBlocking({
808 async mutate(value: string) {
809809 await delay(5);
810810 return `result-${value}`;
811811 },
......@@ -834,8 +834,8 @@ test("QueuedMutation - channel is reused for same key", async () => {
834834 const { client } = createTestClient();
835835 const events: string[] = [];
836836
837 const mutation = client.defineQueued({
838 async mutate(_, value: string) {
837 const mutation = client.defineBlocking({
838 async mutate(value: string) {
839839 events.push(`mutate-${value}`);
840840 return value;
841841 },
......@@ -859,8 +859,8 @@ test("QueuedMutation - channel is reused for same key", async () => {
859859test("QueuedMutation - empty queue after all mutations complete", async () => {
860860 const { client } = createTestClient();
861861
862 const mutation = client.defineQueued({
863 async mutate(_, value: string) {
862 const mutation = client.defineBlocking({
863 async mutate(value: string) {
864864 await delay(5);
865865 return value;
866866 },
......@@ -893,8 +893,8 @@ test("QueuedMutation - multiple onSuccess callbacks are all called", async () =>
893893 const { client } = createTestClient();
894894 const results: string[] = [];
895895
896 const mutation = client.defineQueued({
897 async mutate(_, value: string) {
896 const mutation = client.defineBlocking({
897 async mutate(value: string) {
898898 return value;
899899 },
900900 describe: "test mutation",
......@@ -916,8 +916,8 @@ test("QueuedMutation - refetchOnSuccess false skips refetch", async () => {
916916 const { client } = createTestClient();
917917 let refetchCalled = false;
918918
919 const mutation = client.defineQueued({
920 async mutate(_, value: string) {
919 const mutation = client.defineBlocking({
920 async mutate(value: string) {
921921 return value;
922922 },
923923 describe: "test mutation",
......@@ -938,8 +938,8 @@ test("QueuedMutation - refetchOnSuccess false skips refetch", async () => {
938938test("QueuedMutation - refetch error after mutation failure is reported", async () => {
939939 const { client, errors } = createTestClient();
940940
941 const mutation = client.defineQueued({
942 async mutate(_, _value: string) {
941 const mutation = client.defineBlocking({
942 async mutate(_value: string) {
943943 throw new Error("mutation failed");
944944 },
945945 describe: "failing mutation",