swr-store
Version:
SWR stores for data-fetching
493 lines (482 loc) • 14.3 kB
JavaScript
;
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);
// src/index.ts
var src_exports = {};
__export(src_exports, {
createSWRStore: () => createSWRStore,
mutate: () => mutate,
subscribe: () => subscribe,
trigger: () => trigger
});
module.exports = __toCommonJS(src_exports);
// src/global.ts
var import_lite = require("dequal/lite");
// src/cache/reactive-cache.ts
function createReactiveCache() {
return {
cache: /* @__PURE__ */ new Map(),
subscribers: /* @__PURE__ */ new Map()
};
}
function createReactiveCacheRef(cache, key, value) {
const currentRef = cache.cache.get(key);
if (currentRef) {
return currentRef;
}
const newRef = {
value
};
cache.cache.set(key, newRef);
return newRef;
}
function subscribeReactiveCache(cache, key, listener) {
let subscribers = cache.subscribers.get(key);
if (!subscribers) {
subscribers = /* @__PURE__ */ new Set();
cache.subscribers.set(key, subscribers);
}
subscribers.add(listener);
return () => {
if (subscribers) {
subscribers.delete(listener);
}
};
}
function setReactiveCacheValue(cache, key, value, notify = true) {
const currentRef = createReactiveCacheRef(cache, key, value);
currentRef.value = value;
if (notify) {
let subscribers = cache.subscribers.get(key);
if (!subscribers) {
subscribers = /* @__PURE__ */ new Set();
cache.subscribers.set(key, subscribers);
}
for (const listener of subscribers.keys()) {
listener(value);
}
}
}
function getReactiveCacheListenerSize(cache, key) {
const result = cache.subscribers.get(key);
if (result) {
return result.size;
}
return 0;
}
// src/cache/mutation-cache.ts
var MUTATION_CACHE = createReactiveCache();
function subscribeMutation(key, listener) {
return subscribeReactiveCache(MUTATION_CACHE, key, listener);
}
function setMutation(key, value) {
setReactiveCacheValue(MUTATION_CACHE, key, value);
}
function getMutation(key) {
const result = MUTATION_CACHE.cache.get(key);
if (result) {
return result.value;
}
return void 0;
}
function getMutationListenerSize(key) {
return getReactiveCacheListenerSize(MUTATION_CACHE, key);
}
// src/cache/revalidation-cache.ts
var REVALIDATION_CACHE = createReactiveCache();
function subscribeRevalidation(key, listener) {
return subscribeReactiveCache(REVALIDATION_CACHE, key, listener);
}
function setRevalidation(key, value, notify = true) {
setReactiveCacheValue(REVALIDATION_CACHE, key, value, notify);
}
// src/global.ts
function trigger(key, shouldRevalidate = true) {
setRevalidation(key, shouldRevalidate);
}
function mutate(key, data, shouldRevalidate = true, compare = import_lite.dequal) {
setRevalidation(key, shouldRevalidate);
const current = getMutation(key);
if (current && current.result.status === "success" && data.status === "success" && compare(current.result.data, data.data)) {
current.timestamp = Date.now();
return;
}
setMutation(key, {
result: data,
timestamp: Date.now(),
isValidating: false
});
}
function subscribe(key, listener) {
const wrappedListener = (value) => {
listener(value);
};
return subscribeMutation(key, wrappedListener);
}
// src/default-config.ts
var import_lite2 = require("dequal/lite");
function defaultKey(...args) {
return JSON.stringify(args);
}
var DEFAULT_CONFIG = {
revalidateOnFocus: false,
revalidateOnNetwork: false,
revalidateOnVisibility: false,
refreshWhenHidden: false,
refreshWhenBlurred: false,
refreshWhenOffline: false,
freshAge: 2e3,
staleAge: 3e4,
key: defaultKey,
compare: import_lite2.dequal,
maxRetryInterval: 5e3
};
var default_config_default = DEFAULT_CONFIG;
// src/is-client.ts
var IS_CLIENT = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
var is_client_default = IS_CLIENT;
// src/retry.ts
function createResolvable() {
let resolve = () => {
};
let reject = () => {
};
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return {
promise,
resolve,
reject
};
}
function retry(supplier, options) {
let alive = true;
let schedule;
const resolvable = createResolvable();
const backoff = (timeout = 10, count = 0) => {
const handle = (reason) => {
if (!alive || typeof options.count === "number" && options.count <= count) {
resolvable.reject(reason);
} else {
schedule = window.setTimeout(() => {
backoff(Math.max(10, Math.min(options.interval, timeout * 2)), count + 1);
}, timeout);
}
};
try {
supplier().then(resolvable.resolve, handle);
} catch (reason) {
handle(reason);
}
};
backoff();
return {
resolvable,
cancel: () => {
if (schedule) {
clearTimeout(schedule);
}
alive = false;
}
};
}
// src/create-swr-store.ts
var index = 0;
function getIndex() {
const current = index;
index += 1;
return current;
}
var retries = /* @__PURE__ */ new Map();
var { assign } = Object;
function revalidate(fullOpts, args, opts) {
const defaultRevalidateOptions = {
shouldRevalidate: true,
initialData: fullOpts.initialData,
hydrate: false
};
const revalidateOptions = assign(
{},
defaultRevalidateOptions,
opts
);
const generatedKey = fullOpts.key(...args);
const timestamp = Date.now();
let currentMutation = getMutation(generatedKey);
if (!currentMutation && revalidateOptions.initialData) {
currentMutation = {
result: {
data: revalidateOptions.initialData,
status: "success"
},
timestamp,
isValidating: false
};
if (revalidateOptions.hydrate) {
setMutation(generatedKey, currentMutation);
}
}
if (currentMutation) {
if (!revalidateOptions.shouldRevalidate) {
return currentMutation.result;
}
if (currentMutation.timestamp + fullOpts.freshAge > timestamp) {
return currentMutation.result;
}
if (currentMutation.result.status === "pending") {
const previousRetry = retries.get(generatedKey);
if (previousRetry) {
previousRetry.cancel();
}
}
}
const pendingRetry = retry(() => fullOpts.get(...args), {
count: fullOpts.maxRetryCount,
interval: fullOpts.maxRetryInterval
});
retries.set(generatedKey, pendingRetry);
const pendingData = pendingRetry.resolvable.promise;
const result = {
data: pendingData,
status: "pending"
};
pendingData.then(
(data) => {
const mutation = getMutation(generatedKey);
const shouldUpdate = () => {
if (mutation == null) {
return true;
}
if (mutation.timestamp > timestamp) {
return false;
}
if (mutation.result.status === "success") {
return !fullOpts.compare(
mutation.result.data,
data
);
}
return true;
};
if (shouldUpdate()) {
setMutation(generatedKey, {
result: {
data,
status: "success"
},
timestamp: mutation && mutation.timestamp ? mutation.timestamp : Date.now(),
isValidating: false
});
}
},
(data) => {
const mutation = getMutation(generatedKey);
const shouldUpdate = () => {
if (mutation == null) {
return true;
}
if (mutation.timestamp > timestamp) {
return false;
}
return true;
};
if (shouldUpdate()) {
setMutation(generatedKey, {
result: {
data,
status: "failure"
},
timestamp: mutation && mutation.timestamp ? mutation.timestamp : Date.now(),
isValidating: false
});
}
}
);
if (currentMutation && currentMutation.timestamp + fullOpts.freshAge + fullOpts.staleAge > timestamp) {
currentMutation.timestamp = timestamp;
currentMutation.isValidating = true;
return currentMutation.result;
}
setMutation(generatedKey, {
result,
timestamp,
isValidating: true
});
return result;
}
function lazyRegister(cleanups, generatedKey, fullOpts, args) {
if (getMutationListenerSize(generatedKey) > 0) {
return;
}
const currentCleanups = [];
const subscription = (sub) => {
currentCleanups.push(sub());
};
const onRevalidate = () => {
setRevalidation(generatedKey, true);
};
subscription(() => {
const innerRevalidate = (flag) => {
revalidate(fullOpts, args, {
shouldRevalidate: flag
});
};
return subscribeRevalidation(generatedKey, innerRevalidate);
});
if (is_client_default) {
if (fullOpts.refreshInterval != null) {
if (fullOpts.refreshWhenBlurred) {
subscription(() => {
let interval;
const enter = () => {
window.clearInterval(interval);
interval = window.setInterval(onRevalidate, fullOpts.refreshInterval);
};
const exit = () => {
window.clearInterval(interval);
interval = void 0;
};
window.addEventListener("blur", enter, false);
window.addEventListener("focus", exit, false);
return () => {
window.removeEventListener("blur", enter, false);
window.removeEventListener("focus", exit, false);
window.clearInterval(interval);
};
});
}
if (fullOpts.refreshWhenOffline) {
subscription(() => {
let interval;
const enter = () => {
window.clearInterval(interval);
interval = window.setInterval(onRevalidate, fullOpts.refreshInterval);
};
const exit = () => {
window.clearInterval(interval);
interval = void 0;
};
window.addEventListener("offline", enter, false);
window.addEventListener("online", exit, false);
return () => {
window.removeEventListener("offline", enter, false);
window.removeEventListener("online", exit, false);
window.clearInterval(interval);
};
});
}
if (fullOpts.refreshWhenHidden) {
subscription(() => {
let interval;
const onVisibility = () => {
window.clearInterval(interval);
if (document.visibilityState === "visible") {
interval = void 0;
} else {
interval = window.setInterval(onRevalidate, fullOpts.refreshInterval);
}
};
document.addEventListener("visibilitychange", onVisibility, false);
return () => {
document.removeEventListener("visibilitychange", onVisibility, false);
window.clearInterval(interval);
};
});
}
if (!(fullOpts.refreshWhenHidden || fullOpts.refreshWhenBlurred || fullOpts.refreshWhenOffline)) {
subscription(() => {
const interval = window.setInterval(onRevalidate, fullOpts.refreshInterval);
return () => {
window.clearInterval(interval);
};
});
}
}
if (fullOpts.revalidateOnFocus) {
subscription(() => {
window.addEventListener("focus", onRevalidate, false);
return () => {
window.removeEventListener("focus", onRevalidate, false);
};
});
}
if (fullOpts.revalidateOnNetwork) {
subscription(() => {
window.addEventListener("online", onRevalidate, false);
return () => {
window.removeEventListener("online", onRevalidate, false);
};
});
}
if (fullOpts.revalidateOnVisibility) {
subscription(() => {
const onVisible = () => {
if (document.visibilityState === "visible") {
onRevalidate();
}
};
window.addEventListener("visibilitychange", onVisible, false);
return () => {
window.removeEventListener("visibilitychange", onVisible, false);
};
});
}
}
cleanups.set(generatedKey, currentCleanups);
}
function lazyUnregister(cleanups, generatedKey) {
if (getMutationListenerSize(generatedKey) === 0) {
const actualCleanups = cleanups.get(generatedKey);
if (actualCleanups) {
for (let i = 0, len = actualCleanups.length; i < len; i += 1) {
actualCleanups[i]();
}
cleanups.delete(generatedKey);
}
}
}
function createSWRStore(options) {
const fullOpts = assign({}, default_config_default, options);
const cleanups = /* @__PURE__ */ new Map();
return {
id: `SWRStore-${getIndex()}`,
trigger: (args, shouldRevalidate = true) => {
const generatedKey = fullOpts.key(...args);
trigger(generatedKey, shouldRevalidate);
},
mutate: (args, data, shouldRevalidate = true, compare = fullOpts.compare) => {
const generatedKey = fullOpts.key(...args);
mutate(generatedKey, data, shouldRevalidate, compare);
},
// This function revalidates the mutation cache
// through reactive process
get: (args, opts) => revalidate(fullOpts, args, opts),
subscribe: (args, listener) => {
const generatedKey = fullOpts.key(...args);
lazyRegister(cleanups, generatedKey, fullOpts, args);
const unsubscribe = subscribe(generatedKey, listener);
return () => {
unsubscribe();
lazyUnregister(cleanups, generatedKey);
};
}
};
}
//# sourceMappingURL=index.cjs.map