@tanstack/query-core
Version:
The framework agnostic core that powers TanStack Query
1 lines • 15.2 kB
Source Map (JSON)
{"version":3,"file":"mutation.cjs","names":["Removable","#client","#mutationCache","#observers","#retryer","#dispatch","createRetryer"],"sources":["../../src/mutation.ts"],"sourcesContent":["import { notifyManager } from './notifyManager'\nimport { Removable } from './removable'\nimport { createRetryer } from './retryer'\nimport type {\n DefaultError,\n MutationFunctionContext,\n MutationMeta,\n MutationOptions,\n MutationStatus,\n} from './types'\nimport type { MutationCache } from './mutationCache'\nimport type { MutationObserver } from './mutationObserver'\nimport type { Retryer } from './retryer'\nimport type { QueryClient } from './queryClient'\n\n// TYPES\n\ninterface MutationConfig<TData, TError, TVariables, TOnMutateResult> {\n client: QueryClient\n mutationId: number\n mutationCache: MutationCache\n options: MutationOptions<TData, TError, TVariables, TOnMutateResult>\n state?: MutationState<TData, TError, TVariables, TOnMutateResult>\n}\n\nexport interface MutationState<\n TData = unknown,\n TError = DefaultError,\n TVariables = unknown,\n TOnMutateResult = unknown,\n> {\n context: TOnMutateResult | undefined\n data: TData | undefined\n error: TError | null\n failureCount: number\n failureReason: TError | null\n isPaused: boolean\n status: MutationStatus\n variables: TVariables | undefined\n submittedAt: number\n}\n\ninterface FailedAction<TError> {\n type: 'failed'\n failureCount: number\n error: TError | null\n}\n\ninterface PendingAction<TVariables, TOnMutateResult> {\n type: 'pending'\n isPaused: boolean\n variables?: TVariables\n context?: TOnMutateResult\n}\n\ninterface SuccessAction<TData> {\n type: 'success'\n data: TData\n}\n\ninterface ErrorAction<TError> {\n type: 'error'\n error: TError\n}\n\ninterface PauseAction {\n type: 'pause'\n}\n\ninterface ContinueAction {\n type: 'continue'\n}\n\nexport type Action<TData, TError, TVariables, TOnMutateResult> =\n | ContinueAction\n | ErrorAction<TError>\n | FailedAction<TError>\n | PendingAction<TVariables, TOnMutateResult>\n | PauseAction\n | SuccessAction<TData>\n\n// CLASS\n\nexport class Mutation<\n TData = unknown,\n TError = DefaultError,\n TVariables = unknown,\n TOnMutateResult = unknown,\n> extends Removable {\n state: MutationState<TData, TError, TVariables, TOnMutateResult>\n options!: MutationOptions<TData, TError, TVariables, TOnMutateResult>\n readonly mutationId: number\n\n #client: QueryClient\n #observers: Array<\n MutationObserver<TData, TError, TVariables, TOnMutateResult>\n >\n #mutationCache: MutationCache\n #retryer?: Retryer<TData>\n\n constructor(\n config: MutationConfig<TData, TError, TVariables, TOnMutateResult>,\n ) {\n super()\n\n this.#client = config.client\n this.mutationId = config.mutationId\n this.#mutationCache = config.mutationCache\n this.#observers = []\n this.state = config.state || getDefaultState()\n\n this.setOptions(config.options)\n this.scheduleGc()\n }\n\n setOptions(\n options: MutationOptions<TData, TError, TVariables, TOnMutateResult>,\n ): void {\n this.options = options\n\n this.updateGcTime(this.options.gcTime)\n }\n\n get meta(): MutationMeta | undefined {\n return this.options.meta\n }\n\n addObserver(observer: MutationObserver<any, any, any, any>): void {\n if (!this.#observers.includes(observer)) {\n this.#observers.push(observer)\n\n // Stop the mutation from being garbage collected\n this.clearGcTimeout()\n\n this.#mutationCache.notify({\n type: 'observerAdded',\n mutation: this,\n observer,\n })\n }\n }\n\n removeObserver(observer: MutationObserver<any, any, any, any>): void {\n this.#observers = this.#observers.filter((x) => x !== observer)\n\n this.scheduleGc()\n\n this.#mutationCache.notify({\n type: 'observerRemoved',\n mutation: this,\n observer,\n })\n }\n\n protected optionalRemove() {\n if (!this.#observers.length) {\n if (this.state.status === 'pending') {\n this.scheduleGc()\n } else {\n this.#mutationCache.remove(this)\n }\n }\n }\n\n continue(): Promise<unknown> {\n return (\n this.#retryer?.continue() ??\n // continuing a mutation assumes that variables are set, mutation must have been dehydrated before.\n // a settled mutation has no retryer to continue and must not run again\n (this.state.status === 'pending'\n ? this.execute(this.state.variables!)\n : Promise.resolve())\n )\n }\n\n async execute(variables: TVariables): Promise<TData> {\n const onContinue = () => {\n this.#dispatch({ type: 'continue' })\n }\n\n const mutationFnContext = {\n client: this.#client,\n meta: this.options.meta,\n mutationKey: this.options.mutationKey,\n } satisfies MutationFunctionContext\n\n const retryer = (this.#retryer = createRetryer({\n fn: () => {\n if (!this.options.mutationFn) {\n return Promise.reject(new Error('No mutationFn found'))\n }\n\n return this.options.mutationFn(variables, mutationFnContext)\n },\n onFail: (failureCount, error) => {\n this.#dispatch({ type: 'failed', failureCount, error })\n },\n onPause: () => {\n this.#dispatch({ type: 'pause' })\n },\n onContinue,\n retry: this.options.retry ?? 0,\n retryDelay: this.options.retryDelay,\n networkMode: this.options.networkMode,\n canRun: () => this.#mutationCache.canRun(this),\n }))\n\n const restored = this.state.status === 'pending'\n const isPaused = !retryer.canStart()\n\n try {\n if (restored) {\n // Dispatch continue action to unpause restored mutation\n onContinue()\n } else {\n this.#dispatch({ type: 'pending', variables, isPaused })\n // Notify cache callback\n if (this.#mutationCache.config.onMutate) {\n await this.#mutationCache.config.onMutate(\n variables,\n this as Mutation<unknown, unknown, unknown, unknown>,\n mutationFnContext,\n )\n }\n const context = await this.options.onMutate?.(\n variables,\n mutationFnContext,\n )\n if (context !== this.state.context) {\n this.#dispatch({\n type: 'pending',\n context,\n variables,\n isPaused,\n })\n }\n }\n const data = await retryer.start()\n\n // Notify cache callback\n await this.#mutationCache.config.onSuccess?.(\n data,\n variables,\n this.state.context,\n this as Mutation<unknown, unknown, unknown, unknown>,\n mutationFnContext,\n )\n\n await this.options.onSuccess?.(\n data,\n variables,\n this.state.context!,\n mutationFnContext,\n )\n\n // Notify cache callback\n await this.#mutationCache.config.onSettled?.(\n data,\n null,\n this.state.variables,\n this.state.context,\n this as Mutation<unknown, unknown, unknown, unknown>,\n mutationFnContext,\n )\n\n await this.options.onSettled?.(\n data,\n null,\n variables,\n this.state.context,\n mutationFnContext,\n )\n\n this.#dispatch({ type: 'success', data })\n return data\n } catch (error) {\n try {\n // Notify cache callback\n await this.#mutationCache.config.onError?.(\n error as any,\n variables,\n this.state.context,\n this as Mutation<unknown, unknown, unknown, unknown>,\n mutationFnContext,\n )\n } catch (e) {\n void Promise.reject(e)\n }\n\n try {\n await this.options.onError?.(\n error as TError,\n variables,\n this.state.context,\n mutationFnContext,\n )\n } catch (e) {\n void Promise.reject(e)\n }\n\n try {\n // Notify cache callback\n await this.#mutationCache.config.onSettled?.(\n undefined,\n error as any,\n this.state.variables,\n this.state.context,\n this as Mutation<unknown, unknown, unknown, unknown>,\n mutationFnContext,\n )\n } catch (e) {\n void Promise.reject(e)\n }\n\n try {\n await this.options.onSettled?.(\n undefined,\n error as TError,\n variables,\n this.state.context,\n mutationFnContext,\n )\n } catch (e) {\n void Promise.reject(e)\n }\n\n this.#dispatch({ type: 'error', error: error as TError })\n throw error\n } finally {\n // The settled retryer's promise would otherwise pin this mutation's\n // result, variables and context for as long as the cache keeps it\n if (this.#retryer === retryer) {\n this.#retryer = undefined\n }\n this.#mutationCache.runNext(this)\n }\n }\n\n #dispatch(action: Action<TData, TError, TVariables, TOnMutateResult>): void {\n const reducer = (\n state: MutationState<TData, TError, TVariables, TOnMutateResult>,\n ): MutationState<TData, TError, TVariables, TOnMutateResult> => {\n switch (action.type) {\n case 'failed':\n return {\n ...state,\n failureCount: action.failureCount,\n failureReason: action.error,\n }\n case 'pause':\n return {\n ...state,\n isPaused: true,\n }\n case 'continue':\n return {\n ...state,\n isPaused: false,\n }\n case 'pending':\n return {\n ...state,\n context: action.context,\n data: undefined,\n failureCount: 0,\n failureReason: null,\n error: null,\n isPaused: action.isPaused,\n status: 'pending',\n variables: action.variables,\n submittedAt: Date.now(),\n }\n case 'success':\n return {\n ...state,\n data: action.data,\n failureCount: 0,\n failureReason: null,\n error: null,\n status: 'success',\n isPaused: false,\n }\n case 'error':\n return {\n ...state,\n data: undefined,\n error: action.error,\n failureCount: state.failureCount + 1,\n failureReason: action.error,\n isPaused: false,\n status: 'error',\n }\n }\n }\n this.state = reducer(this.state)\n\n notifyManager.batch(() => {\n this.#observers.forEach((observer) => {\n observer.onMutationUpdate(action)\n })\n this.#mutationCache.notify({\n mutation: this,\n type: 'updated',\n action,\n })\n })\n }\n}\n\nexport function getDefaultState<\n TData,\n TError,\n TVariables,\n TOnMutateResult,\n>(): MutationState<TData, TError, TVariables, TOnMutateResult> {\n return {\n context: undefined,\n data: undefined,\n error: null,\n failureCount: 0,\n failureReason: null,\n isPaused: false,\n status: 'idle',\n variables: undefined,\n submittedAt: 0,\n }\n}\n"],"mappings":"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"}