@tanstack/query-core 5.85.9 → 5.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/legacy/{hydration-BpLOP9Dw.d.ts → hydration-BYonJkjc.d.ts} +1 -1
- package/build/legacy/{hydration-BC7iBQD-.d.cts → hydration-_hO-y142.d.cts} +1 -1
- package/build/legacy/hydration.d.cts +1 -1
- package/build/legacy/hydration.d.ts +1 -1
- package/build/legacy/index.cjs +14 -12
- package/build/legacy/index.cjs.map +1 -1
- package/build/legacy/index.d.cts +5 -4
- package/build/legacy/index.d.ts +5 -4
- package/build/legacy/index.js +26 -23
- package/build/legacy/index.js.map +1 -1
- package/build/legacy/infiniteQueryBehavior.d.cts +1 -1
- package/build/legacy/infiniteQueryBehavior.d.ts +1 -1
- package/build/legacy/infiniteQueryObserver.d.cts +1 -1
- package/build/legacy/infiniteQueryObserver.d.ts +1 -1
- package/build/legacy/mutation.d.cts +1 -1
- package/build/legacy/mutation.d.ts +1 -1
- package/build/legacy/mutationCache.d.cts +1 -1
- package/build/legacy/mutationCache.d.ts +1 -1
- package/build/legacy/mutationObserver.d.cts +1 -1
- package/build/legacy/mutationObserver.d.ts +1 -1
- package/build/legacy/notifyManager.cjs +2 -1
- package/build/legacy/notifyManager.cjs.map +1 -1
- package/build/legacy/notifyManager.js +2 -1
- package/build/legacy/notifyManager.js.map +1 -1
- package/build/legacy/queriesObserver.d.cts +1 -1
- package/build/legacy/queriesObserver.d.ts +1 -1
- package/build/legacy/query.d.cts +1 -1
- package/build/legacy/query.d.ts +1 -1
- package/build/legacy/queryCache.d.cts +1 -1
- package/build/legacy/queryCache.d.ts +1 -1
- package/build/legacy/queryClient.d.cts +1 -1
- package/build/legacy/queryClient.d.ts +1 -1
- package/build/legacy/queryObserver.cjs +5 -4
- package/build/legacy/queryObserver.cjs.map +1 -1
- package/build/legacy/queryObserver.d.cts +1 -1
- package/build/legacy/queryObserver.d.ts +1 -1
- package/build/legacy/queryObserver.js +5 -4
- package/build/legacy/queryObserver.js.map +1 -1
- package/build/legacy/removable.cjs +3 -2
- package/build/legacy/removable.cjs.map +1 -1
- package/build/legacy/removable.js +3 -2
- package/build/legacy/removable.js.map +1 -1
- package/build/legacy/retryer.d.cts +1 -1
- package/build/legacy/retryer.d.ts +1 -1
- package/build/legacy/streamedQuery.cjs +7 -8
- package/build/legacy/streamedQuery.cjs.map +1 -1
- package/build/legacy/streamedQuery.d.cts +18 -10
- package/build/legacy/streamedQuery.d.ts +18 -10
- package/build/legacy/streamedQuery.js +7 -8
- package/build/legacy/streamedQuery.js.map +1 -1
- package/build/legacy/timeoutManager.cjs +110 -0
- package/build/legacy/timeoutManager.cjs.map +1 -0
- package/build/legacy/timeoutManager.d.cts +58 -0
- package/build/legacy/timeoutManager.d.ts +58 -0
- package/build/legacy/timeoutManager.js +81 -0
- package/build/legacy/timeoutManager.js.map +1 -0
- package/build/legacy/types.d.cts +1 -1
- package/build/legacy/types.d.ts +1 -1
- package/build/legacy/utils.cjs +2 -1
- package/build/legacy/utils.cjs.map +1 -1
- package/build/legacy/utils.d.cts +1 -1
- package/build/legacy/utils.d.ts +1 -1
- package/build/legacy/utils.js +2 -1
- package/build/legacy/utils.js.map +1 -1
- package/build/modern/{hydration-BpLOP9Dw.d.ts → hydration-BYonJkjc.d.ts} +1 -1
- package/build/modern/{hydration-BC7iBQD-.d.cts → hydration-_hO-y142.d.cts} +1 -1
- package/build/modern/hydration.d.cts +1 -1
- package/build/modern/hydration.d.ts +1 -1
- package/build/modern/index.cjs +14 -12
- package/build/modern/index.cjs.map +1 -1
- package/build/modern/index.d.cts +5 -4
- package/build/modern/index.d.ts +5 -4
- package/build/modern/index.js +26 -23
- package/build/modern/index.js.map +1 -1
- package/build/modern/infiniteQueryBehavior.d.cts +1 -1
- package/build/modern/infiniteQueryBehavior.d.ts +1 -1
- package/build/modern/infiniteQueryObserver.d.cts +1 -1
- package/build/modern/infiniteQueryObserver.d.ts +1 -1
- package/build/modern/mutation.d.cts +1 -1
- package/build/modern/mutation.d.ts +1 -1
- package/build/modern/mutationCache.d.cts +1 -1
- package/build/modern/mutationCache.d.ts +1 -1
- package/build/modern/mutationObserver.d.cts +1 -1
- package/build/modern/mutationObserver.d.ts +1 -1
- package/build/modern/notifyManager.cjs +2 -1
- package/build/modern/notifyManager.cjs.map +1 -1
- package/build/modern/notifyManager.js +2 -1
- package/build/modern/notifyManager.js.map +1 -1
- package/build/modern/queriesObserver.d.cts +1 -1
- package/build/modern/queriesObserver.d.ts +1 -1
- package/build/modern/query.d.cts +1 -1
- package/build/modern/query.d.ts +1 -1
- package/build/modern/queryCache.d.cts +1 -1
- package/build/modern/queryCache.d.ts +1 -1
- package/build/modern/queryClient.d.cts +1 -1
- package/build/modern/queryClient.d.ts +1 -1
- package/build/modern/queryObserver.cjs +5 -4
- package/build/modern/queryObserver.cjs.map +1 -1
- package/build/modern/queryObserver.d.cts +1 -1
- package/build/modern/queryObserver.d.ts +1 -1
- package/build/modern/queryObserver.js +5 -4
- package/build/modern/queryObserver.js.map +1 -1
- package/build/modern/removable.cjs +3 -2
- package/build/modern/removable.cjs.map +1 -1
- package/build/modern/removable.js +3 -2
- package/build/modern/removable.js.map +1 -1
- package/build/modern/retryer.d.cts +1 -1
- package/build/modern/retryer.d.ts +1 -1
- package/build/modern/streamedQuery.cjs +7 -8
- package/build/modern/streamedQuery.cjs.map +1 -1
- package/build/modern/streamedQuery.d.cts +18 -10
- package/build/modern/streamedQuery.d.ts +18 -10
- package/build/modern/streamedQuery.js +7 -8
- package/build/modern/streamedQuery.js.map +1 -1
- package/build/modern/timeoutManager.cjs +98 -0
- package/build/modern/timeoutManager.cjs.map +1 -0
- package/build/modern/timeoutManager.d.cts +58 -0
- package/build/modern/timeoutManager.d.ts +58 -0
- package/build/modern/timeoutManager.js +70 -0
- package/build/modern/timeoutManager.js.map +1 -0
- package/build/modern/types.d.cts +1 -1
- package/build/modern/types.d.ts +1 -1
- package/build/modern/utils.cjs +2 -1
- package/build/modern/utils.cjs.map +1 -1
- package/build/modern/utils.d.cts +1 -1
- package/build/modern/utils.d.ts +1 -1
- package/build/modern/utils.js +2 -1
- package/build/modern/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +32 -27
- package/src/notifyManager.ts +3 -1
- package/src/queryObserver.ts +8 -6
- package/src/removable.ts +5 -3
- package/src/streamedQuery.ts +47 -23
- package/src/timeoutManager.ts +135 -0
- package/src/utils.ts +2 -1
package/build/modern/utils.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/utils.ts
|
|
2
|
+
import { timeoutManager } from "./timeoutManager.js";
|
|
2
3
|
var isServer = typeof window === "undefined" || "Deno" in globalThis;
|
|
3
4
|
function noop() {
|
|
4
5
|
}
|
|
@@ -173,7 +174,7 @@ function hasObjectPrototype(o) {
|
|
|
173
174
|
}
|
|
174
175
|
function sleep(timeout) {
|
|
175
176
|
return new Promise((resolve) => {
|
|
176
|
-
setTimeout(resolve, timeout);
|
|
177
|
+
timeoutManager.setTimeout(resolve, timeout);
|
|
177
178
|
});
|
|
178
179
|
}
|
|
179
180
|
function replaceData(prevData, data, options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["import type {\n DefaultError,\n Enabled,\n FetchStatus,\n MutationKey,\n MutationStatus,\n QueryFunction,\n QueryKey,\n QueryOptions,\n StaleTime,\n StaleTimeFunction,\n} from './types'\nimport type { Mutation } from './mutation'\nimport type { FetchOptions, Query } from './query'\n\n// TYPES\n\nexport interface QueryFilters<TQueryKey extends QueryKey = QueryKey> {\n /**\n * Filter to active queries, inactive queries or all queries\n */\n type?: QueryTypeFilter\n /**\n * Match query key exactly\n */\n exact?: boolean\n /**\n * Include queries matching this predicate function\n */\n predicate?: (query: Query) => boolean\n /**\n * Include queries matching this query key\n */\n queryKey?: TQueryKey\n /**\n * Include or exclude stale queries\n */\n stale?: boolean\n /**\n * Include queries matching their fetchStatus\n */\n fetchStatus?: FetchStatus\n}\n\nexport interface MutationFilters<\n TData = unknown,\n TError = DefaultError,\n TVariables = unknown,\n TContext = unknown,\n> {\n /**\n * Match mutation key exactly\n */\n exact?: boolean\n /**\n * Include mutations matching this predicate function\n */\n predicate?: (\n mutation: Mutation<TData, TError, TVariables, TContext>,\n ) => boolean\n /**\n * Include mutations matching this mutation key\n */\n mutationKey?: MutationKey\n /**\n * Filter by mutation status\n */\n status?: MutationStatus\n}\n\nexport type Updater<TInput, TOutput> = TOutput | ((input: TInput) => TOutput)\n\nexport type QueryTypeFilter = 'all' | 'active' | 'inactive'\n\n// UTILS\n\nexport const isServer = typeof window === 'undefined' || 'Deno' in globalThis\n\nexport function noop(): void\nexport function noop(): undefined\nexport function noop() {}\n\nexport function functionalUpdate<TInput, TOutput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as (_: TInput) => TOutput)(input)\n : updater\n}\n\nexport function isValidTimeout(value: unknown): value is number {\n return typeof value === 'number' && value >= 0 && value !== Infinity\n}\n\nexport function timeUntilStale(updatedAt: number, staleTime?: number): number {\n return Math.max(updatedAt + (staleTime || 0) - Date.now(), 0)\n}\n\nexport function resolveStaleTime<\n TQueryFnData = unknown,\n TError = DefaultError,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n staleTime:\n | undefined\n | StaleTimeFunction<TQueryFnData, TError, TData, TQueryKey>,\n query: Query<TQueryFnData, TError, TData, TQueryKey>,\n): StaleTime | undefined {\n return typeof staleTime === 'function' ? staleTime(query) : staleTime\n}\n\nexport function resolveEnabled<\n TQueryFnData = unknown,\n TError = DefaultError,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n enabled: undefined | Enabled<TQueryFnData, TError, TData, TQueryKey>,\n query: Query<TQueryFnData, TError, TData, TQueryKey>,\n): boolean | undefined {\n return typeof enabled === 'function' ? enabled(query) : enabled\n}\n\nexport function matchQuery(\n filters: QueryFilters,\n query: Query<any, any, any, any>,\n): boolean {\n const {\n type = 'all',\n exact,\n fetchStatus,\n predicate,\n queryKey,\n stale,\n } = filters\n\n if (queryKey) {\n if (exact) {\n if (query.queryHash !== hashQueryKeyByOptions(queryKey, query.options)) {\n return false\n }\n } else if (!partialMatchKey(query.queryKey, queryKey)) {\n return false\n }\n }\n\n if (type !== 'all') {\n const isActive = query.isActive()\n if (type === 'active' && !isActive) {\n return false\n }\n if (type === 'inactive' && isActive) {\n return false\n }\n }\n\n if (typeof stale === 'boolean' && query.isStale() !== stale) {\n return false\n }\n\n if (fetchStatus && fetchStatus !== query.state.fetchStatus) {\n return false\n }\n\n if (predicate && !predicate(query)) {\n return false\n }\n\n return true\n}\n\nexport function matchMutation(\n filters: MutationFilters,\n mutation: Mutation<any, any>,\n): boolean {\n const { exact, status, predicate, mutationKey } = filters\n if (mutationKey) {\n if (!mutation.options.mutationKey) {\n return false\n }\n if (exact) {\n if (hashKey(mutation.options.mutationKey) !== hashKey(mutationKey)) {\n return false\n }\n } else if (!partialMatchKey(mutation.options.mutationKey, mutationKey)) {\n return false\n }\n }\n\n if (status && mutation.state.status !== status) {\n return false\n }\n\n if (predicate && !predicate(mutation)) {\n return false\n }\n\n return true\n}\n\nexport function hashQueryKeyByOptions<TQueryKey extends QueryKey = QueryKey>(\n queryKey: TQueryKey,\n options?: Pick<QueryOptions<any, any, any, any>, 'queryKeyHashFn'>,\n): string {\n const hashFn = options?.queryKeyHashFn || hashKey\n return hashFn(queryKey)\n}\n\n/**\n * Default query & mutation keys hash function.\n * Hashes the value into a stable hash.\n */\nexport function hashKey(queryKey: QueryKey | MutationKey): string {\n return JSON.stringify(queryKey, (_, val) =>\n isPlainObject(val)\n ? Object.keys(val)\n .sort()\n .reduce((result, key) => {\n result[key] = val[key]\n return result\n }, {} as any)\n : val,\n )\n}\n\n/**\n * Checks if key `b` partially matches with key `a`.\n */\nexport function partialMatchKey(a: QueryKey, b: QueryKey): boolean\nexport function partialMatchKey(a: any, b: any): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (a && b && typeof a === 'object' && typeof b === 'object') {\n return Object.keys(b).every((key) => partialMatchKey(a[key], b[key]))\n }\n\n return false\n}\n\nconst hasOwn = Object.prototype.hasOwnProperty\n\n/**\n * This function returns `a` if `b` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between JSON values for example.\n */\nexport function replaceEqualDeep<T>(a: unknown, b: T): T\nexport function replaceEqualDeep(a: any, b: any): any {\n if (a === b) {\n return a\n }\n\n const array = isPlainArray(a) && isPlainArray(b)\n\n if (!array && !(isPlainObject(a) && isPlainObject(b))) return b\n\n const aItems = array ? a : Object.keys(a)\n const aSize = aItems.length\n const bItems = array ? b : Object.keys(b)\n const bSize = bItems.length\n const copy: any = array ? new Array(bSize) : {}\n\n let equalItems = 0\n\n for (let i = 0; i < bSize; i++) {\n const key: any = array ? i : bItems[i]\n const aItem = a[key]\n const bItem = b[key]\n\n if (aItem === bItem) {\n copy[key] = aItem\n if (array ? i < aSize : hasOwn.call(a, key)) equalItems++\n continue\n }\n\n if (\n aItem === null ||\n bItem === null ||\n typeof aItem !== 'object' ||\n typeof bItem !== 'object'\n ) {\n copy[key] = bItem\n continue\n }\n\n const v = replaceEqualDeep(aItem, bItem)\n copy[key] = v\n if (v === aItem) equalItems++\n }\n\n return aSize === bSize && equalItems === aSize ? a : copy\n}\n\n/**\n * Shallow compare objects.\n */\nexport function shallowEqualObjects<T extends Record<string, any>>(\n a: T,\n b: T | undefined,\n): boolean {\n if (!b || Object.keys(a).length !== Object.keys(b).length) {\n return false\n }\n\n for (const key in a) {\n if (a[key] !== b[key]) {\n return false\n }\n }\n\n return true\n}\n\nexport function isPlainArray(value: unknown): value is Array<unknown> {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any): o is Record<PropertyKey, unknown> {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has no constructor\n const ctor = o.constructor\n if (ctor === undefined) {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Handles Objects created by Object.create(<arbitrary prototype>)\n if (Object.getPrototypeOf(o) !== Object.prototype) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any): boolean {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\nexport function sleep(timeout: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, timeout)\n })\n}\n\nexport function replaceData<\n TData,\n TOptions extends QueryOptions<any, any, any, any>,\n>(prevData: TData | undefined, data: TData, options: TOptions): TData {\n if (typeof options.structuralSharing === 'function') {\n return options.structuralSharing(prevData, data) as TData\n } else if (options.structuralSharing !== false) {\n if (process.env.NODE_ENV !== 'production') {\n try {\n return replaceEqualDeep(prevData, data)\n } catch (error) {\n console.error(\n `Structural sharing requires data to be JSON serializable. To fix this, turn off structuralSharing or return JSON-serializable data from your queryFn. [${options.queryHash}]: ${error}`,\n )\n\n // Prevent the replaceEqualDeep from being called again down below.\n throw error\n }\n }\n // Structurally share data between prev and new data if needed\n return replaceEqualDeep(prevData, data)\n }\n return data\n}\n\nexport function keepPreviousData<T>(\n previousData: T | undefined,\n): T | undefined {\n return previousData\n}\n\nexport function addToEnd<T>(items: Array<T>, item: T, max = 0): Array<T> {\n const newItems = [...items, item]\n return max && newItems.length > max ? newItems.slice(1) : newItems\n}\n\nexport function addToStart<T>(items: Array<T>, item: T, max = 0): Array<T> {\n const newItems = [item, ...items]\n return max && newItems.length > max ? newItems.slice(0, -1) : newItems\n}\n\nexport const skipToken = Symbol()\nexport type SkipToken = typeof skipToken\n\nexport function ensureQueryFn<\n TQueryFnData = unknown,\n TQueryKey extends QueryKey = QueryKey,\n>(\n options: {\n queryFn?: QueryFunction<TQueryFnData, TQueryKey> | SkipToken\n queryHash?: string\n },\n fetchOptions?: FetchOptions<TQueryFnData>,\n): QueryFunction<TQueryFnData, TQueryKey> {\n if (process.env.NODE_ENV !== 'production') {\n if (options.queryFn === skipToken) {\n console.error(\n `Attempted to invoke queryFn when set to skipToken. This is likely a configuration error. Query hash: '${options.queryHash}'`,\n )\n }\n }\n\n // if we attempt to retry a fetch that was triggered from an initialPromise\n // when we don't have a queryFn yet, we can't retry, so we just return the already rejected initialPromise\n // if an observer has already mounted, we will be able to retry with that queryFn\n if (!options.queryFn && fetchOptions?.initialPromise) {\n return () => fetchOptions.initialPromise!\n }\n\n if (!options.queryFn || options.queryFn === skipToken) {\n return () =>\n Promise.reject(new Error(`Missing queryFn: '${options.queryHash}'`))\n }\n\n return options.queryFn\n}\n\nexport function shouldThrowError<T extends (...args: Array<any>) => boolean>(\n throwOnError: boolean | T | undefined,\n params: Parameters<T>,\n): boolean {\n // Allow throwOnError function to override throwing behavior on a per-error basis\n if (typeof throwOnError === 'function') {\n return throwOnError(...params)\n }\n\n return !!throwOnError\n}\n"],"mappings":";AA4EO,IAAM,WAAW,OAAO,WAAW,eAAe,UAAU;AAI5D,SAAS,OAAO;AAAC;AAEjB,SAAS,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAmC,KAAK,IACzC;AACN;AAEO,SAAS,eAAe,OAAiC;AAC9D,SAAO,OAAO,UAAU,YAAY,SAAS,KAAK,UAAU;AAC9D;AAEO,SAAS,eAAe,WAAmB,WAA4B;AAC5E,SAAO,KAAK,IAAI,aAAa,aAAa,KAAK,KAAK,IAAI,GAAG,CAAC;AAC9D;AAEO,SAAS,iBAMd,WAGA,OACuB;AACvB,SAAO,OAAO,cAAc,aAAa,UAAU,KAAK,IAAI;AAC9D;AAEO,SAAS,eAMd,SACA,OACqB;AACrB,SAAO,OAAO,YAAY,aAAa,QAAQ,KAAK,IAAI;AAC1D;AAEO,SAAS,WACd,SACA,OACS;AACT,QAAM;AAAA,IACJ,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,MAAI,UAAU;AACZ,QAAI,OAAO;AACT,UAAI,MAAM,cAAc,sBAAsB,UAAU,MAAM,OAAO,GAAG;AACtE,eAAO;AAAA,MACT;AAAA,IACF,WAAW,CAAC,gBAAgB,MAAM,UAAU,QAAQ,GAAG;AACrD,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,SAAS,OAAO;AAClB,UAAM,WAAW,MAAM,SAAS;AAChC,QAAI,SAAS,YAAY,CAAC,UAAU;AAClC,aAAO;AAAA,IACT;AACA,QAAI,SAAS,cAAc,UAAU;AACnC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,OAAO,UAAU,aAAa,MAAM,QAAQ,MAAM,OAAO;AAC3D,WAAO;AAAA,EACT;AAEA,MAAI,eAAe,gBAAgB,MAAM,MAAM,aAAa;AAC1D,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,CAAC,UAAU,KAAK,GAAG;AAClC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,cACd,SACA,UACS;AACT,QAAM,EAAE,OAAO,QAAQ,WAAW,YAAY,IAAI;AAClD,MAAI,aAAa;AACf,QAAI,CAAC,SAAS,QAAQ,aAAa;AACjC,aAAO;AAAA,IACT;AACA,QAAI,OAAO;AACT,UAAI,QAAQ,SAAS,QAAQ,WAAW,MAAM,QAAQ,WAAW,GAAG;AAClE,eAAO;AAAA,MACT;AAAA,IACF,WAAW,CAAC,gBAAgB,SAAS,QAAQ,aAAa,WAAW,GAAG;AACtE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,UAAU,SAAS,MAAM,WAAW,QAAQ;AAC9C,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,CAAC,UAAU,QAAQ,GAAG;AACrC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,sBACd,UACA,SACQ;AACR,QAAM,SAAS,SAAS,kBAAkB;AAC1C,SAAO,OAAO,QAAQ;AACxB;AAMO,SAAS,QAAQ,UAA0C;AAChE,SAAO,KAAK;AAAA,IAAU;AAAA,IAAU,CAAC,GAAG,QAClC,cAAc,GAAG,IACb,OAAO,KAAK,GAAG,EACZ,KAAK,EACL,OAAO,CAAC,QAAQ,QAAQ;AACvB,aAAO,GAAG,IAAI,IAAI,GAAG;AACrB,aAAO;AAAA,IACT,GAAG,CAAC,CAAQ,IACd;AAAA,EACN;AACF;AAMO,SAAS,gBAAgB,GAAQ,GAAiB;AACvD,MAAI,MAAM,GAAG;AACX,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,MAAM,OAAO,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,MAAI,KAAK,KAAK,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;AAC5D,WAAO,OAAO,KAAK,CAAC,EAAE,MAAM,CAAC,QAAQ,gBAAgB,EAAE,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;AAAA,EACtE;AAEA,SAAO;AACT;AAEA,IAAM,SAAS,OAAO,UAAU;AAQzB,SAAS,iBAAiB,GAAQ,GAAa;AACpD,MAAI,MAAM,GAAG;AACX,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,aAAa,CAAC,KAAK,aAAa,CAAC;AAE/C,MAAI,CAAC,SAAS,EAAE,cAAc,CAAC,KAAK,cAAc,CAAC,GAAI,QAAO;AAE9D,QAAM,SAAS,QAAQ,IAAI,OAAO,KAAK,CAAC;AACxC,QAAM,QAAQ,OAAO;AACrB,QAAM,SAAS,QAAQ,IAAI,OAAO,KAAK,CAAC;AACxC,QAAM,QAAQ,OAAO;AACrB,QAAM,OAAY,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;AAE9C,MAAI,aAAa;AAEjB,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,UAAM,MAAW,QAAQ,IAAI,OAAO,CAAC;AACrC,UAAM,QAAQ,EAAE,GAAG;AACnB,UAAM,QAAQ,EAAE,GAAG;AAEnB,QAAI,UAAU,OAAO;AACnB,WAAK,GAAG,IAAI;AACZ,UAAI,QAAQ,IAAI,QAAQ,OAAO,KAAK,GAAG,GAAG,EAAG;AAC7C;AAAA,IACF;AAEA,QACE,UAAU,QACV,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,UACjB;AACA,WAAK,GAAG,IAAI;AACZ;AAAA,IACF;AAEA,UAAM,IAAI,iBAAiB,OAAO,KAAK;AACvC,SAAK,GAAG,IAAI;AACZ,QAAI,MAAM,MAAO;AAAA,EACnB;AAEA,SAAO,UAAU,SAAS,eAAe,QAAQ,IAAI;AACvD;AAKO,SAAS,oBACd,GACA,GACS;AACT,MAAI,CAAC,KAAK,OAAO,KAAK,CAAC,EAAE,WAAW,OAAO,KAAK,CAAC,EAAE,QAAQ;AACzD,WAAO;AAAA,EACT;AAEA,aAAW,OAAO,GAAG;AACnB,QAAI,EAAE,GAAG,MAAM,EAAE,GAAG,GAAG;AACrB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,aAAa,OAAyC;AACpE,SAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,OAAO,KAAK,KAAK,EAAE;AACrE;AAGO,SAAS,cAAc,GAA2C;AACvE,MAAI,CAAC,mBAAmB,CAAC,GAAG;AAC1B,WAAO;AAAA,EACT;AAGA,QAAM,OAAO,EAAE;AACf,MAAI,SAAS,QAAW;AACtB,WAAO;AAAA,EACT;AAGA,QAAM,OAAO,KAAK;AAClB,MAAI,CAAC,mBAAmB,IAAI,GAAG;AAC7B,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,KAAK,eAAe,eAAe,GAAG;AACzC,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,eAAe,CAAC,MAAM,OAAO,WAAW;AACjD,WAAO;AAAA,EACT;AAGA,SAAO;AACT;AAEA,SAAS,mBAAmB,GAAiB;AAC3C,SAAO,OAAO,UAAU,SAAS,KAAK,CAAC,MAAM;AAC/C;AAEO,SAAS,MAAM,SAAgC;AACpD,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,eAAW,SAAS,OAAO;AAAA,EAC7B,CAAC;AACH;AAEO,SAAS,YAGd,UAA6B,MAAa,SAA0B;AACpE,MAAI,OAAO,QAAQ,sBAAsB,YAAY;AACnD,WAAO,QAAQ,kBAAkB,UAAU,IAAI;AAAA,EACjD,WAAW,QAAQ,sBAAsB,OAAO;AAC9C,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,UAAI;AACF,eAAO,iBAAiB,UAAU,IAAI;AAAA,MACxC,SAAS,OAAO;AACd,gBAAQ;AAAA,UACN,0JAA0J,QAAQ,SAAS,MAAM,KAAK;AAAA,QACxL;AAGA,cAAM;AAAA,MACR;AAAA,IACF;AAEA,WAAO,iBAAiB,UAAU,IAAI;AAAA,EACxC;AACA,SAAO;AACT;AAEO,SAAS,iBACd,cACe;AACf,SAAO;AACT;AAEO,SAAS,SAAY,OAAiB,MAAS,MAAM,GAAa;AACvE,QAAM,WAAW,CAAC,GAAG,OAAO,IAAI;AAChC,SAAO,OAAO,SAAS,SAAS,MAAM,SAAS,MAAM,CAAC,IAAI;AAC5D;AAEO,SAAS,WAAc,OAAiB,MAAS,MAAM,GAAa;AACzE,QAAM,WAAW,CAAC,MAAM,GAAG,KAAK;AAChC,SAAO,OAAO,SAAS,SAAS,MAAM,SAAS,MAAM,GAAG,EAAE,IAAI;AAChE;AAEO,IAAM,YAAY,OAAO;AAGzB,SAAS,cAId,SAIA,cACwC;AACxC,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,QAAI,QAAQ,YAAY,WAAW;AACjC,cAAQ;AAAA,QACN,yGAAyG,QAAQ,SAAS;AAAA,MAC5H;AAAA,IACF;AAAA,EACF;AAKA,MAAI,CAAC,QAAQ,WAAW,cAAc,gBAAgB;AACpD,WAAO,MAAM,aAAa;AAAA,EAC5B;AAEA,MAAI,CAAC,QAAQ,WAAW,QAAQ,YAAY,WAAW;AACrD,WAAO,MACL,QAAQ,OAAO,IAAI,MAAM,qBAAqB,QAAQ,SAAS,GAAG,CAAC;AAAA,EACvE;AAEA,SAAO,QAAQ;AACjB;AAEO,SAAS,iBACd,cACA,QACS;AAET,MAAI,OAAO,iBAAiB,YAAY;AACtC,WAAO,aAAa,GAAG,MAAM;AAAA,EAC/B;AAEA,SAAO,CAAC,CAAC;AACX;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["import { timeoutManager } from './timeoutManager'\nimport type {\n DefaultError,\n Enabled,\n FetchStatus,\n MutationKey,\n MutationStatus,\n QueryFunction,\n QueryKey,\n QueryOptions,\n StaleTime,\n StaleTimeFunction,\n} from './types'\nimport type { Mutation } from './mutation'\nimport type { FetchOptions, Query } from './query'\n\n// TYPES\n\nexport interface QueryFilters<TQueryKey extends QueryKey = QueryKey> {\n /**\n * Filter to active queries, inactive queries or all queries\n */\n type?: QueryTypeFilter\n /**\n * Match query key exactly\n */\n exact?: boolean\n /**\n * Include queries matching this predicate function\n */\n predicate?: (query: Query) => boolean\n /**\n * Include queries matching this query key\n */\n queryKey?: TQueryKey\n /**\n * Include or exclude stale queries\n */\n stale?: boolean\n /**\n * Include queries matching their fetchStatus\n */\n fetchStatus?: FetchStatus\n}\n\nexport interface MutationFilters<\n TData = unknown,\n TError = DefaultError,\n TVariables = unknown,\n TContext = unknown,\n> {\n /**\n * Match mutation key exactly\n */\n exact?: boolean\n /**\n * Include mutations matching this predicate function\n */\n predicate?: (\n mutation: Mutation<TData, TError, TVariables, TContext>,\n ) => boolean\n /**\n * Include mutations matching this mutation key\n */\n mutationKey?: MutationKey\n /**\n * Filter by mutation status\n */\n status?: MutationStatus\n}\n\nexport type Updater<TInput, TOutput> = TOutput | ((input: TInput) => TOutput)\n\nexport type QueryTypeFilter = 'all' | 'active' | 'inactive'\n\n// UTILS\n\nexport const isServer = typeof window === 'undefined' || 'Deno' in globalThis\n\nexport function noop(): void\nexport function noop(): undefined\nexport function noop() {}\n\nexport function functionalUpdate<TInput, TOutput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as (_: TInput) => TOutput)(input)\n : updater\n}\n\nexport function isValidTimeout(value: unknown): value is number {\n return typeof value === 'number' && value >= 0 && value !== Infinity\n}\n\nexport function timeUntilStale(updatedAt: number, staleTime?: number): number {\n return Math.max(updatedAt + (staleTime || 0) - Date.now(), 0)\n}\n\nexport function resolveStaleTime<\n TQueryFnData = unknown,\n TError = DefaultError,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n staleTime:\n | undefined\n | StaleTimeFunction<TQueryFnData, TError, TData, TQueryKey>,\n query: Query<TQueryFnData, TError, TData, TQueryKey>,\n): StaleTime | undefined {\n return typeof staleTime === 'function' ? staleTime(query) : staleTime\n}\n\nexport function resolveEnabled<\n TQueryFnData = unknown,\n TError = DefaultError,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n enabled: undefined | Enabled<TQueryFnData, TError, TData, TQueryKey>,\n query: Query<TQueryFnData, TError, TData, TQueryKey>,\n): boolean | undefined {\n return typeof enabled === 'function' ? enabled(query) : enabled\n}\n\nexport function matchQuery(\n filters: QueryFilters,\n query: Query<any, any, any, any>,\n): boolean {\n const {\n type = 'all',\n exact,\n fetchStatus,\n predicate,\n queryKey,\n stale,\n } = filters\n\n if (queryKey) {\n if (exact) {\n if (query.queryHash !== hashQueryKeyByOptions(queryKey, query.options)) {\n return false\n }\n } else if (!partialMatchKey(query.queryKey, queryKey)) {\n return false\n }\n }\n\n if (type !== 'all') {\n const isActive = query.isActive()\n if (type === 'active' && !isActive) {\n return false\n }\n if (type === 'inactive' && isActive) {\n return false\n }\n }\n\n if (typeof stale === 'boolean' && query.isStale() !== stale) {\n return false\n }\n\n if (fetchStatus && fetchStatus !== query.state.fetchStatus) {\n return false\n }\n\n if (predicate && !predicate(query)) {\n return false\n }\n\n return true\n}\n\nexport function matchMutation(\n filters: MutationFilters,\n mutation: Mutation<any, any>,\n): boolean {\n const { exact, status, predicate, mutationKey } = filters\n if (mutationKey) {\n if (!mutation.options.mutationKey) {\n return false\n }\n if (exact) {\n if (hashKey(mutation.options.mutationKey) !== hashKey(mutationKey)) {\n return false\n }\n } else if (!partialMatchKey(mutation.options.mutationKey, mutationKey)) {\n return false\n }\n }\n\n if (status && mutation.state.status !== status) {\n return false\n }\n\n if (predicate && !predicate(mutation)) {\n return false\n }\n\n return true\n}\n\nexport function hashQueryKeyByOptions<TQueryKey extends QueryKey = QueryKey>(\n queryKey: TQueryKey,\n options?: Pick<QueryOptions<any, any, any, any>, 'queryKeyHashFn'>,\n): string {\n const hashFn = options?.queryKeyHashFn || hashKey\n return hashFn(queryKey)\n}\n\n/**\n * Default query & mutation keys hash function.\n * Hashes the value into a stable hash.\n */\nexport function hashKey(queryKey: QueryKey | MutationKey): string {\n return JSON.stringify(queryKey, (_, val) =>\n isPlainObject(val)\n ? Object.keys(val)\n .sort()\n .reduce((result, key) => {\n result[key] = val[key]\n return result\n }, {} as any)\n : val,\n )\n}\n\n/**\n * Checks if key `b` partially matches with key `a`.\n */\nexport function partialMatchKey(a: QueryKey, b: QueryKey): boolean\nexport function partialMatchKey(a: any, b: any): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (a && b && typeof a === 'object' && typeof b === 'object') {\n return Object.keys(b).every((key) => partialMatchKey(a[key], b[key]))\n }\n\n return false\n}\n\nconst hasOwn = Object.prototype.hasOwnProperty\n\n/**\n * This function returns `a` if `b` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between JSON values for example.\n */\nexport function replaceEqualDeep<T>(a: unknown, b: T): T\nexport function replaceEqualDeep(a: any, b: any): any {\n if (a === b) {\n return a\n }\n\n const array = isPlainArray(a) && isPlainArray(b)\n\n if (!array && !(isPlainObject(a) && isPlainObject(b))) return b\n\n const aItems = array ? a : Object.keys(a)\n const aSize = aItems.length\n const bItems = array ? b : Object.keys(b)\n const bSize = bItems.length\n const copy: any = array ? new Array(bSize) : {}\n\n let equalItems = 0\n\n for (let i = 0; i < bSize; i++) {\n const key: any = array ? i : bItems[i]\n const aItem = a[key]\n const bItem = b[key]\n\n if (aItem === bItem) {\n copy[key] = aItem\n if (array ? i < aSize : hasOwn.call(a, key)) equalItems++\n continue\n }\n\n if (\n aItem === null ||\n bItem === null ||\n typeof aItem !== 'object' ||\n typeof bItem !== 'object'\n ) {\n copy[key] = bItem\n continue\n }\n\n const v = replaceEqualDeep(aItem, bItem)\n copy[key] = v\n if (v === aItem) equalItems++\n }\n\n return aSize === bSize && equalItems === aSize ? a : copy\n}\n\n/**\n * Shallow compare objects.\n */\nexport function shallowEqualObjects<T extends Record<string, any>>(\n a: T,\n b: T | undefined,\n): boolean {\n if (!b || Object.keys(a).length !== Object.keys(b).length) {\n return false\n }\n\n for (const key in a) {\n if (a[key] !== b[key]) {\n return false\n }\n }\n\n return true\n}\n\nexport function isPlainArray(value: unknown): value is Array<unknown> {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any): o is Record<PropertyKey, unknown> {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has no constructor\n const ctor = o.constructor\n if (ctor === undefined) {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Handles Objects created by Object.create(<arbitrary prototype>)\n if (Object.getPrototypeOf(o) !== Object.prototype) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any): boolean {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\nexport function sleep(timeout: number): Promise<void> {\n return new Promise((resolve) => {\n timeoutManager.setTimeout(resolve, timeout)\n })\n}\n\nexport function replaceData<\n TData,\n TOptions extends QueryOptions<any, any, any, any>,\n>(prevData: TData | undefined, data: TData, options: TOptions): TData {\n if (typeof options.structuralSharing === 'function') {\n return options.structuralSharing(prevData, data) as TData\n } else if (options.structuralSharing !== false) {\n if (process.env.NODE_ENV !== 'production') {\n try {\n return replaceEqualDeep(prevData, data)\n } catch (error) {\n console.error(\n `Structural sharing requires data to be JSON serializable. To fix this, turn off structuralSharing or return JSON-serializable data from your queryFn. [${options.queryHash}]: ${error}`,\n )\n\n // Prevent the replaceEqualDeep from being called again down below.\n throw error\n }\n }\n // Structurally share data between prev and new data if needed\n return replaceEqualDeep(prevData, data)\n }\n return data\n}\n\nexport function keepPreviousData<T>(\n previousData: T | undefined,\n): T | undefined {\n return previousData\n}\n\nexport function addToEnd<T>(items: Array<T>, item: T, max = 0): Array<T> {\n const newItems = [...items, item]\n return max && newItems.length > max ? newItems.slice(1) : newItems\n}\n\nexport function addToStart<T>(items: Array<T>, item: T, max = 0): Array<T> {\n const newItems = [item, ...items]\n return max && newItems.length > max ? newItems.slice(0, -1) : newItems\n}\n\nexport const skipToken = Symbol()\nexport type SkipToken = typeof skipToken\n\nexport function ensureQueryFn<\n TQueryFnData = unknown,\n TQueryKey extends QueryKey = QueryKey,\n>(\n options: {\n queryFn?: QueryFunction<TQueryFnData, TQueryKey> | SkipToken\n queryHash?: string\n },\n fetchOptions?: FetchOptions<TQueryFnData>,\n): QueryFunction<TQueryFnData, TQueryKey> {\n if (process.env.NODE_ENV !== 'production') {\n if (options.queryFn === skipToken) {\n console.error(\n `Attempted to invoke queryFn when set to skipToken. This is likely a configuration error. Query hash: '${options.queryHash}'`,\n )\n }\n }\n\n // if we attempt to retry a fetch that was triggered from an initialPromise\n // when we don't have a queryFn yet, we can't retry, so we just return the already rejected initialPromise\n // if an observer has already mounted, we will be able to retry with that queryFn\n if (!options.queryFn && fetchOptions?.initialPromise) {\n return () => fetchOptions.initialPromise!\n }\n\n if (!options.queryFn || options.queryFn === skipToken) {\n return () =>\n Promise.reject(new Error(`Missing queryFn: '${options.queryHash}'`))\n }\n\n return options.queryFn\n}\n\nexport function shouldThrowError<T extends (...args: Array<any>) => boolean>(\n throwOnError: boolean | T | undefined,\n params: Parameters<T>,\n): boolean {\n // Allow throwOnError function to override throwing behavior on a per-error basis\n if (typeof throwOnError === 'function') {\n return throwOnError(...params)\n }\n\n return !!throwOnError\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,50 +1,55 @@
|
|
|
1
1
|
/* istanbul ignore file */
|
|
2
2
|
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
export { focusManager } from './focusManager'
|
|
4
|
+
export {
|
|
5
|
+
defaultShouldDehydrateMutation,
|
|
6
|
+
defaultShouldDehydrateQuery,
|
|
7
|
+
dehydrate,
|
|
8
|
+
hydrate,
|
|
9
|
+
} from './hydration'
|
|
9
10
|
export { InfiniteQueryObserver } from './infiniteQueryObserver'
|
|
10
11
|
export { MutationCache } from './mutationCache'
|
|
11
12
|
export type { MutationCacheNotifyEvent } from './mutationCache'
|
|
12
13
|
export { MutationObserver } from './mutationObserver'
|
|
13
|
-
export {
|
|
14
|
-
export { focusManager } from './focusManager'
|
|
14
|
+
export { defaultScheduler, notifyManager } from './notifyManager'
|
|
15
15
|
export { onlineManager } from './onlineManager'
|
|
16
|
+
export { QueriesObserver } from './queriesObserver'
|
|
17
|
+
export { QueryCache } from './queryCache'
|
|
18
|
+
export type { QueryCacheNotifyEvent } from './queryCache'
|
|
19
|
+
export { QueryClient } from './queryClient'
|
|
20
|
+
export { QueryObserver } from './queryObserver'
|
|
21
|
+
export { CancelledError, isCancelledError } from './retryer'
|
|
22
|
+
export {
|
|
23
|
+
timeoutManager,
|
|
24
|
+
type ManagedTimerId,
|
|
25
|
+
type TimeoutCallback,
|
|
26
|
+
type TimeoutProvider,
|
|
27
|
+
} from './timeoutManager'
|
|
16
28
|
export {
|
|
17
29
|
hashKey,
|
|
18
|
-
partialMatchKey,
|
|
19
|
-
replaceEqualDeep,
|
|
20
30
|
isServer,
|
|
21
|
-
matchQuery,
|
|
22
|
-
matchMutation,
|
|
23
31
|
keepPreviousData,
|
|
24
|
-
|
|
32
|
+
matchMutation,
|
|
33
|
+
matchQuery,
|
|
25
34
|
noop,
|
|
35
|
+
partialMatchKey,
|
|
36
|
+
replaceEqualDeep,
|
|
26
37
|
shouldThrowError,
|
|
38
|
+
skipToken,
|
|
27
39
|
} from './utils'
|
|
28
|
-
export type { MutationFilters, QueryFilters,
|
|
29
|
-
export { isCancelledError } from './retryer'
|
|
30
|
-
export {
|
|
31
|
-
dehydrate,
|
|
32
|
-
hydrate,
|
|
33
|
-
defaultShouldDehydrateQuery,
|
|
34
|
-
defaultShouldDehydrateMutation,
|
|
35
|
-
} from './hydration'
|
|
40
|
+
export type { MutationFilters, QueryFilters, SkipToken, Updater } from './utils'
|
|
36
41
|
|
|
37
42
|
export { streamedQuery as experimental_streamedQuery } from './streamedQuery'
|
|
38
43
|
|
|
39
44
|
// Types
|
|
40
|
-
export * from './types'
|
|
41
|
-
export type { QueryState } from './query'
|
|
42
|
-
export { Query } from './query'
|
|
43
|
-
export type { MutationState } from './mutation'
|
|
44
|
-
export { Mutation } from './mutation'
|
|
45
45
|
export type {
|
|
46
|
-
DehydrateOptions,
|
|
47
46
|
DehydratedState,
|
|
47
|
+
DehydrateOptions,
|
|
48
48
|
HydrateOptions,
|
|
49
49
|
} from './hydration'
|
|
50
|
+
export { Mutation } from './mutation'
|
|
51
|
+
export type { MutationState } from './mutation'
|
|
50
52
|
export type { QueriesObserverOptions } from './queriesObserver'
|
|
53
|
+
export { Query } from './query'
|
|
54
|
+
export type { QueryState } from './query'
|
|
55
|
+
export * from './types'
|
package/src/notifyManager.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// TYPES
|
|
2
2
|
|
|
3
|
+
import { systemSetTimeoutZero } from './timeoutManager'
|
|
4
|
+
|
|
3
5
|
type NotifyCallback = () => void
|
|
4
6
|
|
|
5
7
|
type NotifyFunction = (callback: () => void) => void
|
|
@@ -10,7 +12,7 @@ type BatchCallsCallback<T extends Array<unknown>> = (...args: T) => void
|
|
|
10
12
|
|
|
11
13
|
type ScheduleFunction = (callback: () => void) => void
|
|
12
14
|
|
|
13
|
-
export const defaultScheduler: ScheduleFunction =
|
|
15
|
+
export const defaultScheduler: ScheduleFunction = systemSetTimeoutZero
|
|
14
16
|
|
|
15
17
|
export function createNotifyManager() {
|
|
16
18
|
let queue: Array<NotifyCallback> = []
|
package/src/queryObserver.ts
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
shallowEqualObjects,
|
|
14
14
|
timeUntilStale,
|
|
15
15
|
} from './utils'
|
|
16
|
+
import { timeoutManager } from './timeoutManager'
|
|
17
|
+
import type { ManagedTimerId } from './timeoutManager'
|
|
16
18
|
import type { FetchOptions, Query, QueryState } from './query'
|
|
17
19
|
import type { QueryClient } from './queryClient'
|
|
18
20
|
import type { PendingThenable, Thenable } from './thenable'
|
|
@@ -62,8 +64,8 @@ export class QueryObserver<
|
|
|
62
64
|
// This property keeps track of the last query with defined data.
|
|
63
65
|
// It will be used to pass the previous data and query to the placeholder function between renders.
|
|
64
66
|
#lastQueryWithDefinedData?: Query<TQueryFnData, TError, TQueryData, TQueryKey>
|
|
65
|
-
#staleTimeoutId?:
|
|
66
|
-
#refetchIntervalId?:
|
|
67
|
+
#staleTimeoutId?: ManagedTimerId
|
|
68
|
+
#refetchIntervalId?: ManagedTimerId
|
|
67
69
|
#currentRefetchInterval?: number | false
|
|
68
70
|
#trackedProps = new Set<keyof QueryObserverResult>()
|
|
69
71
|
|
|
@@ -365,7 +367,7 @@ export class QueryObserver<
|
|
|
365
367
|
// To mitigate this issue we always add 1 ms to the timeout.
|
|
366
368
|
const timeout = time + 1
|
|
367
369
|
|
|
368
|
-
this.#staleTimeoutId = setTimeout(() => {
|
|
370
|
+
this.#staleTimeoutId = timeoutManager.setTimeout(() => {
|
|
369
371
|
if (!this.#currentResult.isStale) {
|
|
370
372
|
this.updateResult()
|
|
371
373
|
}
|
|
@@ -394,7 +396,7 @@ export class QueryObserver<
|
|
|
394
396
|
return
|
|
395
397
|
}
|
|
396
398
|
|
|
397
|
-
this.#refetchIntervalId = setInterval(() => {
|
|
399
|
+
this.#refetchIntervalId = timeoutManager.setInterval(() => {
|
|
398
400
|
if (
|
|
399
401
|
this.options.refetchIntervalInBackground ||
|
|
400
402
|
focusManager.isFocused()
|
|
@@ -411,14 +413,14 @@ export class QueryObserver<
|
|
|
411
413
|
|
|
412
414
|
#clearStaleTimeout(): void {
|
|
413
415
|
if (this.#staleTimeoutId) {
|
|
414
|
-
clearTimeout(this.#staleTimeoutId)
|
|
416
|
+
timeoutManager.clearTimeout(this.#staleTimeoutId)
|
|
415
417
|
this.#staleTimeoutId = undefined
|
|
416
418
|
}
|
|
417
419
|
}
|
|
418
420
|
|
|
419
421
|
#clearRefetchInterval(): void {
|
|
420
422
|
if (this.#refetchIntervalId) {
|
|
421
|
-
clearInterval(this.#refetchIntervalId)
|
|
423
|
+
timeoutManager.clearInterval(this.#refetchIntervalId)
|
|
422
424
|
this.#refetchIntervalId = undefined
|
|
423
425
|
}
|
|
424
426
|
}
|
package/src/removable.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { timeoutManager } from './timeoutManager'
|
|
1
2
|
import { isServer, isValidTimeout } from './utils'
|
|
3
|
+
import type { ManagedTimerId } from './timeoutManager'
|
|
2
4
|
|
|
3
5
|
export abstract class Removable {
|
|
4
6
|
gcTime!: number
|
|
5
|
-
#gcTimeout?:
|
|
7
|
+
#gcTimeout?: ManagedTimerId
|
|
6
8
|
|
|
7
9
|
destroy(): void {
|
|
8
10
|
this.clearGcTimeout()
|
|
@@ -12,7 +14,7 @@ export abstract class Removable {
|
|
|
12
14
|
this.clearGcTimeout()
|
|
13
15
|
|
|
14
16
|
if (isValidTimeout(this.gcTime)) {
|
|
15
|
-
this.#gcTimeout = setTimeout(() => {
|
|
17
|
+
this.#gcTimeout = timeoutManager.setTimeout(() => {
|
|
16
18
|
this.optionalRemove()
|
|
17
19
|
}, this.gcTime)
|
|
18
20
|
}
|
|
@@ -28,7 +30,7 @@ export abstract class Removable {
|
|
|
28
30
|
|
|
29
31
|
protected clearGcTimeout() {
|
|
30
32
|
if (this.#gcTimeout) {
|
|
31
|
-
clearTimeout(this.#gcTimeout)
|
|
33
|
+
timeoutManager.clearTimeout(this.#gcTimeout)
|
|
32
34
|
this.#gcTimeout = undefined
|
|
33
35
|
}
|
|
34
36
|
}
|
package/src/streamedQuery.ts
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import { addToEnd } from './utils'
|
|
2
2
|
import type { QueryFunction, QueryFunctionContext, QueryKey } from './types'
|
|
3
3
|
|
|
4
|
+
type BaseStreamedQueryParams<TQueryFnData, TQueryKey extends QueryKey> = {
|
|
5
|
+
streamFn: (
|
|
6
|
+
context: QueryFunctionContext<TQueryKey>,
|
|
7
|
+
) => AsyncIterable<TQueryFnData> | Promise<AsyncIterable<TQueryFnData>>
|
|
8
|
+
refetchMode?: 'append' | 'reset' | 'replace'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type SimpleStreamedQueryParams<
|
|
12
|
+
TQueryFnData,
|
|
13
|
+
TQueryKey extends QueryKey,
|
|
14
|
+
> = BaseStreamedQueryParams<TQueryFnData, TQueryKey> & {
|
|
15
|
+
reducer?: never
|
|
16
|
+
initialValue?: never
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type ReducibleStreamedQueryParams<
|
|
20
|
+
TQueryFnData,
|
|
21
|
+
TData,
|
|
22
|
+
TQueryKey extends QueryKey,
|
|
23
|
+
> = BaseStreamedQueryParams<TQueryFnData, TQueryKey> & {
|
|
24
|
+
reducer: (acc: TData, chunk: TQueryFnData) => TData
|
|
25
|
+
initialValue: TData
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type StreamedQueryParams<TQueryFnData, TData, TQueryKey extends QueryKey> =
|
|
29
|
+
| SimpleStreamedQueryParams<TQueryFnData, TQueryKey>
|
|
30
|
+
| ReducibleStreamedQueryParams<TQueryFnData, TData, TQueryKey>
|
|
31
|
+
|
|
4
32
|
/**
|
|
5
33
|
* This is a helper function to create a query function that streams data from an AsyncIterable.
|
|
6
34
|
* Data will be an Array of all the chunks received.
|
|
@@ -11,31 +39,29 @@ import type { QueryFunction, QueryFunctionContext, QueryKey } from './types'
|
|
|
11
39
|
* Defaults to `'reset'`, erases all data and puts the query back into `pending` state.
|
|
12
40
|
* Set to `'append'` to append new data to the existing data.
|
|
13
41
|
* Set to `'replace'` to write all data to the cache once the stream ends.
|
|
14
|
-
* @param
|
|
15
|
-
* Defaults to
|
|
16
|
-
*
|
|
17
|
-
* If the number of chunks exceeds this number, the oldest chunk will be removed.
|
|
42
|
+
* @param reducer - A function to reduce the streamed chunks into the final data.
|
|
43
|
+
* Defaults to a function that appends chunks to the end of the array.
|
|
44
|
+
* @param initialValue - Initial value to be used while the first chunk is being fetched.
|
|
18
45
|
*/
|
|
19
46
|
export function streamedQuery<
|
|
20
47
|
TQueryFnData = unknown,
|
|
48
|
+
TData = Array<TQueryFnData>,
|
|
21
49
|
TQueryKey extends QueryKey = QueryKey,
|
|
22
50
|
>({
|
|
23
|
-
|
|
51
|
+
streamFn,
|
|
24
52
|
refetchMode = 'reset',
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}): QueryFunction<Array<TQueryFnData>, TQueryKey> {
|
|
53
|
+
reducer = (items, chunk) =>
|
|
54
|
+
addToEnd(items as Array<TQueryFnData>, chunk) as TData,
|
|
55
|
+
initialValue = [] as TData,
|
|
56
|
+
}: StreamedQueryParams<TQueryFnData, TData, TQueryKey>): QueryFunction<
|
|
57
|
+
TData,
|
|
58
|
+
TQueryKey
|
|
59
|
+
> {
|
|
33
60
|
return async (context) => {
|
|
34
61
|
const query = context.client
|
|
35
62
|
.getQueryCache()
|
|
36
63
|
.find({ queryKey: context.queryKey, exact: true })
|
|
37
64
|
const isRefetch = !!query && query.state.data !== undefined
|
|
38
|
-
|
|
39
65
|
if (isRefetch && refetchMode === 'reset') {
|
|
40
66
|
query.setState({
|
|
41
67
|
status: 'pending',
|
|
@@ -45,8 +71,9 @@ export function streamedQuery<
|
|
|
45
71
|
})
|
|
46
72
|
}
|
|
47
73
|
|
|
48
|
-
let result
|
|
49
|
-
|
|
74
|
+
let result = initialValue
|
|
75
|
+
|
|
76
|
+
const stream = await streamFn(context)
|
|
50
77
|
|
|
51
78
|
for await (const chunk of stream) {
|
|
52
79
|
if (context.signal.aborted) {
|
|
@@ -55,19 +82,16 @@ export function streamedQuery<
|
|
|
55
82
|
|
|
56
83
|
// don't append to the cache directly when replace-refetching
|
|
57
84
|
if (!isRefetch || refetchMode !== 'replace') {
|
|
58
|
-
context.client.setQueryData<
|
|
59
|
-
|
|
60
|
-
(prev = []) => {
|
|
61
|
-
return addToEnd(prev, chunk, maxChunks)
|
|
62
|
-
},
|
|
85
|
+
context.client.setQueryData<TData>(context.queryKey, (prev) =>
|
|
86
|
+
reducer(prev === undefined ? initialValue : prev, chunk),
|
|
63
87
|
)
|
|
64
88
|
}
|
|
65
|
-
result =
|
|
89
|
+
result = reducer(result, chunk)
|
|
66
90
|
}
|
|
67
91
|
|
|
68
92
|
// finalize result: replace-refetching needs to write to the cache
|
|
69
93
|
if (isRefetch && refetchMode === 'replace' && !context.signal.aborted) {
|
|
70
|
-
context.client.setQueryData<
|
|
94
|
+
context.client.setQueryData<TData>(context.queryKey, result)
|
|
71
95
|
}
|
|
72
96
|
|
|
73
97
|
return context.client.getQueryData(context.queryKey)!
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {@link TimeoutManager} does not support passing arguments to the callback.
|
|
3
|
+
*
|
|
4
|
+
* `(_: void)` is the argument type inferred by TypeScript's default typings for
|
|
5
|
+
* `setTimeout(cb, number)`.
|
|
6
|
+
* If we don't accept a single void argument, then
|
|
7
|
+
* `new Promise(resolve => timeoutManager.setTimeout(resolve, N))` is a type error.
|
|
8
|
+
*/
|
|
9
|
+
export type TimeoutCallback = (_: void) => void
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Wrapping `setTimeout` is awkward from a typing perspective because platform
|
|
13
|
+
* typings may extend the return type of `setTimeout`. For example, NodeJS
|
|
14
|
+
* typings add `NodeJS.Timeout`; but a non-default `timeoutManager` may not be
|
|
15
|
+
* able to return such a type.
|
|
16
|
+
*/
|
|
17
|
+
export type ManagedTimerId = number | { [Symbol.toPrimitive]: () => number }
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Backend for timer functions.
|
|
21
|
+
*/
|
|
22
|
+
export type TimeoutProvider<TTimerId extends ManagedTimerId = ManagedTimerId> =
|
|
23
|
+
{
|
|
24
|
+
readonly setTimeout: (callback: TimeoutCallback, delay: number) => TTimerId
|
|
25
|
+
readonly clearTimeout: (timeoutId: TTimerId | undefined) => void
|
|
26
|
+
|
|
27
|
+
readonly setInterval: (callback: TimeoutCallback, delay: number) => TTimerId
|
|
28
|
+
readonly clearInterval: (intervalId: TTimerId | undefined) => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const defaultTimeoutProvider: TimeoutProvider<
|
|
32
|
+
ReturnType<typeof setTimeout>
|
|
33
|
+
> = {
|
|
34
|
+
// We need the wrapper function syntax below instead of direct references to
|
|
35
|
+
// global setTimeout etc.
|
|
36
|
+
//
|
|
37
|
+
// BAD: `setTimeout: setTimeout`
|
|
38
|
+
// GOOD: `setTimeout: (cb, delay) => setTimeout(cb, delay)`
|
|
39
|
+
//
|
|
40
|
+
// If we use direct references here, then anything that wants to spy on or
|
|
41
|
+
// replace the global setTimeout (like tests) won't work since we'll already
|
|
42
|
+
// have a hard reference to the original implementation at the time when this
|
|
43
|
+
// file was imported.
|
|
44
|
+
setTimeout: (callback, delay) => setTimeout(callback, delay),
|
|
45
|
+
clearTimeout: (timeoutId) => clearTimeout(timeoutId),
|
|
46
|
+
|
|
47
|
+
setInterval: (callback, delay) => setInterval(callback, delay),
|
|
48
|
+
clearInterval: (intervalId) => clearInterval(intervalId),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Allows customization of how timeouts are created.
|
|
53
|
+
*
|
|
54
|
+
* @tanstack/query-core makes liberal use of timeouts to implement `staleTime`
|
|
55
|
+
* and `gcTime`. The default TimeoutManager provider uses the platform's global
|
|
56
|
+
* `setTimeout` implementation, which is known to have scalability issues with
|
|
57
|
+
* thousands of timeouts on the event loop.
|
|
58
|
+
*
|
|
59
|
+
* If you hit this limitation, consider providing a custom TimeoutProvider that
|
|
60
|
+
* coalesces timeouts.
|
|
61
|
+
*/
|
|
62
|
+
export class TimeoutManager implements Omit<TimeoutProvider, 'name'> {
|
|
63
|
+
// We cannot have TimeoutManager<T> as we must instantiate it with a concrete
|
|
64
|
+
// type at app boot; and if we leave that type, then any new timer provider
|
|
65
|
+
// would need to support ReturnType<typeof setTimeout>, which is infeasible.
|
|
66
|
+
//
|
|
67
|
+
// We settle for type safety for the TimeoutProvider type, and accept that
|
|
68
|
+
// this class is unsafe internally to allow for extension.
|
|
69
|
+
#provider: TimeoutProvider<any> = defaultTimeoutProvider
|
|
70
|
+
#providerCalled = false
|
|
71
|
+
|
|
72
|
+
setTimeoutProvider<TTimerId extends ManagedTimerId>(
|
|
73
|
+
provider: TimeoutProvider<TTimerId>,
|
|
74
|
+
): void {
|
|
75
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
76
|
+
if (this.#providerCalled && provider !== this.#provider) {
|
|
77
|
+
// After changing providers, `clearTimeout` will not work as expected for
|
|
78
|
+
// timeouts from the previous provider.
|
|
79
|
+
//
|
|
80
|
+
// Since they may allocate the same timeout ID, clearTimeout may cancel an
|
|
81
|
+
// arbitrary different timeout, or unexpected no-op.
|
|
82
|
+
//
|
|
83
|
+
// We could protect against this by mixing the timeout ID bits
|
|
84
|
+
// deterministically with some per-provider bits.
|
|
85
|
+
//
|
|
86
|
+
// We could internally queue `setTimeout` calls to `TimeoutManager` until
|
|
87
|
+
// some API call to set the initial provider.
|
|
88
|
+
console.error(
|
|
89
|
+
`[timeoutManager]: Switching provider after calls to previous provider might result in unexpected behavior.`,
|
|
90
|
+
{ previous: this.#provider, provider },
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.#provider = provider
|
|
96
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
97
|
+
this.#providerCalled = false
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setTimeout(callback: TimeoutCallback, delay: number): ManagedTimerId {
|
|
102
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
103
|
+
this.#providerCalled = true
|
|
104
|
+
}
|
|
105
|
+
return this.#provider.setTimeout(callback, delay)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
clearTimeout(timeoutId: ManagedTimerId | undefined): void {
|
|
109
|
+
this.#provider.clearTimeout(timeoutId)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setInterval(callback: TimeoutCallback, delay: number): ManagedTimerId {
|
|
113
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
114
|
+
this.#providerCalled = true
|
|
115
|
+
}
|
|
116
|
+
return this.#provider.setInterval(callback, delay)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
clearInterval(intervalId: ManagedTimerId | undefined): void {
|
|
120
|
+
this.#provider.clearInterval(intervalId)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const timeoutManager = new TimeoutManager()
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* In many cases code wants to delay to the next event loop tick; this is not
|
|
128
|
+
* mediated by {@link timeoutManager}.
|
|
129
|
+
*
|
|
130
|
+
* This function is provided to make auditing the `tanstack/query-core` for
|
|
131
|
+
* incorrect use of system `setTimeout` easier.
|
|
132
|
+
*/
|
|
133
|
+
export function systemSetTimeoutZero(callback: TimeoutCallback): void {
|
|
134
|
+
setTimeout(callback, 0)
|
|
135
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { timeoutManager } from './timeoutManager'
|
|
1
2
|
import type {
|
|
2
3
|
DefaultError,
|
|
3
4
|
Enabled,
|
|
@@ -361,7 +362,7 @@ function hasObjectPrototype(o: any): boolean {
|
|
|
361
362
|
|
|
362
363
|
export function sleep(timeout: number): Promise<void> {
|
|
363
364
|
return new Promise((resolve) => {
|
|
364
|
-
setTimeout(resolve, timeout)
|
|
365
|
+
timeoutManager.setTimeout(resolve, timeout)
|
|
365
366
|
})
|
|
366
367
|
}
|
|
367
368
|
|