624 lines
23 KiB
JavaScript
624 lines
23 KiB
JavaScript
'use client';
|
|
var React = require('react');
|
|
var revalidateEvents = require('./events.js');
|
|
var lite = require('dequal/lite');
|
|
|
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
|
function _interopNamespace(e) {
|
|
if (e && e.__esModule) return e;
|
|
var n = Object.create(null);
|
|
if (e) {
|
|
Object.keys(e).forEach(function (k) {
|
|
if (k !== 'default') {
|
|
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
Object.defineProperty(n, k, d.get ? d : {
|
|
enumerable: true,
|
|
get: function () { return e[k]; }
|
|
});
|
|
}
|
|
});
|
|
}
|
|
n.default = e;
|
|
return n;
|
|
}
|
|
|
|
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
var revalidateEvents__namespace = /*#__PURE__*/_interopNamespace(revalidateEvents);
|
|
|
|
// Global state used to deduplicate requests and store listeners
|
|
const SWRGlobalState = new WeakMap();
|
|
|
|
// Shared state between server components and client components
|
|
const noop = ()=>{};
|
|
// Using noop() as the undefined value as undefined can be replaced
|
|
// by something else. Prettier ignore and extra parentheses are necessary here
|
|
// to ensure that tsc doesn't remove the __NOINLINE__ comment.
|
|
// prettier-ignore
|
|
const UNDEFINED = /*#__NOINLINE__*/ noop();
|
|
const OBJECT = Object;
|
|
const isUndefined = (v)=>v === UNDEFINED;
|
|
const isFunction = (v)=>typeof v == 'function';
|
|
const mergeObjects = (a, b)=>({
|
|
...a,
|
|
...b
|
|
});
|
|
const isPromiseLike = (x)=>isFunction(x.then);
|
|
|
|
const EMPTY_CACHE = {};
|
|
const INITIAL_CACHE = {};
|
|
const STR_UNDEFINED = 'undefined';
|
|
// NOTE: Use the function to guarantee it's re-evaluated between jsdom and node runtime for tests.
|
|
const isWindowDefined = typeof window != STR_UNDEFINED;
|
|
const isDocumentDefined = typeof document != STR_UNDEFINED;
|
|
const isLegacyDeno = isWindowDefined && 'Deno' in window;
|
|
const hasRequestAnimationFrame = ()=>isWindowDefined && typeof window['requestAnimationFrame'] != STR_UNDEFINED;
|
|
const createCacheHelper = (cache, key)=>{
|
|
const state = SWRGlobalState.get(cache);
|
|
return [
|
|
// Getter
|
|
()=>!isUndefined(key) && cache.get(key) || EMPTY_CACHE,
|
|
// Setter
|
|
(info)=>{
|
|
if (!isUndefined(key)) {
|
|
const prev = cache.get(key);
|
|
// Before writing to the store, we keep the value in the initial cache
|
|
// if it's not there yet.
|
|
if (!(key in INITIAL_CACHE)) {
|
|
INITIAL_CACHE[key] = prev;
|
|
}
|
|
state[5](key, mergeObjects(prev, info), prev || EMPTY_CACHE);
|
|
}
|
|
},
|
|
// Subscriber
|
|
state[6],
|
|
// Get server cache snapshot
|
|
()=>{
|
|
if (!isUndefined(key)) {
|
|
// If the cache was updated on the client, we return the stored initial value.
|
|
if (key in INITIAL_CACHE) return INITIAL_CACHE[key];
|
|
}
|
|
// If we haven't done any client-side updates, we return the current value.
|
|
return !isUndefined(key) && cache.get(key) || EMPTY_CACHE;
|
|
}
|
|
];
|
|
} // export { UNDEFINED, OBJECT, isUndefined, isFunction, mergeObjects, isPromiseLike }
|
|
;
|
|
|
|
/**
|
|
* Due to the bug https://bugs.chromium.org/p/chromium/issues/detail?id=678075,
|
|
* it's not reliable to detect if the browser is currently online or offline
|
|
* based on `navigator.onLine`.
|
|
* As a workaround, we always assume it's online on the first load, and change
|
|
* the status upon `online` or `offline` events.
|
|
*/ let online = true;
|
|
const isOnline = ()=>online;
|
|
// For node and React Native, `add/removeEventListener` doesn't exist on window.
|
|
const [onWindowEvent, offWindowEvent] = isWindowDefined && window.addEventListener ? [
|
|
window.addEventListener.bind(window),
|
|
window.removeEventListener.bind(window)
|
|
] : [
|
|
noop,
|
|
noop
|
|
];
|
|
const isVisible = ()=>{
|
|
const visibilityState = isDocumentDefined && document.visibilityState;
|
|
return isUndefined(visibilityState) || visibilityState !== 'hidden';
|
|
};
|
|
const initFocus = (callback)=>{
|
|
// focus revalidate
|
|
if (isDocumentDefined) {
|
|
document.addEventListener('visibilitychange', callback);
|
|
}
|
|
onWindowEvent('focus', callback);
|
|
return ()=>{
|
|
if (isDocumentDefined) {
|
|
document.removeEventListener('visibilitychange', callback);
|
|
}
|
|
offWindowEvent('focus', callback);
|
|
};
|
|
};
|
|
const initReconnect = (callback)=>{
|
|
// revalidate on reconnected
|
|
const onOnline = ()=>{
|
|
online = true;
|
|
callback();
|
|
};
|
|
// nothing to revalidate, just update the status
|
|
const onOffline = ()=>{
|
|
online = false;
|
|
};
|
|
onWindowEvent('online', onOnline);
|
|
onWindowEvent('offline', onOffline);
|
|
return ()=>{
|
|
offWindowEvent('online', onOnline);
|
|
offWindowEvent('offline', onOffline);
|
|
};
|
|
};
|
|
const preset = {
|
|
isOnline,
|
|
isVisible
|
|
};
|
|
const defaultConfigOptions = {
|
|
initFocus,
|
|
initReconnect
|
|
};
|
|
|
|
const IS_REACT_LEGACY = !React__default.default.useId;
|
|
const IS_SERVER = !isWindowDefined || isLegacyDeno;
|
|
// Polyfill requestAnimationFrame
|
|
const rAF = (f)=>hasRequestAnimationFrame() ? window['requestAnimationFrame'](f) : setTimeout(f, 1);
|
|
// React currently throws a warning when using useLayoutEffect on the server.
|
|
// To get around it, we can conditionally useEffect on the server (no-op) and
|
|
// useLayoutEffect in the browser.
|
|
const useIsomorphicLayoutEffect = IS_SERVER ? React.useEffect : React.useLayoutEffect;
|
|
// This assignment is to extend the Navigator type to use effectiveType.
|
|
const navigatorConnection = typeof navigator !== 'undefined' && navigator.connection;
|
|
// Adjust the config based on slow connection status (<= 70Kbps).
|
|
const slowConnection = !IS_SERVER && navigatorConnection && ([
|
|
'slow-2g',
|
|
'2g'
|
|
].includes(navigatorConnection.effectiveType) || navigatorConnection.saveData);
|
|
|
|
// use WeakMap to store the object->key mapping
|
|
// so the objects can be garbage collected.
|
|
// WeakMap uses a hashtable under the hood, so the lookup
|
|
// complexity is almost O(1).
|
|
const table = new WeakMap();
|
|
const getTypeName = (value)=>OBJECT.prototype.toString.call(value);
|
|
const isObjectTypeName = (typeName, type)=>typeName === `[object ${type}]`;
|
|
// counter of the key
|
|
let counter = 0;
|
|
// A stable hash implementation that supports:
|
|
// - Fast and ensures unique hash properties
|
|
// - Handles unserializable values
|
|
// - Handles object key ordering
|
|
// - Generates short results
|
|
//
|
|
// This is not a serialization function, and the result is not guaranteed to be
|
|
// parsable.
|
|
const stableHash = (arg)=>{
|
|
const type = typeof arg;
|
|
const typeName = getTypeName(arg);
|
|
const isDate = isObjectTypeName(typeName, 'Date');
|
|
const isRegex = isObjectTypeName(typeName, 'RegExp');
|
|
const isPlainObject = isObjectTypeName(typeName, 'Object');
|
|
let result;
|
|
let index;
|
|
if (OBJECT(arg) === arg && !isDate && !isRegex) {
|
|
// Object/function, not null/date/regexp. Use WeakMap to store the id first.
|
|
// If it's already hashed, directly return the result.
|
|
result = table.get(arg);
|
|
if (result) return result;
|
|
// Store the hash first for circular reference detection before entering the
|
|
// recursive `stableHash` calls.
|
|
// For other objects like set and map, we use this id directly as the hash.
|
|
result = ++counter + '~';
|
|
table.set(arg, result);
|
|
if (Array.isArray(arg)) {
|
|
// Array.
|
|
result = '@';
|
|
for(index = 0; index < arg.length; index++){
|
|
result += stableHash(arg[index]) + ',';
|
|
}
|
|
table.set(arg, result);
|
|
}
|
|
if (isPlainObject) {
|
|
// Object, sort keys.
|
|
result = '#';
|
|
const keys = OBJECT.keys(arg).sort();
|
|
while(!isUndefined(index = keys.pop())){
|
|
if (!isUndefined(arg[index])) {
|
|
result += index + ':' + stableHash(arg[index]) + ',';
|
|
}
|
|
}
|
|
table.set(arg, result);
|
|
}
|
|
} else {
|
|
result = isDate ? arg.toJSON() : type == 'symbol' ? arg.toString() : type == 'string' ? JSON.stringify(arg) : '' + arg;
|
|
}
|
|
return result;
|
|
};
|
|
|
|
const serialize = (key)=>{
|
|
if (isFunction(key)) {
|
|
try {
|
|
key = key();
|
|
} catch (err) {
|
|
// dependencies not ready
|
|
key = '';
|
|
}
|
|
}
|
|
// Use the original key as the argument of fetcher. This can be a string or an
|
|
// array of values.
|
|
const args = key;
|
|
// If key is not falsy, or not an empty array, hash it.
|
|
key = typeof key == 'string' ? key : (Array.isArray(key) ? key.length : key) ? stableHash(key) : '';
|
|
return [
|
|
key,
|
|
args
|
|
];
|
|
};
|
|
|
|
// Global timestamp.
|
|
let __timestamp = 0;
|
|
const getTimestamp = ()=>++__timestamp;
|
|
|
|
async function internalMutate(...args) {
|
|
const [cache, _key, _data, _opts] = args;
|
|
// When passing as a boolean, it's explicitly used to disable/enable
|
|
// revalidation.
|
|
const options = mergeObjects({
|
|
populateCache: true,
|
|
throwOnError: true
|
|
}, typeof _opts === 'boolean' ? {
|
|
revalidate: _opts
|
|
} : _opts || {});
|
|
let populateCache = options.populateCache;
|
|
const rollbackOnErrorOption = options.rollbackOnError;
|
|
let optimisticData = options.optimisticData;
|
|
const rollbackOnError = (error)=>{
|
|
return typeof rollbackOnErrorOption === 'function' ? rollbackOnErrorOption(error) : rollbackOnErrorOption !== false;
|
|
};
|
|
const throwOnError = options.throwOnError;
|
|
// If the second argument is a key filter, return the mutation results for all
|
|
// filtered keys.
|
|
if (isFunction(_key)) {
|
|
const keyFilter = _key;
|
|
const matchedKeys = [];
|
|
const it = cache.keys();
|
|
for (const key of it){
|
|
if (// Skip the special useSWRInfinite and useSWRSubscription keys.
|
|
!/^\$(inf|sub)\$/.test(key) && keyFilter(cache.get(key)._k)) {
|
|
matchedKeys.push(key);
|
|
}
|
|
}
|
|
return Promise.all(matchedKeys.map(mutateByKey));
|
|
}
|
|
return mutateByKey(_key);
|
|
async function mutateByKey(_k) {
|
|
// Serialize key
|
|
const [key] = serialize(_k);
|
|
if (!key) return;
|
|
const [get, set] = createCacheHelper(cache, key);
|
|
const [EVENT_REVALIDATORS, MUTATION, FETCH, PRELOAD] = SWRGlobalState.get(cache);
|
|
const startRevalidate = ()=>{
|
|
const revalidators = EVENT_REVALIDATORS[key];
|
|
const revalidate = isFunction(options.revalidate) ? options.revalidate(get().data, _k) : options.revalidate !== false;
|
|
if (revalidate) {
|
|
// Invalidate the key by deleting the concurrent request markers so new
|
|
// requests will not be deduped.
|
|
delete FETCH[key];
|
|
delete PRELOAD[key];
|
|
if (revalidators && revalidators[0]) {
|
|
return revalidators[0](revalidateEvents__namespace.MUTATE_EVENT).then(()=>get().data);
|
|
}
|
|
}
|
|
return get().data;
|
|
};
|
|
// If there is no new data provided, revalidate the key with current state.
|
|
if (args.length < 3) {
|
|
// Revalidate and broadcast state.
|
|
return startRevalidate();
|
|
}
|
|
let data = _data;
|
|
let error;
|
|
let isError = false;
|
|
// Update global timestamps.
|
|
const beforeMutationTs = getTimestamp();
|
|
MUTATION[key] = [
|
|
beforeMutationTs,
|
|
0
|
|
];
|
|
const hasOptimisticData = !isUndefined(optimisticData);
|
|
const state = get();
|
|
// `displayedData` is the current value on screen. It could be the optimistic value
|
|
// that is going to be overridden by a `committedData`, or get reverted back.
|
|
// `committedData` is the validated value that comes from a fetch or mutation.
|
|
const displayedData = state.data;
|
|
const currentData = state._c;
|
|
const committedData = isUndefined(currentData) ? displayedData : currentData;
|
|
// Do optimistic data update.
|
|
if (hasOptimisticData) {
|
|
optimisticData = isFunction(optimisticData) ? optimisticData(committedData, displayedData) : optimisticData;
|
|
// When we set optimistic data, backup the current committedData data in `_c`.
|
|
set({
|
|
data: optimisticData,
|
|
_c: committedData
|
|
});
|
|
}
|
|
if (isFunction(data)) {
|
|
// `data` is a function, call it passing current cache value.
|
|
try {
|
|
data = data(committedData);
|
|
} catch (err) {
|
|
// If it throws an error synchronously, we shouldn't update the cache.
|
|
error = err;
|
|
isError = true;
|
|
}
|
|
}
|
|
// `data` is a promise/thenable, resolve the final data first.
|
|
if (data && isPromiseLike(data)) {
|
|
// This means that the mutation is async, we need to check timestamps to
|
|
// avoid race conditions.
|
|
data = await data.catch((err)=>{
|
|
error = err;
|
|
isError = true;
|
|
});
|
|
// Check if other mutations have occurred since we've started this mutation.
|
|
// If there's a race we don't update cache or broadcast the change,
|
|
// just return the data.
|
|
if (beforeMutationTs !== MUTATION[key][0]) {
|
|
if (isError) throw error;
|
|
return data;
|
|
} else if (isError && hasOptimisticData && rollbackOnError(error)) {
|
|
// Rollback. Always populate the cache in this case but without
|
|
// transforming the data.
|
|
populateCache = true;
|
|
// Reset data to be the latest committed data, and clear the `_c` value.
|
|
set({
|
|
data: committedData,
|
|
_c: UNDEFINED
|
|
});
|
|
}
|
|
}
|
|
// If we should write back the cache after request.
|
|
if (populateCache) {
|
|
if (!isError) {
|
|
// Transform the result into data.
|
|
if (isFunction(populateCache)) {
|
|
const populateCachedData = populateCache(data, committedData);
|
|
set({
|
|
data: populateCachedData,
|
|
error: UNDEFINED,
|
|
_c: UNDEFINED
|
|
});
|
|
} else {
|
|
// Only update cached data and reset the error if there's no error. Data can be `undefined` here.
|
|
set({
|
|
data,
|
|
error: UNDEFINED,
|
|
_c: UNDEFINED
|
|
});
|
|
}
|
|
}
|
|
}
|
|
// Reset the timestamp to mark the mutation has ended.
|
|
MUTATION[key][1] = getTimestamp();
|
|
// Update existing SWR Hooks' internal states:
|
|
Promise.resolve(startRevalidate()).then(()=>{
|
|
// The mutation and revalidation are ended, we can clear it since the data is
|
|
// not an optimistic value anymore.
|
|
set({
|
|
_c: UNDEFINED
|
|
});
|
|
});
|
|
// Throw error or return data
|
|
if (isError) {
|
|
if (throwOnError) throw error;
|
|
return;
|
|
}
|
|
return data;
|
|
}
|
|
}
|
|
|
|
const revalidateAllKeys = (revalidators, type)=>{
|
|
for(const key in revalidators){
|
|
if (revalidators[key][0]) revalidators[key][0](type);
|
|
}
|
|
};
|
|
const initCache = (provider, options)=>{
|
|
// The global state for a specific provider will be used to deduplicate
|
|
// requests and store listeners. As well as a mutate function that is bound to
|
|
// the cache.
|
|
// The provider's global state might be already initialized. Let's try to get the
|
|
// global state associated with the provider first.
|
|
if (!SWRGlobalState.has(provider)) {
|
|
const opts = mergeObjects(defaultConfigOptions, options);
|
|
// If there's no global state bound to the provider, create a new one with the
|
|
// new mutate function.
|
|
const EVENT_REVALIDATORS = Object.create(null);
|
|
const mutate = internalMutate.bind(UNDEFINED, provider);
|
|
let unmount = noop;
|
|
const subscriptions = Object.create(null);
|
|
const subscribe = (key, callback)=>{
|
|
const subs = subscriptions[key] || [];
|
|
subscriptions[key] = subs;
|
|
subs.push(callback);
|
|
return ()=>subs.splice(subs.indexOf(callback), 1);
|
|
};
|
|
const setter = (key, value, prev)=>{
|
|
provider.set(key, value);
|
|
const subs = subscriptions[key];
|
|
if (subs) {
|
|
for (const fn of subs){
|
|
fn(value, prev);
|
|
}
|
|
}
|
|
};
|
|
const initProvider = ()=>{
|
|
if (!SWRGlobalState.has(provider)) {
|
|
// Update the state if it's new, or if the provider has been extended.
|
|
SWRGlobalState.set(provider, [
|
|
EVENT_REVALIDATORS,
|
|
Object.create(null),
|
|
Object.create(null),
|
|
Object.create(null),
|
|
mutate,
|
|
setter,
|
|
subscribe
|
|
]);
|
|
if (!IS_SERVER) {
|
|
// When listening to the native events for auto revalidations,
|
|
// we intentionally put a delay (setTimeout) here to make sure they are
|
|
// fired after immediate JavaScript executions, which can be
|
|
// React's state updates.
|
|
// This avoids some unnecessary revalidations such as
|
|
// https://github.com/vercel/swr/issues/1680.
|
|
const releaseFocus = opts.initFocus(setTimeout.bind(UNDEFINED, revalidateAllKeys.bind(UNDEFINED, EVENT_REVALIDATORS, revalidateEvents__namespace.FOCUS_EVENT)));
|
|
const releaseReconnect = opts.initReconnect(setTimeout.bind(UNDEFINED, revalidateAllKeys.bind(UNDEFINED, EVENT_REVALIDATORS, revalidateEvents__namespace.RECONNECT_EVENT)));
|
|
unmount = ()=>{
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
releaseFocus && releaseFocus();
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
releaseReconnect && releaseReconnect();
|
|
// When un-mounting, we need to remove the cache provider from the state
|
|
// storage too because it's a side-effect. Otherwise, when re-mounting we
|
|
// will not re-register those event listeners.
|
|
SWRGlobalState.delete(provider);
|
|
};
|
|
}
|
|
}
|
|
};
|
|
initProvider();
|
|
// This is a new provider, we need to initialize it and setup DOM events
|
|
// listeners for `focus` and `reconnect` actions.
|
|
// We might want to inject an extra layer on top of `provider` in the future,
|
|
// such as key serialization, auto GC, etc.
|
|
// For now, it's just a `Map` interface without any modifications.
|
|
return [
|
|
provider,
|
|
mutate,
|
|
initProvider,
|
|
unmount
|
|
];
|
|
}
|
|
return [
|
|
provider,
|
|
SWRGlobalState.get(provider)[4]
|
|
];
|
|
};
|
|
|
|
// error retry
|
|
const onErrorRetry = (_, __, config, revalidate, opts)=>{
|
|
const maxRetryCount = config.errorRetryCount;
|
|
const currentRetryCount = opts.retryCount;
|
|
// Exponential backoff
|
|
const timeout = ~~((Math.random() + 0.5) * (1 << (currentRetryCount < 8 ? currentRetryCount : 8))) * config.errorRetryInterval;
|
|
if (!isUndefined(maxRetryCount) && currentRetryCount > maxRetryCount) {
|
|
return;
|
|
}
|
|
setTimeout(revalidate, timeout, opts);
|
|
};
|
|
const compare = lite.dequal;
|
|
// Default cache provider
|
|
const [cache, mutate] = initCache(new Map());
|
|
// Default config
|
|
const defaultConfig = mergeObjects({
|
|
// events
|
|
onLoadingSlow: noop,
|
|
onSuccess: noop,
|
|
onError: noop,
|
|
onErrorRetry,
|
|
onDiscarded: noop,
|
|
// switches
|
|
revalidateOnFocus: true,
|
|
revalidateOnReconnect: true,
|
|
revalidateIfStale: true,
|
|
shouldRetryOnError: true,
|
|
// timeouts
|
|
errorRetryInterval: slowConnection ? 10000 : 5000,
|
|
focusThrottleInterval: 5 * 1000,
|
|
dedupingInterval: 2 * 1000,
|
|
loadingTimeout: slowConnection ? 5000 : 3000,
|
|
// providers
|
|
compare,
|
|
isPaused: ()=>false,
|
|
cache,
|
|
mutate,
|
|
fallback: {}
|
|
}, // use web preset by default
|
|
preset);
|
|
|
|
const mergeConfigs = (a, b)=>{
|
|
// Need to create a new object to avoid mutating the original here.
|
|
const v = mergeObjects(a, b);
|
|
// If two configs are provided, merge their `use` and `fallback` options.
|
|
if (b) {
|
|
const { use: u1, fallback: f1 } = a;
|
|
const { use: u2, fallback: f2 } = b;
|
|
if (u1 && u2) {
|
|
v.use = u1.concat(u2);
|
|
}
|
|
if (f1 && f2) {
|
|
v.fallback = mergeObjects(f1, f2);
|
|
}
|
|
}
|
|
return v;
|
|
};
|
|
|
|
const SWRConfigContext = React.createContext({});
|
|
const SWRConfig = (props)=>{
|
|
const { value } = props;
|
|
const parentConfig = React.useContext(SWRConfigContext);
|
|
const isFunctionalConfig = isFunction(value);
|
|
const config = React.useMemo(()=>isFunctionalConfig ? value(parentConfig) : value, [
|
|
isFunctionalConfig,
|
|
parentConfig,
|
|
value
|
|
]);
|
|
// Extend parent context values and middleware.
|
|
const extendedConfig = React.useMemo(()=>isFunctionalConfig ? config : mergeConfigs(parentConfig, config), [
|
|
isFunctionalConfig,
|
|
parentConfig,
|
|
config
|
|
]);
|
|
// Should not use the inherited provider.
|
|
const provider = config && config.provider;
|
|
// initialize the cache only on first access.
|
|
const cacheContextRef = React.useRef(UNDEFINED);
|
|
if (provider && !cacheContextRef.current) {
|
|
cacheContextRef.current = initCache(provider(extendedConfig.cache || cache), config);
|
|
}
|
|
const cacheContext = cacheContextRef.current;
|
|
// Override the cache if a new provider is given.
|
|
if (cacheContext) {
|
|
extendedConfig.cache = cacheContext[0];
|
|
extendedConfig.mutate = cacheContext[1];
|
|
}
|
|
// Unsubscribe events.
|
|
useIsomorphicLayoutEffect(()=>{
|
|
if (cacheContext) {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
cacheContext[2] && cacheContext[2]();
|
|
return cacheContext[3];
|
|
}
|
|
}, []);
|
|
return React.createElement(SWRConfigContext.Provider, mergeObjects(props, {
|
|
value: extendedConfig
|
|
}));
|
|
};
|
|
|
|
exports.IS_REACT_LEGACY = IS_REACT_LEGACY;
|
|
exports.IS_SERVER = IS_SERVER;
|
|
exports.OBJECT = OBJECT;
|
|
exports.SWRConfig = SWRConfig;
|
|
exports.SWRConfigContext = SWRConfigContext;
|
|
exports.SWRGlobalState = SWRGlobalState;
|
|
exports.UNDEFINED = UNDEFINED;
|
|
exports.cache = cache;
|
|
exports.compare = compare;
|
|
exports.createCacheHelper = createCacheHelper;
|
|
exports.defaultConfig = defaultConfig;
|
|
exports.defaultConfigOptions = defaultConfigOptions;
|
|
exports.getTimestamp = getTimestamp;
|
|
exports.hasRequestAnimationFrame = hasRequestAnimationFrame;
|
|
exports.initCache = initCache;
|
|
exports.internalMutate = internalMutate;
|
|
exports.isDocumentDefined = isDocumentDefined;
|
|
exports.isFunction = isFunction;
|
|
exports.isLegacyDeno = isLegacyDeno;
|
|
exports.isPromiseLike = isPromiseLike;
|
|
exports.isUndefined = isUndefined;
|
|
exports.isWindowDefined = isWindowDefined;
|
|
exports.mergeConfigs = mergeConfigs;
|
|
exports.mergeObjects = mergeObjects;
|
|
exports.mutate = mutate;
|
|
exports.noop = noop;
|
|
exports.preset = preset;
|
|
exports.rAF = rAF;
|
|
exports.serialize = serialize;
|
|
exports.slowConnection = slowConnection;
|
|
exports.stableHash = stableHash;
|
|
exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
|