| ... | @@ -112,11 +112,22 @@ interface PendingDebouncedState<Args extends unknown[], Result> { | ... | @@ -112,11 +112,22 @@ interface PendingDebouncedState<Args extends unknown[], Result> { |
| 112 | shouldCallGlobalHandler: boolean; | 112 | shouldCallGlobalHandler: boolean; |
| 113 | } | 113 | } |
| 114 | | 114 | |
| 115 | /** Wrapper for errors that includes the description captured before rollback */ | 115 | /** Internal wrapper that preserves the pre-rollback description for reporting. */ |
| 116 | interface MutationError { | 116 | class MutationError extends Error { |
| 117 | __mutationError: true; | 117 | constructor( |
| 118 | error: unknown; | 118 | readonly error: unknown, |
| 119 | description: string; | 119 | readonly description: string, |
| | 120 | ) { |
| | 121 | super(errMessage(error), { cause: error }); |
| | 122 | this.name = "MutationError"; |
| | 123 | } |
| | 124 | } |
| | 125 | |
| | 126 | function unwrapMutationError(caught: unknown) { |
| | 127 | if (caught instanceof MutationError) { |
| | 128 | return { error: caught.error, description: caught.description }; |
| | 129 | } |
| | 130 | return { error: caught, description: null }; |
| 120 | } | 131 | } |
| 121 | | 132 | |
| 122 | interface Channel<Args extends unknown[], Result, OptimisticHelpers> { | 133 | interface Channel<Args extends unknown[], Result, OptimisticHelpers> { |
| ... | @@ -283,16 +294,12 @@ export class BlockingMutation< | ... | @@ -283,16 +294,12 @@ export class BlockingMutation< |
| 283 | onSettled?.({ status: "success", result }); | 294 | onSettled?.({ status: "success", result }); |
| 284 | return result; | 295 | return result; |
| 285 | }).catch((caught: unknown) => { | 296 | }).catch((caught: unknown) => { |
| 286 | // Extract error and description if this is a wrapped mutation error | 297 | const { error } = unwrapMutationError(caught); |
| 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 | | 298 | |
| 291 | // Call user handlers with the unwrapped error | | |
| 292 | onError?.(error); | 299 | onError?.(error); |
| 293 | onSettled?.({ status: "error", error }); | 300 | onSettled?.({ status: "error", error }); |
| 294 | | 301 | |
| 295 | throw caught; | 302 | throw error; |
| 296 | }); | 303 | }); |
| 297 | } | 304 | } |
| 298 | | 305 | |
| ... | @@ -326,12 +333,8 @@ export class BlockingMutation< | ... | @@ -326,12 +333,8 @@ export class BlockingMutation< |
| 326 | } | 333 | } |
| 327 | } | 334 | } |
| 328 | }).catch((caught: unknown) => { | 335 | }).catch((caught: unknown) => { |
| 329 | // Extract error and description if this is a wrapped mutation error | 336 | const { error, description = this.describe(...args) } = unwrapMutationError(caught); |
| 330 | const isMutationError = (caught as MutationError)?.__mutationError === true; | | |
| 331 | const error = isMutationError ? (caught as MutationError).error : caught; | | |
| 332 | const description = isMutationError ? (caught as MutationError).description : this.describe(...args); | | |
| 333 | | 337 | |
| 334 | // Call user handlers with the unwrapped error | | |
| 335 | onError?.(error); | 338 | onError?.(error); |
| 336 | onSettled?.({ status: "error", error }); | 339 | onSettled?.({ status: "error", error }); |
| 337 | | 340 | |
| ... | @@ -533,7 +536,7 @@ export class BlockingMutation< | ... | @@ -533,7 +536,7 @@ export class BlockingMutation< |
| 533 | }, (error) => { | 536 | }, (error) => { |
| 534 | // Capture description BEFORE rollback so it sees optimistic state | 537 | // Capture description BEFORE rollback so it sees optimistic state |
| 535 | const description = this.describe(...args); | 538 | const description = this.describe(...args); |
| 536 | const wrappedError: MutationError = { __mutationError: true, error, description }; | 539 | const wrappedError = new MutationError(error, description); |
| 537 | | 540 | |
| 538 | // if an error happens, then every rollback is called in reverse order | 541 | // if an error happens, then every rollback is called in reverse order |
| 539 | let next; | 542 | let next; |
| ... | @@ -801,10 +804,7 @@ export class BlockingMutation< | ... | @@ -801,10 +804,7 @@ export class BlockingMutation< |
| 801 | } | 804 | } |
| 802 | }, | 805 | }, |
| 803 | (caught) => { | 806 | (caught) => { |
| 804 | // Extract error and description if this is a wrapped mutation error | 807 | const { error, description = this.describe(...args) } = unwrapMutationError(caught); |
| 805 | const isMutationError = (caught as MutationError)?.__mutationError === true; | | |
| 806 | const error = isMutationError ? (caught as MutationError).error : caught; | | |
| 807 | const description = isMutationError ? (caught as MutationError).description : this.describe(...args); | | |
| 808 | | 808 | |
| 809 | // Reject all pending promises with unwrapped error | 809 | // Reject all pending promises with unwrapped error |
| 810 | pending.forEach((p) => p.reject(error)); | 810 | pending.forEach((p) => p.reject(error)); |