authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-30 17:37:38-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-30 17:38:12-08:00
log9fe44b404d1cfae5e4751ac3ef020ef8da521b1a
tree8b51c37b5ffc230b053e5a5a9256b9ffab28c7b6
parent30b230b64fca127fd768443d19b768ab5a7f88a2
signaturebadge-check Signed by SSH key SHA256:xbd+BjjhyBfwk7GVoURf9Yx0gzDerHbvYv7SddNWmAs

feat: add runAsHeadlessPromise


4 files changed, 56 insertions(+), 1 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": "1.0.0",3 "version": "1.1.0",
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 created+5
...@@ -0,0 +1,5 @@
1# notable changes in React Mutation
2
3## v1.1
4
5- Add `runAsHeadlessPromise`. The function name is intentionally long to avoid using it, please use `runWithOptions` instead.
src/mutation.ts+40
...@@ -256,6 +256,46 @@ export class BlockingMutation<...@@ -256,6 +256,46 @@ export class BlockingMutation<
256 this.runWithOptions(...args, {});256 this.runWithOptions(...args, {});
257 }257 }
258258
259 /**
260 * Using this in any situation is likely incorrect. Use {@linkcode runWithOptions}
261 * to handle success and error.
262 *
263 * By using this, you must handle the success and error conditions of the
264 * promise, or else the user will never see the result on screen. If the
265 * mutation has snapshots, be aware that cancelled mutations are implemented
266 * with promises that never resolve.
267 */
268 runAsHeadlessPromise(...array: [...Args, RunOptions<Result>]): Promise<Result> {
269 if (!this.#client.enabled) {
270 throw new Error(
271 "MutationClient was passed enabled: false. Are you trying to perform a mutation from SSR?",
272 );
273 }
274
275 const args = array.slice() as Args;
276 const { onSuccess, onSuccessDataOnly, onError, onSettled, onRestore } = args
277 .pop() as RunOptions<Result>;
278 const promise = this.#runWithOptions(args, onRestore, true);
279 return promise.then((result) => {
280 // Call user handlers
281 onSuccess?.(result);
282 onSuccessDataOnly?.(result);
283 onSettled?.({ status: "success", result });
284 return result;
285 }).catch((caught: unknown) => {
286 // Extract error and description if this is a wrapped mutation error
287 const isMutationError = (caught as MutationError)?.__mutationError === true;
288 const error = isMutationError ? (caught as MutationError).error : caught;
289 const description = isMutationError ? (caught as MutationError).description : this.describe(...args);
290
291 // Call user handlers with the unwrapped error
292 onError?.(error);
293 onSettled?.({ status: "error", error });
294
295 throw caught;
296 });
297 }
298
259 /** Calls the mutation with custom handlers that can suppress global handlers. */299 /** Calls the mutation with custom handlers that can suppress global handlers. */
260 runWithOptions(...array: [...Args, RunOptions<Result>]): void {300 runWithOptions(...array: [...Args, RunOptions<Result>]): void {
261 if (!this.#client.enabled) {301 if (!this.#client.enabled) {
src/types.ts+10
...@@ -5,6 +5,16 @@ export interface Mutation<Args extends unknown[], Result> {...@@ -5,6 +5,16 @@ export interface Mutation<Args extends unknown[], Result> {
5 run(...args: Args): void;5 run(...args: Args): void;
6 /** Calls the mutation with custom handlers that can suppress global handlers. */6 /** Calls the mutation with custom handlers that can suppress global handlers. */
7 runWithOptions(...args: [...args: Args, options: RunOptions<Result>]): void;7 runWithOptions(...args: [...args: Args, options: RunOptions<Result>]): void;
8 /**
9 * Using this in any situation is likely incorrect, even internally.
10 * Use {@linkcode runWithOptions} to handle success and error.
11 *
12 * By using this, you must handle the success and error conditions of the
13 * promise, or else the user will never see the result on screen. If the
14 * mutation has snapshots, be aware that cancelled mutations are implemented
15 * with promises that never resolve.
16 */
17 runAsHeadlessPromise(...array: [...Args, RunOptions<Result>]): Promise<Result>;
818
9 /** Returns the concurrency key used for a given set of arguments */19 /** Returns the concurrency key used for a given set of arguments */
10 key(args: Args): string;20 key(args: Args): string;