authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-02-12 16:07:36-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-02-12 16:11:07-08:00
log2f39b6a4fe105ba99110ed4a2b9b63124cccbfcc
tree9665344e6da0a3a798e96f2a10f3ee51534dce9c
parente3d6a4b906d60a10abef5cb9611947b52c979f1a
signaturelock-open Commit is signed but in an unrecognized format.

fix: pass context to optimistic handlers

honestly really sus that ts didnt catch this bug. but i consider it a success that we had so many mutations at work that didn't requires directly accessing context in an optimistic updater -- it means the helpers pattern is too goated.

5 files changed, 56 insertions(+), 3 deletions(-)

jsr.json+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1{1{
2 "name": "@clo/react-mutation",2 "name": "@clo/react-mutation",
3 "version": "2.0.0",3 "version": "2.0.1",
4 "exports": {4 "exports": {
5 ".": "./src/mod.ts",5 ".": "./src/mod.ts",
6 "./tanstack-query.ts": "./src/tanstack-query.ts",6 "./tanstack-query.ts": "./src/tanstack-query.ts",
readme.changes.md+6
...@@ -1,5 +1,11 @@...@@ -1,5 +1,11 @@
1# notable changes in React Mutation1# notable changes in React Mutation
22
3## v2.0.1
4
5### bugfixes
6
7- typescript violation not passing client context to optimistic handlers
8
3## v29## v2
410
5### breaking11### breaking
src/mutation.ts+2
...@@ -413,6 +413,7 @@ export class BlockingMutation<...@@ -413,6 +413,7 @@ export class BlockingMutation<
413413
414 try {414 try {
415 this.#options.optimistic({415 this.#options.optimistic({
416 ...this.#client.context,
416 args,417 args,
417 helpers: channel.helpers,418 helpers: channel.helpers,
418 onRestore,419 onRestore,
...@@ -623,6 +624,7 @@ export class BlockingMutation<...@@ -623,6 +624,7 @@ export class BlockingMutation<
623624
624 try {625 try {
625 this.#options.optimistic({626 this.#options.optimistic({
627 ...this.client.context,
626 args,628 args,
627 helpers: channel.helpers,629 helpers: channel.helpers,
628 onRestore,630 onRestore,
test/optimistic.ts created+45
...@@ -0,0 +1,45 @@
1import { delay } from "@clo/lib/async.ts";
2import { assertEquals } from "@std/assert";
3import { test, vi } from "vitest";
4import { createTestMutationClient } from "./share.ts";
5
6test("passes context value to functions", async () => {
7 vi.useFakeTimers();
8 const { client, successMessages } = createTestMutationClient();
9 const calls: string[] = [];
10
11 let values: number[] = [];
12 const mutTest = client.define({
13 async mutate() {
14 values.push(this.contextValue);
15 calls.push("mutate");
16 await delay(100);
17 },
18 describe: ({ contextValue }) => {
19 values.push(contextValue);
20 calls.push("describe");
21 return "test action";
22 },
23 describeResult: ({ contextValue }) => {
24 values.push(contextValue);
25 calls.push("describe");
26 return "tested action";
27 },
28 optimistic: ({ contextValue, onRefetch, onRestore }) => {
29 values.push(contextValue);
30 onRefetch(async () => void calls.push("refetch"));
31 onRestore(async () => void calls.push("restore"));
32 calls.push("optimistic");
33 },
34 refetchOnSuccess: false,
35 });
36
37 mutTest.run();
38 await vi.advanceTimersByTimeAsync(200);
39
40 // Even with refetchOnSuccess=false, errors should still trigger refetch
41 assertEquals(calls.includes("refetch"), true, "refetch should be called on error");
42 assertEquals(calls.includes("restore"), true, "restore should be called on error");
43 assertEquals(successMessages.length, 1);
44 assertEquals(values, [42, 42, 42]);
45});
test/share.ts+2-2
...@@ -2,7 +2,7 @@...@@ -2,7 +2,7 @@
2import { MutationClient } from "../src/client.ts";2import { MutationClient } from "../src/client.ts";
33
4export interface TestMutationClient {4export interface TestMutationClient {
5 client: MutationClient<{}, {}>;5 client: MutationClient<{ contextValue: number }, {}>;
6 errorMessages: Array<{ message: string; error: unknown }>;6 errorMessages: Array<{ message: string; error: unknown }>;
7 successMessages: string[];7 successMessages: string[];
8}8}
...@@ -12,7 +12,7 @@ export function createTestMutationClient(): TestMutationClient {...@@ -12,7 +12,7 @@ export function createTestMutationClient(): TestMutationClient {
12 const successMessages: string[] = [];12 const successMessages: string[] = [];
1313
14 const client = new MutationClient({14 const client = new MutationClient({
15 context: {},15 context: { contextValue: 42 },
16 getOptimisticHelpers: () => ({}),16 getOptimisticHelpers: () => ({}),
17 reportError: (message: string, error: unknown) => {17 reportError: (message: string, error: unknown) => {
18 errorMessages.push({ message, error });18 errorMessages.push({ message, error });