@pinia/colada-plugin-retry
Version:
Retry failed requests with Pinia Colada
137 lines (136 loc) • 5.05 kB
JavaScript
import { shallowRef, toValue } from "vue";
//#region src/retry.ts
const RETRY_OPTIONS_DEFAULTS = {
delay: (attempt) => {
const time = Math.min(2 ** attempt * 1e3, 3e4);
if (process.env.NODE_ENV === "development") console.debug(`⏲️ delaying attempt #${attempt + 1} by ${time}ms`);
return time;
},
retry: (count) => {
if (process.env.NODE_ENV === "development") console.debug(`🔄 Retrying ${"🟨".repeat(count + 1)}${"⬜️".repeat(2 - count)}`);
return count < 2;
}
};
/**
* Plugin that adds the ability to retry failed queries.
*
* @param globalOptions - global options for the retries
*/
function PiniaColadaRetry(globalOptions) {
const defaults = {
...RETRY_OPTIONS_DEFAULTS,
...globalOptions
};
return ({ queryCache, scope }) => {
const retryMap = /* @__PURE__ */ new Map();
let isInternalCall = false;
queryCache.$onAction(({ name, args, after, onError }) => {
if (name === "extend") {
const [entry] = args;
scope.run(() => {
entry.ext.isRetrying = shallowRef(false);
entry.ext.retryCount = shallowRef(0);
entry.ext.retryError = shallowRef(null);
});
if (process.env.NODE_ENV === "development") updateDevtoolsState(entry);
return;
} else if (name === "remove" || name === "cancel") {
const [cacheEntry] = args;
const key = cacheEntry.keyHash;
const entry = retryMap.get(key);
if (entry) {
clearTimeout(entry.timeoutId);
retryMap.delete(key);
}
cacheEntry.ext.isRetrying.value = false;
cacheEntry.ext.retryCount.value = 0;
cacheEntry.ext.retryError.value = null;
if (process.env.NODE_ENV === "development") updateDevtoolsState(cacheEntry);
} else if (name === "fetch") {
const [queryEntry] = args;
const localOptions = queryEntry.options?.retry;
const options = { ...typeof localOptions === "object" ? localOptions : { retry: localOptions } };
const retry = options.retry ?? defaults.retry;
const delay = options.delay ?? defaults.delay;
if (retry === 0) return;
const key = queryEntry.keyHash;
clearTimeout(retryMap.get(key)?.timeoutId);
if (!isInternalCall) {
retryMap.delete(key);
queryEntry.ext.isRetrying.value = false;
queryEntry.ext.retryCount.value = 0;
queryEntry.ext.retryError.value = null;
if (process.env.NODE_ENV === "development") updateDevtoolsState(queryEntry);
}
const previousState = queryEntry.state.value;
const retryFetch = () => {
if (queryEntry.state.value.status === "error") {
const error = queryEntry.state.value.error;
let entry = retryMap.get(key);
if (!entry) {
entry = { retryCount: 0 };
retryMap.set(key, entry);
}
if (typeof retry === "number" ? retry > entry.retryCount : retry(entry.retryCount, error)) {
queryEntry.ext.isRetrying.value = true;
queryEntry.ext.retryCount.value = entry.retryCount + 1;
queryEntry.ext.retryError.value = error;
if (process.env.NODE_ENV === "development") updateDevtoolsState(queryEntry);
queryEntry.state.value = previousState;
const delayTime = typeof delay === "function" ? delay(entry.retryCount) : delay;
queryEntry.when = 0;
entry.timeoutId = setTimeout(() => {
if (!queryEntry.active || toValue(queryEntry.options?.enabled) === false) {
retryMap.delete(key);
queryEntry.ext.isRetrying.value = false;
queryEntry.ext.retryCount.value = 0;
queryEntry.ext.retryError.value = null;
if (process.env.NODE_ENV === "development") updateDevtoolsState(queryEntry);
return;
}
isInternalCall = true;
Promise.resolve(queryCache.fetch(queryEntry)).catch(process.env.NODE_ENV !== "test" ? console.error : () => {});
isInternalCall = false;
if (entry) entry.retryCount++;
}, delayTime);
} else {
queryEntry.ext.isRetrying.value = false;
queryEntry.ext.retryError.value = null;
retryMap.delete(key);
if (process.env.NODE_ENV === "development") updateDevtoolsState(queryEntry);
}
} else {
queryEntry.ext.isRetrying.value = false;
queryEntry.ext.retryCount.value = 0;
queryEntry.ext.retryError.value = null;
retryMap.delete(key);
if (process.env.NODE_ENV === "development") updateDevtoolsState(queryEntry);
}
};
onError(retryFetch);
after(retryFetch);
}
});
};
}
/**
* Updates the devtools state for the retry plugin. Only used in development mode.
*
* @param entry - the query entry to update
*
* @internal
*/
function updateDevtoolsState(entry) {
if (entry.ext.retry) {
entry.ext.retry.isRetrying = entry.ext.isRetrying.value;
entry.ext.retry.retryCount = entry.ext.retryCount.value;
entry.ext.retry.retryError = entry.ext.retryError.value;
} else entry.ext.retry ??= {
isRetrying: entry.ext.isRetrying.value,
retryCount: entry.ext.retryCount.value,
retryError: entry.ext.retryError.value
};
}
//#endregion
export { PiniaColadaRetry };
//# sourceMappingURL=index.mjs.map