@tanstack/query-core
Version:
The framework agnostic core that powers TanStack Query
329 lines (328 loc) • 13.6 kB
JavaScript
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldGet2, r as _assertClassBrand, t as _classPrivateFieldSet2 } from "./classPrivateFieldSet2-CukOTU3U.js";
import { notifyManager } from "./notifyManager.js";
import { createRetryer } from "./retryer.js";
import { Removable } from "./removable.js";
import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-CgB-WYk3.js";
//#region src/mutation.ts
var _client = /* @__PURE__ */ new WeakMap();
var _observers = /* @__PURE__ */ new WeakMap();
var _mutationCache = /* @__PURE__ */ new WeakMap();
var _retryer = /* @__PURE__ */ new WeakMap();
var _Mutation_brand = /* @__PURE__ */ new WeakSet();
/**
* Represents a single mutation attempt. A `Mutation` holds the mutation's
* options, state (data/error/status), and the `MutationObserver`s currently
* subscribed to it.
*
* Instances are created and managed internally by `MutationCache`; application
* code typically interacts with mutations indirectly through `QueryClient` or
* a framework hook like `useMutation`. Direct access to a `Mutation` instance
* is possible via `mutationCache.find()`/`getAll()` for inspecting cache state.
* @example
* ```ts
* const mutationCache = queryClient.getMutationCache()
*
* const mutation = mutationCache.find({ mutationKey: ['addPost'] })
* ```
*/
var Mutation = class extends Removable {
constructor(config) {
super();
_classPrivateMethodInitSpec(this, _Mutation_brand);
_classPrivateFieldInitSpec(this, _client, void 0);
_classPrivateFieldInitSpec(this, _observers, void 0);
_classPrivateFieldInitSpec(this, _mutationCache, void 0);
_classPrivateFieldInitSpec(this, _retryer, void 0);
_classPrivateFieldSet2(_client, this, config.client);
this.mutationId = config.mutationId;
_classPrivateFieldSet2(_mutationCache, this, config.mutationCache);
_classPrivateFieldSet2(_observers, this, []);
this.state = config.state || getDefaultState();
this.setOptions(config.options);
this.scheduleGc();
}
/**
* Replaces the mutation's options and applies their `gcTime`.
* @internal
* @param options - The new mutation options.
*/
setOptions(options) {
this.options = options;
this.updateGcTime(this.options.gcTime);
}
/**
* The `meta` object passed in the mutation's options, if any.
* @returns The mutation's `meta`, or `undefined` if none was set.
*/
get meta() {
return this.options.meta;
}
/**
* Subscribes an observer to the mutation and stops its garbage collection.
* @internal
* @param observer - The observer to add. Adding an observer that is already subscribed does
* nothing.
*/
addObserver(observer) {
if (!_classPrivateFieldGet2(_observers, this).includes(observer)) {
_classPrivateFieldGet2(_observers, this).push(observer);
this.clearGcTimeout();
_classPrivateFieldGet2(_mutationCache, this).notify({
type: "observerAdded",
mutation: this,
observer
});
}
}
/**
* Unsubscribes an observer from the mutation.
* @internal
* @param observer - The observer to remove. Garbage collection is scheduled afterwards.
*/
removeObserver(observer) {
_classPrivateFieldSet2(_observers, this, _classPrivateFieldGet2(_observers, this).filter((x) => x !== observer));
this.scheduleGc();
_classPrivateFieldGet2(_mutationCache, this).notify({
type: "observerRemoved",
mutation: this,
observer
});
}
optionalRemove() {
if (!_classPrivateFieldGet2(_observers, this).length) {
if (this.state.status === "pending") this.scheduleGc();
else _classPrivateFieldGet2(_mutationCache, this).remove(this);
}
}
/**
* Resumes a mutation that is currently paused or was restored from a
* dehydrated, still-`pending` state.
*
* - If this mutation has an active retryer (it paused mid-attempt, e.g. due
* to the network mode or scope-based queuing), its retryer is resumed.
* - Otherwise, if the mutation's status is still `pending` (e.g. it was
* dehydrated while an attempt was in flight and never got a retryer in
* this instance), `execute` is called again with the last known variables.
* - Otherwise the mutation has already settled and this resolves immediately
* without running anything again.
* @returns A promise that settles with the resumed mutation: it rejects if the mutation fails.
* It resolves immediately if the mutation has already settled.
* @see {@link Mutation#execute}
* @example
* ```ts
* // typically driven by reconnect handling, e.g. queryClient.resumePausedMutations()
* const mutation = mutationCache.find({ mutationKey: ['addPost'] })
* await mutation?.continue()
* ```
*/
continue() {
var _classPrivateFieldGet2$1;
return ((_classPrivateFieldGet2$1 = _classPrivateFieldGet2(_retryer, this)) === null || _classPrivateFieldGet2$1 === void 0 ? void 0 : _classPrivateFieldGet2$1.continue()) ?? (this.state.status === "pending" ? this.execute(this.state.variables) : Promise.resolve());
}
/**
* Runs the mutation function for the given variables through a retryer, and
* drives the mutation's state and lifecycle callbacks through to settlement.
*
* If this mutation's state is already `pending` when `execute` is called
* (i.e. it was restored, still in-flight, from a dehydrated state), the
* `onMutate` step is skipped and a `continue` action is dispatched to
* unpause it; otherwise a `pending` action is dispatched first, then the
* mutation cache's `onMutate` and the mutation's own `onMutate` option are
* awaited in that order, and the resulting context is stored.
*
* The mutation function is then run (subject to `retry`/`retryDelay`/
* `networkMode`, and to the mutation cache's scope-based serialization).
* On success, the cache's `onSuccess`/`onSettled` callbacks run before the
* mutation's own `onSuccess`/`onSettled` options, a `success` action is
* dispatched, and the resolved data is returned. On failure, the same
* cache-then-option ordering is used for `onError`/`onSettled`, but each of
* those four callbacks is individually caught so that a throwing callback
* cannot mask the original error; an `error` action is then dispatched and
* the original error is re-thrown.
* @param variables - The variables passed to the `mutationFn`.
* @returns A promise that resolves with the mutation's data, or rejects with its error.
* @see {@link Mutation#continue}
* @example
* ```ts
* // Called internally by `MutationObserver.mutate` and `Mutation.continue` —
* // applications normally trigger mutations through those, not this method.
* const data = await mutation.execute(variables)
* ```
*/
async execute(variables) {
const onContinue = () => {
_assertClassBrand(_Mutation_brand, this, _dispatch).call(this, { type: "continue" });
};
const mutationFnContext = {
client: _classPrivateFieldGet2(_client, this),
meta: this.options.meta,
mutationKey: this.options.mutationKey
};
const retryer = _classPrivateFieldSet2(_retryer, this, createRetryer({
fn: () => {
if (!this.options.mutationFn) return Promise.reject(/* @__PURE__ */ new Error("No mutationFn found"));
return this.options.mutationFn(variables, mutationFnContext);
},
onFail: (failureCount, error) => {
_assertClassBrand(_Mutation_brand, this, _dispatch).call(this, {
type: "failed",
failureCount,
error
});
},
onPause: () => {
_assertClassBrand(_Mutation_brand, this, _dispatch).call(this, { type: "pause" });
},
onContinue,
retry: this.options.retry ?? 0,
retryDelay: this.options.retryDelay,
networkMode: this.options.networkMode,
canRun: () => _classPrivateFieldGet2(_mutationCache, this).canRun(this)
}));
const restored = this.state.status === "pending";
const isPaused = !retryer.canStart();
try {
var _classPrivateFieldGet3, _classPrivateFieldGet4, _this$options$onSucce, _this$options2, _classPrivateFieldGet5, _classPrivateFieldGet6, _this$options$onSettl, _this$options3;
if (restored) onContinue();
else {
var _this$options$onMutat, _this$options;
_assertClassBrand(_Mutation_brand, this, _dispatch).call(this, {
type: "pending",
variables,
isPaused
});
if (_classPrivateFieldGet2(_mutationCache, this).config.onMutate) await _classPrivateFieldGet2(_mutationCache, this).config.onMutate(variables, this, mutationFnContext);
const context = await ((_this$options$onMutat = (_this$options = this.options).onMutate) === null || _this$options$onMutat === void 0 ? void 0 : _this$options$onMutat.call(_this$options, variables, mutationFnContext));
if (context !== this.state.context) _assertClassBrand(_Mutation_brand, this, _dispatch).call(this, {
type: "pending",
context,
variables,
isPaused
});
}
const data = await retryer.start();
await ((_classPrivateFieldGet3 = (_classPrivateFieldGet4 = _classPrivateFieldGet2(_mutationCache, this).config).onSuccess) === null || _classPrivateFieldGet3 === void 0 ? void 0 : _classPrivateFieldGet3.call(_classPrivateFieldGet4, data, variables, this.state.context, this, mutationFnContext));
await ((_this$options$onSucce = (_this$options2 = this.options).onSuccess) === null || _this$options$onSucce === void 0 ? void 0 : _this$options$onSucce.call(_this$options2, data, variables, this.state.context, mutationFnContext));
await ((_classPrivateFieldGet5 = (_classPrivateFieldGet6 = _classPrivateFieldGet2(_mutationCache, this).config).onSettled) === null || _classPrivateFieldGet5 === void 0 ? void 0 : _classPrivateFieldGet5.call(_classPrivateFieldGet6, data, null, this.state.variables, this.state.context, this, mutationFnContext));
await ((_this$options$onSettl = (_this$options3 = this.options).onSettled) === null || _this$options$onSettl === void 0 ? void 0 : _this$options$onSettl.call(_this$options3, data, null, variables, this.state.context, mutationFnContext));
_assertClassBrand(_Mutation_brand, this, _dispatch).call(this, {
type: "success",
data
});
return data;
} catch (error) {
try {
var _classPrivateFieldGet7, _classPrivateFieldGet8;
await ((_classPrivateFieldGet7 = (_classPrivateFieldGet8 = _classPrivateFieldGet2(_mutationCache, this).config).onError) === null || _classPrivateFieldGet7 === void 0 ? void 0 : _classPrivateFieldGet7.call(_classPrivateFieldGet8, error, variables, this.state.context, this, mutationFnContext));
} catch (e) {
Promise.reject(e);
}
try {
var _this$options$onError, _this$options4;
await ((_this$options$onError = (_this$options4 = this.options).onError) === null || _this$options$onError === void 0 ? void 0 : _this$options$onError.call(_this$options4, error, variables, this.state.context, mutationFnContext));
} catch (e) {
Promise.reject(e);
}
try {
var _classPrivateFieldGet9, _classPrivateFieldGet10;
await ((_classPrivateFieldGet9 = (_classPrivateFieldGet10 = _classPrivateFieldGet2(_mutationCache, this).config).onSettled) === null || _classPrivateFieldGet9 === void 0 ? void 0 : _classPrivateFieldGet9.call(_classPrivateFieldGet10, void 0, error, this.state.variables, this.state.context, this, mutationFnContext));
} catch (e) {
Promise.reject(e);
}
try {
var _this$options$onSettl2, _this$options5;
await ((_this$options$onSettl2 = (_this$options5 = this.options).onSettled) === null || _this$options$onSettl2 === void 0 ? void 0 : _this$options$onSettl2.call(_this$options5, void 0, error, variables, this.state.context, mutationFnContext));
} catch (e) {
Promise.reject(e);
}
_assertClassBrand(_Mutation_brand, this, _dispatch).call(this, {
type: "error",
error
});
throw error;
} finally {
if (_classPrivateFieldGet2(_retryer, this) === retryer) _classPrivateFieldSet2(_retryer, this, void 0);
_classPrivateFieldGet2(_mutationCache, this).runNext(this);
}
}
};
function _dispatch(action) {
const reducer = (state) => {
switch (action.type) {
case "failed": return {
...state,
failureCount: action.failureCount,
failureReason: action.error
};
case "pause": return {
...state,
isPaused: true
};
case "continue": return {
...state,
isPaused: false
};
case "pending": return {
...state,
context: action.context,
data: void 0,
failureCount: 0,
failureReason: null,
error: null,
isPaused: action.isPaused,
status: "pending",
variables: action.variables,
submittedAt: Date.now()
};
case "success": return {
...state,
data: action.data,
failureCount: 0,
failureReason: null,
error: null,
status: "success",
isPaused: false
};
case "error": return {
...state,
data: void 0,
error: action.error,
failureCount: state.failureCount + 1,
failureReason: action.error,
isPaused: false,
status: "error"
};
}
};
this.state = reducer(this.state);
notifyManager.batch(() => {
_classPrivateFieldGet2(_observers, this).forEach((observer) => {
observer.onMutationUpdate(action);
});
_classPrivateFieldGet2(_mutationCache, this).notify({
mutation: this,
type: "updated",
action
});
});
}
/**
* Returns the initial state of a mutation: `'idle'`, with no data, error, or variables.
* @returns The initial mutation state.
*/
function getDefaultState() {
return {
context: void 0,
data: void 0,
error: null,
failureCount: 0,
failureReason: null,
isPaused: false,
status: "idle",
variables: void 0,
submittedAt: 0
};
}
//#endregion
export { Mutation, getDefaultState };
//# sourceMappingURL=mutation.js.map