mirror of
https://github.com/hanzoai/agency.git
synced 2026-11-02 19:13:28 +00:00
281 lines
7.0 KiB
Plaintext
281 lines
7.0 KiB
Plaintext
// src/mutation.ts
|
|
import { notifyManager } from "./notifyManager.js";
|
|
import { Removable } from "./removable.js";
|
|
import { createRetryer } from "./retryer.js";
|
|
var Mutation = class extends Removable {
|
|
#client;
|
|
#observers;
|
|
#mutationCache;
|
|
#retryer;
|
|
constructor(config) {
|
|
super();
|
|
this.#client = config.client;
|
|
this.mutationId = config.mutationId;
|
|
this.#mutationCache = config.mutationCache;
|
|
this.#observers = [];
|
|
this.state = config.state || getDefaultState();
|
|
this.setOptions(config.options);
|
|
this.scheduleGc();
|
|
}
|
|
setOptions(options) {
|
|
this.options = options;
|
|
this.updateGcTime(this.options.gcTime);
|
|
}
|
|
get meta() {
|
|
return this.options.meta;
|
|
}
|
|
addObserver(observer) {
|
|
if (!this.#observers.includes(observer)) {
|
|
this.#observers.push(observer);
|
|
this.clearGcTimeout();
|
|
this.#mutationCache.notify({
|
|
type: "observerAdded",
|
|
mutation: this,
|
|
observer
|
|
});
|
|
}
|
|
}
|
|
removeObserver(observer) {
|
|
this.#observers = this.#observers.filter((x) => x !== observer);
|
|
this.scheduleGc();
|
|
this.#mutationCache.notify({
|
|
type: "observerRemoved",
|
|
mutation: this,
|
|
observer
|
|
});
|
|
}
|
|
optionalRemove() {
|
|
if (!this.#observers.length) {
|
|
if (this.state.status === "pending") {
|
|
this.scheduleGc();
|
|
} else {
|
|
this.#mutationCache.remove(this);
|
|
}
|
|
}
|
|
}
|
|
continue() {
|
|
return this.#retryer?.continue() ?? // continuing a mutation assumes that variables are set, mutation must have been dehydrated before
|
|
this.execute(this.state.variables);
|
|
}
|
|
async execute(variables) {
|
|
const onContinue = () => {
|
|
this.#dispatch({ type: "continue" });
|
|
};
|
|
const mutationFnContext = {
|
|
client: this.#client,
|
|
meta: this.options.meta,
|
|
mutationKey: this.options.mutationKey
|
|
};
|
|
this.#retryer = createRetryer({
|
|
fn: () => {
|
|
if (!this.options.mutationFn) {
|
|
return Promise.reject(new Error("No mutationFn found"));
|
|
}
|
|
return this.options.mutationFn(variables, mutationFnContext);
|
|
},
|
|
onFail: (failureCount, error) => {
|
|
this.#dispatch({ type: "failed", failureCount, error });
|
|
},
|
|
onPause: () => {
|
|
this.#dispatch({ type: "pause" });
|
|
},
|
|
onContinue,
|
|
retry: this.options.retry ?? 0,
|
|
retryDelay: this.options.retryDelay,
|
|
networkMode: this.options.networkMode,
|
|
canRun: () => this.#mutationCache.canRun(this)
|
|
});
|
|
const restored = this.state.status === "pending";
|
|
const isPaused = !this.#retryer.canStart();
|
|
try {
|
|
if (restored) {
|
|
onContinue();
|
|
} else {
|
|
this.#dispatch({ type: "pending", variables, isPaused });
|
|
if (this.#mutationCache.config.onMutate) {
|
|
await this.#mutationCache.config.onMutate(
|
|
variables,
|
|
this,
|
|
mutationFnContext
|
|
);
|
|
}
|
|
const context = await this.options.onMutate?.(
|
|
variables,
|
|
mutationFnContext
|
|
);
|
|
if (context !== this.state.context) {
|
|
this.#dispatch({
|
|
type: "pending",
|
|
context,
|
|
variables,
|
|
isPaused
|
|
});
|
|
}
|
|
}
|
|
const data = await this.#retryer.start();
|
|
await this.#mutationCache.config.onSuccess?.(
|
|
data,
|
|
variables,
|
|
this.state.context,
|
|
this,
|
|
mutationFnContext
|
|
);
|
|
await this.options.onSuccess?.(
|
|
data,
|
|
variables,
|
|
this.state.context,
|
|
mutationFnContext
|
|
);
|
|
await this.#mutationCache.config.onSettled?.(
|
|
data,
|
|
null,
|
|
this.state.variables,
|
|
this.state.context,
|
|
this,
|
|
mutationFnContext
|
|
);
|
|
await this.options.onSettled?.(
|
|
data,
|
|
null,
|
|
variables,
|
|
this.state.context,
|
|
mutationFnContext
|
|
);
|
|
this.#dispatch({ type: "success", data });
|
|
return data;
|
|
} catch (error) {
|
|
try {
|
|
await this.#mutationCache.config.onError?.(
|
|
error,
|
|
variables,
|
|
this.state.context,
|
|
this,
|
|
mutationFnContext
|
|
);
|
|
} catch (e) {
|
|
void Promise.reject(e);
|
|
}
|
|
try {
|
|
await this.options.onError?.(
|
|
error,
|
|
variables,
|
|
this.state.context,
|
|
mutationFnContext
|
|
);
|
|
} catch (e) {
|
|
void Promise.reject(e);
|
|
}
|
|
try {
|
|
await this.#mutationCache.config.onSettled?.(
|
|
void 0,
|
|
error,
|
|
this.state.variables,
|
|
this.state.context,
|
|
this,
|
|
mutationFnContext
|
|
);
|
|
} catch (e) {
|
|
void Promise.reject(e);
|
|
}
|
|
try {
|
|
await this.options.onSettled?.(
|
|
void 0,
|
|
error,
|
|
variables,
|
|
this.state.context,
|
|
mutationFnContext
|
|
);
|
|
} catch (e) {
|
|
void Promise.reject(e);
|
|
}
|
|
this.#dispatch({ type: "error", error });
|
|
throw error;
|
|
} finally {
|
|
this.#mutationCache.runNext(this);
|
|
}
|
|
}
|
|
#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(() => {
|
|
this.#observers.forEach((observer) => {
|
|
observer.onMutationUpdate(action);
|
|
});
|
|
this.#mutationCache.notify({
|
|
mutation: this,
|
|
type: "updated",
|
|
action
|
|
});
|
|
});
|
|
}
|
|
};
|
|
function getDefaultState() {
|
|
return {
|
|
context: void 0,
|
|
data: void 0,
|
|
error: null,
|
|
failureCount: 0,
|
|
failureReason: null,
|
|
isPaused: false,
|
|
status: "idle",
|
|
variables: void 0,
|
|
submittedAt: 0
|
|
};
|
|
}
|
|
export {
|
|
Mutation,
|
|
getDefaultState
|
|
};
|
|
//# sourceMappingURL=mutation.js.map |