UNPKG

react-query-cache-persistent

Version:

1,037 lines (1,025 loc) 27.9 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/core/src/index.ts var src_exports = {}; __export(src_exports, { PersistentQueryCache: () => PersistentQueryCache }); module.exports = __toCommonJS(src_exports); // node_modules/@tanstack/query-core/build/modern/subscribable.js var Subscribable = class { constructor() { this.listeners = /* @__PURE__ */ new Set(); this.subscribe = this.subscribe.bind(this); } subscribe(listener) { this.listeners.add(listener); this.onSubscribe(); return () => { this.listeners.delete(listener); this.onUnsubscribe(); }; } hasListeners() { return this.listeners.size > 0; } onSubscribe() { } onUnsubscribe() { } }; // node_modules/@tanstack/query-core/build/modern/utils.js var isServer = typeof window === "undefined" || "Deno" in globalThis; function noop() { return void 0; } function isValidTimeout(value) { return typeof value === "number" && value >= 0 && value !== Infinity; } function timeUntilStale(updatedAt, staleTime) { return Math.max(updatedAt + (staleTime || 0) - Date.now(), 0); } function matchQuery(filters, query) { const { type = "all", exact, fetchStatus, predicate, queryKey, stale } = filters; if (queryKey) { if (exact) { if (query.queryHash !== hashQueryKeyByOptions(queryKey, query.options)) { return false; } } else if (!partialMatchKey(query.queryKey, queryKey)) { return false; } } if (type !== "all") { const isActive = query.isActive(); if (type === "active" && !isActive) { return false; } if (type === "inactive" && isActive) { return false; } } if (typeof stale === "boolean" && query.isStale() !== stale) { return false; } if (fetchStatus && fetchStatus !== query.state.fetchStatus) { return false; } if (predicate && !predicate(query)) { return false; } return true; } function hashQueryKeyByOptions(queryKey, options) { const hashFn = options?.queryKeyHashFn || hashKey; return hashFn(queryKey); } function hashKey(queryKey) { return JSON.stringify( queryKey, (_, val) => isPlainObject(val) ? Object.keys(val).sort().reduce((result, key) => { result[key] = val[key]; return result; }, {}) : val ); } function partialMatchKey(a, b) { if (a === b) { return true; } if (typeof a !== typeof b) { return false; } if (a && b && typeof a === "object" && typeof b === "object") { return !Object.keys(b).some((key) => !partialMatchKey(a[key], b[key])); } return false; } function replaceEqualDeep(a, b) { if (a === b) { return a; } const array = isPlainArray(a) && isPlainArray(b); if (array || isPlainObject(a) && isPlainObject(b)) { const aItems = array ? a : Object.keys(a); const aSize = aItems.length; const bItems = array ? b : Object.keys(b); const bSize = bItems.length; const copy = array ? [] : {}; let equalItems = 0; for (let i = 0; i < bSize; i++) { const key = array ? i : bItems[i]; if ((!array && aItems.includes(key) || array) && a[key] === void 0 && b[key] === void 0) { copy[key] = void 0; equalItems++; } else { copy[key] = replaceEqualDeep(a[key], b[key]); if (copy[key] === a[key] && a[key] !== void 0) { equalItems++; } } } return aSize === bSize && equalItems === aSize ? a : copy; } return b; } function isPlainArray(value) { return Array.isArray(value) && value.length === Object.keys(value).length; } function isPlainObject(o) { if (!hasObjectPrototype(o)) { return false; } const ctor = o.constructor; if (ctor === void 0) { return true; } const prot = ctor.prototype; if (!hasObjectPrototype(prot)) { return false; } if (!prot.hasOwnProperty("isPrototypeOf")) { return false; } if (Object.getPrototypeOf(o) !== Object.prototype) { return false; } return true; } function hasObjectPrototype(o) { return Object.prototype.toString.call(o) === "[object Object]"; } function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } function replaceData(prevData, data, options) { if (typeof options.structuralSharing === "function") { return options.structuralSharing(prevData, data); } else if (options.structuralSharing !== false) { return replaceEqualDeep(prevData, data); } return data; } var skipToken = Symbol(); var ensureQueryFn = (options, fetchOptions) => { if (process.env.NODE_ENV !== "production") { if (options.queryFn === skipToken) { console.error( `Attempted to invoke queryFn when set to skipToken. This is likely a configuration error. Query hash: '${options.queryHash}'` ); } } if (!options.queryFn && fetchOptions?.initialPromise) { return () => fetchOptions.initialPromise; } if (!options.queryFn || options.queryFn === skipToken) { return () => Promise.reject(new Error(`Missing queryFn: '${options.queryHash}'`)); } return options.queryFn; }; // node_modules/@tanstack/query-core/build/modern/focusManager.js var FocusManager = class extends Subscribable { #focused; #cleanup; #setup; constructor() { super(); this.#setup = (onFocus) => { if (!isServer && window.addEventListener) { const listener = () => onFocus(); window.addEventListener("visibilitychange", listener, false); return () => { window.removeEventListener("visibilitychange", listener); }; } return; }; } onSubscribe() { if (!this.#cleanup) { this.setEventListener(this.#setup); } } onUnsubscribe() { if (!this.hasListeners()) { this.#cleanup?.(); this.#cleanup = void 0; } } setEventListener(setup) { this.#setup = setup; this.#cleanup?.(); this.#cleanup = setup((focused) => { if (typeof focused === "boolean") { this.setFocused(focused); } else { this.onFocus(); } }); } setFocused(focused) { const changed = this.#focused !== focused; if (changed) { this.#focused = focused; this.onFocus(); } } onFocus() { const isFocused = this.isFocused(); this.listeners.forEach((listener) => { listener(isFocused); }); } isFocused() { if (typeof this.#focused === "boolean") { return this.#focused; } return globalThis.document?.visibilityState !== "hidden"; } }; var focusManager = new FocusManager(); // node_modules/@tanstack/query-core/build/modern/onlineManager.js var OnlineManager = class extends Subscribable { #online = true; #cleanup; #setup; constructor() { super(); this.#setup = (onOnline) => { if (!isServer && window.addEventListener) { const onlineListener = () => onOnline(true); const offlineListener = () => onOnline(false); window.addEventListener("online", onlineListener, false); window.addEventListener("offline", offlineListener, false); return () => { window.removeEventListener("online", onlineListener); window.removeEventListener("offline", offlineListener); }; } return; }; } onSubscribe() { if (!this.#cleanup) { this.setEventListener(this.#setup); } } onUnsubscribe() { if (!this.hasListeners()) { this.#cleanup?.(); this.#cleanup = void 0; } } setEventListener(setup) { this.#setup = setup; this.#cleanup?.(); this.#cleanup = setup(this.setOnline.bind(this)); } setOnline(online) { const changed = this.#online !== online; if (changed) { this.#online = online; this.listeners.forEach((listener) => { listener(online); }); } } isOnline() { return this.#online; } }; var onlineManager = new OnlineManager(); // node_modules/@tanstack/query-core/build/modern/retryer.js function defaultRetryDelay(failureCount) { return Math.min(1e3 * 2 ** failureCount, 3e4); } function canFetch(networkMode) { return (networkMode ?? "online") === "online" ? onlineManager.isOnline() : true; } var CancelledError = class { constructor(options) { this.revert = options?.revert; this.silent = options?.silent; } }; function isCancelledError(value) { return value instanceof CancelledError; } function createRetryer(config) { let isRetryCancelled = false; let failureCount = 0; let isResolved = false; let continueFn; let promiseResolve; let promiseReject; const promise = new Promise((outerResolve, outerReject) => { promiseResolve = outerResolve; promiseReject = outerReject; }); const cancel = (cancelOptions) => { if (!isResolved) { reject(new CancelledError(cancelOptions)); config.abort?.(); } }; const cancelRetry = () => { isRetryCancelled = true; }; const continueRetry = () => { isRetryCancelled = false; }; const canContinue = () => focusManager.isFocused() && (config.networkMode === "always" || onlineManager.isOnline()) && config.canRun(); const canStart = () => canFetch(config.networkMode) && config.canRun(); const resolve = (value) => { if (!isResolved) { isResolved = true; config.onSuccess?.(value); continueFn?.(); promiseResolve(value); } }; const reject = (value) => { if (!isResolved) { isResolved = true; config.onError?.(value); continueFn?.(); promiseReject(value); } }; const pause = () => { return new Promise((continueResolve) => { continueFn = (value) => { if (isResolved || canContinue()) { continueResolve(value); } }; config.onPause?.(); }).then(() => { continueFn = void 0; if (!isResolved) { config.onContinue?.(); } }); }; const run = () => { if (isResolved) { return; } let promiseOrValue; const initialPromise = failureCount === 0 ? config.initialPromise : void 0; try { promiseOrValue = initialPromise ?? config.fn(); } catch (error) { promiseOrValue = Promise.reject(error); } Promise.resolve(promiseOrValue).then(resolve).catch((error) => { if (isResolved) { return; } const retry = config.retry ?? (isServer ? 0 : 3); const retryDelay = config.retryDelay ?? defaultRetryDelay; const delay = typeof retryDelay === "function" ? retryDelay(failureCount, error) : retryDelay; const shouldRetry = retry === true || typeof retry === "number" && failureCount < retry || typeof retry === "function" && retry(failureCount, error); if (isRetryCancelled || !shouldRetry) { reject(error); return; } failureCount++; config.onFail?.(failureCount, error); sleep(delay).then(() => { return canContinue() ? void 0 : pause(); }).then(() => { if (isRetryCancelled) { reject(error); } else { run(); } }); }); }; return { promise, cancel, continue: () => { continueFn?.(); return promise; }, cancelRetry, continueRetry, canStart, start: () => { if (canStart()) { run(); } else { pause().then(run); } return promise; } }; } // node_modules/@tanstack/query-core/build/modern/notifyManager.js function createNotifyManager() { let queue = []; let transactions = 0; let notifyFn = (callback) => { callback(); }; let batchNotifyFn = (callback) => { callback(); }; let scheduleFn = (cb) => setTimeout(cb, 0); const setScheduler = (fn) => { scheduleFn = fn; }; const batch = (callback) => { let result; transactions++; try { result = callback(); } finally { transactions--; if (!transactions) { flush(); } } return result; }; const schedule = (callback) => { if (transactions) { queue.push(callback); } else { scheduleFn(() => { notifyFn(callback); }); } }; const batchCalls = (callback) => { return (...args) => { schedule(() => { callback(...args); }); }; }; const flush = () => { const originalQueue = queue; queue = []; if (originalQueue.length) { scheduleFn(() => { batchNotifyFn(() => { originalQueue.forEach((callback) => { notifyFn(callback); }); }); }); } }; const setNotifyFunction = (fn) => { notifyFn = fn; }; const setBatchNotifyFunction = (fn) => { batchNotifyFn = fn; }; return { batch, batchCalls, schedule, setNotifyFunction, setBatchNotifyFunction, setScheduler }; } var notifyManager = createNotifyManager(); // node_modules/@tanstack/query-core/build/modern/removable.js var Removable = class { #gcTimeout; destroy() { this.clearGcTimeout(); } scheduleGc() { this.clearGcTimeout(); if (isValidTimeout(this.gcTime)) { this.#gcTimeout = setTimeout(() => { this.optionalRemove(); }, this.gcTime); } } updateGcTime(newGcTime) { this.gcTime = Math.max( this.gcTime || 0, newGcTime ?? (isServer ? Infinity : 5 * 60 * 1e3) ); } clearGcTimeout() { if (this.#gcTimeout) { clearTimeout(this.#gcTimeout); this.#gcTimeout = void 0; } } }; // node_modules/@tanstack/query-core/build/modern/query.js var Query = class extends Removable { #initialState; #revertState; #cache; #retryer; #defaultOptions; #abortSignalConsumed; constructor(config) { super(); this.#abortSignalConsumed = false; this.#defaultOptions = config.defaultOptions; this.setOptions(config.options); this.observers = []; this.#cache = config.cache; this.queryKey = config.queryKey; this.queryHash = config.queryHash; this.#initialState = config.state || getDefaultState(this.options); this.state = this.#initialState; this.scheduleGc(); } get meta() { return this.options.meta; } get promise() { return this.#retryer?.promise; } setOptions(options) { this.options = { ...this.#defaultOptions, ...options }; this.updateGcTime(this.options.gcTime); } optionalRemove() { if (!this.observers.length && this.state.fetchStatus === "idle") { this.#cache.remove(this); } } setData(newData, options) { const data = replaceData(this.state.data, newData, this.options); this.#dispatch({ data, type: "success", dataUpdatedAt: options?.updatedAt, manual: options?.manual }); return data; } setState(state, setStateOptions) { this.#dispatch({ type: "setState", state, setStateOptions }); } cancel(options) { const promise = this.#retryer?.promise; this.#retryer?.cancel(options); return promise ? promise.then(noop).catch(noop) : Promise.resolve(); } destroy() { super.destroy(); this.cancel({ silent: true }); } reset() { this.destroy(); this.setState(this.#initialState); } isActive() { return this.observers.some((observer) => observer.options.enabled !== false); } isDisabled() { return this.getObserversCount() > 0 && !this.isActive(); } isStale() { if (this.state.isInvalidated) { return true; } if (this.getObserversCount() > 0) { return this.observers.some( (observer) => observer.getCurrentResult().isStale ); } return this.state.data === void 0; } isStaleByTime(staleTime = 0) { return this.state.isInvalidated || this.state.data === void 0 || !timeUntilStale(this.state.dataUpdatedAt, staleTime); } onFocus() { const observer = this.observers.find((x) => x.shouldFetchOnWindowFocus()); observer?.refetch({ cancelRefetch: false }); this.#retryer?.continue(); } onOnline() { const observer = this.observers.find((x) => x.shouldFetchOnReconnect()); observer?.refetch({ cancelRefetch: false }); this.#retryer?.continue(); } addObserver(observer) { if (!this.observers.includes(observer)) { this.observers.push(observer); this.clearGcTimeout(); this.#cache.notify({ type: "observerAdded", query: this, observer }); } } removeObserver(observer) { if (this.observers.includes(observer)) { this.observers = this.observers.filter((x) => x !== observer); if (!this.observers.length) { if (this.#retryer) { if (this.#abortSignalConsumed) { this.#retryer.cancel({ revert: true }); } else { this.#retryer.cancelRetry(); } } this.scheduleGc(); } this.#cache.notify({ type: "observerRemoved", query: this, observer }); } } getObserversCount() { return this.observers.length; } invalidate() { if (!this.state.isInvalidated) { this.#dispatch({ type: "invalidate" }); } } fetch(options, fetchOptions) { if (this.state.fetchStatus !== "idle") { if (this.state.data !== void 0 && fetchOptions?.cancelRefetch) { this.cancel({ silent: true }); } else if (this.#retryer) { this.#retryer.continueRetry(); return this.#retryer.promise; } } if (options) { this.setOptions(options); } if (!this.options.queryFn) { const observer = this.observers.find((x) => x.options.queryFn); if (observer) { this.setOptions(observer.options); } } if (process.env.NODE_ENV !== "production") { if (!Array.isArray(this.options.queryKey)) { console.error( `As of v4, queryKey needs to be an Array. If you are using a string like 'repoData', please change it to an Array, e.g. ['repoData']` ); } } const abortController = new AbortController(); const addSignalProperty = (object) => { Object.defineProperty(object, "signal", { enumerable: true, get: () => { this.#abortSignalConsumed = true; return abortController.signal; } }); }; const fetchFn = () => { const queryFn = ensureQueryFn(this.options, fetchOptions); const queryFnContext = { queryKey: this.queryKey, meta: this.meta }; addSignalProperty(queryFnContext); this.#abortSignalConsumed = false; if (this.options.persister) { return this.options.persister( queryFn, queryFnContext, this ); } return queryFn(queryFnContext); }; const context = { fetchOptions, options: this.options, queryKey: this.queryKey, state: this.state, fetchFn }; addSignalProperty(context); this.options.behavior?.onFetch( context, this ); this.#revertState = this.state; if (this.state.fetchStatus === "idle" || this.state.fetchMeta !== context.fetchOptions?.meta) { this.#dispatch({ type: "fetch", meta: context.fetchOptions?.meta }); } const onError = (error) => { if (!(isCancelledError(error) && error.silent)) { this.#dispatch({ type: "error", error }); } if (!isCancelledError(error)) { this.#cache.config.onError?.( error, this ); this.#cache.config.onSettled?.( this.state.data, error, this ); } if (!this.isFetchingOptimistic) { this.scheduleGc(); } this.isFetchingOptimistic = false; }; this.#retryer = createRetryer({ initialPromise: fetchOptions?.initialPromise, fn: context.fetchFn, abort: abortController.abort.bind(abortController), onSuccess: (data) => { if (data === void 0) { if (process.env.NODE_ENV !== "production") { console.error( `Query data cannot be undefined. Please make sure to return a value other than undefined from your query function. Affected query key: ${this.queryHash}` ); } onError(new Error(`${this.queryHash} data is undefined`)); return; } this.setData(data); this.#cache.config.onSuccess?.(data, this); this.#cache.config.onSettled?.( data, this.state.error, this ); if (!this.isFetchingOptimistic) { this.scheduleGc(); } this.isFetchingOptimistic = false; }, onError, onFail: (failureCount, error) => { this.#dispatch({ type: "failed", failureCount, error }); }, onPause: () => { this.#dispatch({ type: "pause" }); }, onContinue: () => { this.#dispatch({ type: "continue" }); }, retry: context.options.retry, retryDelay: context.options.retryDelay, networkMode: context.options.networkMode, canRun: () => true }); return this.#retryer.start(); } #dispatch(action) { const reducer = (state) => { switch (action.type) { case "failed": return { ...state, fetchFailureCount: action.failureCount, fetchFailureReason: action.error }; case "pause": return { ...state, fetchStatus: "paused" }; case "continue": return { ...state, fetchStatus: "fetching" }; case "fetch": return { ...state, ...fetchState(state.data, this.options), fetchMeta: action.meta ?? null }; case "success": return { ...state, data: action.data, dataUpdateCount: state.dataUpdateCount + 1, dataUpdatedAt: action.dataUpdatedAt ?? Date.now(), error: null, isInvalidated: false, status: "success", ...!action.manual && { fetchStatus: "idle", fetchFailureCount: 0, fetchFailureReason: null } }; case "error": const error = action.error; if (isCancelledError(error) && error.revert && this.#revertState) { return { ...this.#revertState, fetchStatus: "idle" }; } return { ...state, error, errorUpdateCount: state.errorUpdateCount + 1, errorUpdatedAt: Date.now(), fetchFailureCount: state.fetchFailureCount + 1, fetchFailureReason: error, fetchStatus: "idle", status: "error" }; case "invalidate": return { ...state, isInvalidated: true }; case "setState": return { ...state, ...action.state }; } }; this.state = reducer(this.state); notifyManager.batch(() => { this.observers.forEach((observer) => { observer.onQueryUpdate(); }); this.#cache.notify({ query: this, type: "updated", action }); }); } }; function fetchState(data, options) { return { fetchFailureCount: 0, fetchFailureReason: null, fetchStatus: canFetch(options.networkMode) ? "fetching" : "paused", ...data === void 0 && { error: null, status: "pending" } }; } function getDefaultState(options) { const data = typeof options.initialData === "function" ? options.initialData() : options.initialData; const hasData = data !== void 0; const initialDataUpdatedAt = hasData ? typeof options.initialDataUpdatedAt === "function" ? options.initialDataUpdatedAt() : options.initialDataUpdatedAt : 0; return { data, dataUpdateCount: 0, dataUpdatedAt: hasData ? initialDataUpdatedAt ?? Date.now() : 0, error: null, errorUpdateCount: 0, errorUpdatedAt: 0, fetchFailureCount: 0, fetchFailureReason: null, fetchMeta: null, isInvalidated: false, status: hasData ? "success" : "pending", fetchStatus: "idle" }; } // node_modules/@tanstack/query-core/build/modern/queryCache.js var QueryCache = class extends Subscribable { constructor(config = {}) { super(); this.config = config; this.#queries = /* @__PURE__ */ new Map(); } #queries; build(client, options, state) { const queryKey = options.queryKey; const queryHash = options.queryHash ?? hashQueryKeyByOptions(queryKey, options); let query = this.get(queryHash); if (!query) { query = new Query({ cache: this, queryKey, queryHash, options: client.defaultQueryOptions(options), state, defaultOptions: client.getQueryDefaults(queryKey) }); this.add(query); } return query; } add(query) { if (!this.#queries.has(query.queryHash)) { this.#queries.set(query.queryHash, query); this.notify({ type: "added", query }); } } remove(query) { const queryInMap = this.#queries.get(query.queryHash); if (queryInMap) { query.destroy(); if (queryInMap === query) { this.#queries.delete(query.queryHash); } this.notify({ type: "removed", query }); } } clear() { notifyManager.batch(() => { this.getAll().forEach((query) => { this.remove(query); }); }); } get(queryHash) { return this.#queries.get(queryHash); } getAll() { return [...this.#queries.values()]; } find(filters) { const defaultedFilters = { exact: true, ...filters }; return this.getAll().find( (query) => matchQuery(defaultedFilters, query) ); } findAll(filters = {}) { const queries = this.getAll(); return Object.keys(filters).length > 0 ? queries.filter((query) => matchQuery(filters, query)) : queries; } notify(event) { notifyManager.batch(() => { this.listeners.forEach((listener) => { listener(event); }); }); } onFocus() { notifyManager.batch(() => { this.getAll().forEach((query) => { query.onFocus(); }); }); } onOnline() { notifyManager.batch(() => { this.getAll().forEach((query) => { query.onOnline(); }); }); } }; // packages/core/src/PersistentQueryCache.ts var PersistentQueryCache = class extends QueryCache { persistor; constructor(persistor) { super(); this.persistor = persistor; } add(query) { this.persistor.add(query); super.add(query); } remove(query) { super.remove(query); this.persistor.removed(query); } notify(event) { super.notify(event); if (event.type === "updated") { const query = event.query; this.persistor.updated(query); } else if (event.type === "observerResultsUpdated") { const query = event.query; this.persistor.updated(query); } } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { PersistentQueryCache });