@modern-js/runtime-utils
Version:
A Progressive React Framework for modern web development.
382 lines (381 loc) • 14.8 kB
JavaScript
;
var __webpack_require__ = {};
(()=>{
__webpack_require__.d = (exports1, definition)=>{
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
enumerable: true,
get: definition[key]
});
};
})();
(()=>{
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
})();
(()=>{
__webpack_require__.r = (exports1)=>{
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
value: 'Module'
});
Object.defineProperty(exports1, '__esModule', {
value: true
});
};
})();
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
cache: ()=>cache,
revalidateTag: ()=>revalidateTag,
CacheSize: ()=>CacheSize,
clearStore: ()=>clearStore,
configureCache: ()=>configureCache,
withRequestCache: ()=>withRequestCache,
generateKey: ()=>generateKey,
CacheTime: ()=>CacheTime
});
const external_lru_cache_namespaceObject = require("lru-cache");
const external_async_storage_js_namespaceObject = require("./async_storage.js");
const CacheSize = {
KB: 1024,
MB: 1048576,
GB: 1073741824
};
const CacheTime = {
SECOND: 1000,
MINUTE: 60000,
HOUR: 3600000,
DAY: 86400000,
WEEK: 604800000,
MONTH: 2592000000
};
function estimateObjectSize(data) {
const type = typeof data;
if ('number' === type) return 8;
if ('boolean' === type) return 4;
if ('string' === type) return Math.max(2 * data.length, 1);
if (null == data) return 1;
if (ArrayBuffer.isView(data)) return Math.max(data.byteLength, 1);
if (Array.isArray(data)) return Math.max(data.reduce((acc, item)=>acc + estimateObjectSize(item), 0), 1);
if (data instanceof Map || data instanceof Set) return 1024;
if (data instanceof Date) return 8;
if ('object' === type) return Math.max(Object.entries(data).reduce((acc, [key, value])=>acc + 2 * key.length + estimateObjectSize(value), 0), 1);
return 1;
}
class MemoryContainer {
async get(key) {
return this.lru.get(key);
}
async set(key, value, options) {
if (options?.ttl) this.lru.set(key, value, {
ttl: 1000 * options.ttl
});
else this.lru.set(key, value);
}
async has(key) {
return this.lru.has(key);
}
async delete(key) {
return this.lru.delete(key);
}
async clear() {
this.lru.clear();
}
constructor(options){
this.lru = new external_lru_cache_namespaceObject.LRUCache({
maxSize: options?.maxSize ?? CacheSize.GB,
sizeCalculation: estimateObjectSize,
updateAgeOnGet: true,
updateAgeOnHas: true
});
}
}
const isServer = "u" < typeof window;
const requestCacheMap = new WeakMap();
const TAG_PREFIX = 'tag:';
const CACHE_PREFIX = 'modernjs_cache:';
const ongoingRevalidations = new Map();
let cache_storage;
let cacheConfig = {
maxSize: CacheSize.GB
};
function getStorage() {
if (cache_storage) return cache_storage;
cache_storage = cacheConfig.container ? cacheConfig.container : new MemoryContainer({
maxSize: cacheConfig.maxSize
});
return cache_storage;
}
function configureCache(config) {
cacheConfig = {
...cacheConfig,
...config
};
cache_storage = void 0;
}
function generateKey(args) {
return JSON.stringify(args, (_, value)=>{
if (value && 'object' == typeof value && !Array.isArray(value)) return Object.keys(value).sort().reduce((result, key)=>{
result[key] = value[key];
return result;
}, {});
return value;
});
}
function generateStableFunctionId(fn) {
const fnString = fn.toString();
let hash = 0;
for(let i = 0; i < fnString.length; i++){
const char = fnString.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash &= hash;
}
return `fn_${fn.name || 'anonymous'}_${Math.abs(hash).toString(36)}`;
}
function cache(fn, options) {
return async (...args)=>{
if (isServer && void 0 === options) {
const storage = await (0, external_async_storage_js_namespaceObject.getAsyncLocalStorage)();
const request = storage?.useContext()?.request;
if (request) {
let shouldDisableCaching = false;
if (cacheConfig.unstable_shouldDisable) shouldDisableCaching = await cacheConfig.unstable_shouldDisable({
request
});
if (shouldDisableCaching) return fn(...args);
let requestCache = requestCacheMap.get(request);
if (!requestCache) {
requestCache = new Map();
requestCacheMap.set(request, requestCache);
}
let fnCache = requestCache.get(fn);
if (!fnCache) {
fnCache = new Map();
requestCache.set(fn, fnCache);
}
const key = generateKey(args);
if (fnCache.has(key)) return fnCache.get(key);
const promise = fn(...args);
fnCache.set(key, promise);
try {
const data = await promise;
return data;
} catch (error) {
fnCache.delete(key);
throw error;
}
}
} else if (void 0 !== options) try {
const { tag, maxAge = 5 * CacheTime.MINUTE, revalidate = 0, customKey, onCache, getKey, unstable_shouldCache } = options;
let missReason;
const currentStorage = getStorage();
const now = Date.now();
const tags = tag ? Array.isArray(tag) ? tag : [
tag
] : [];
const genKey = getKey ? getKey(...args) : generateKey(args);
let finalKey;
if (customKey) finalKey = customKey({
params: args,
fn,
generatedKey: genKey
});
else {
const functionId = generateStableFunctionId(fn);
finalKey = `${functionId}:${genKey}`;
}
const storageKey = `${CACHE_PREFIX}${finalKey}`;
let shouldDisableCaching = false;
if (isServer && cacheConfig.unstable_shouldDisable) {
const asyncStorage = await (0, external_async_storage_js_namespaceObject.getAsyncLocalStorage)();
const request = asyncStorage?.useContext()?.request;
if (request) shouldDisableCaching = await cacheConfig.unstable_shouldDisable({
request
});
}
if (shouldDisableCaching) missReason = 1;
else {
const cached = await currentStorage.get(storageKey);
if (cached) try {
const cacheItem = cached;
const age = now - cacheItem.timestamp;
if (age < maxAge) {
onCache?.({
status: 'hit',
key: finalKey,
params: args,
result: cacheItem.data
});
return cacheItem.data;
}
if (revalidate > 0 && age < maxAge + revalidate) {
onCache?.({
status: 'stale',
key: finalKey,
params: args,
result: cacheItem.data
});
if (!ongoingRevalidations.has(storageKey)) {
const revalidationPromise = (async ()=>{
try {
const newData = await fn(...args);
let shouldCache = true;
if (unstable_shouldCache) shouldCache = await unstable_shouldCache({
params: args,
result: newData
});
if (shouldCache) await setCacheItem(currentStorage, storageKey, newData, tags, maxAge, revalidate);
} catch (error) {
if (isServer) {
const asyncStorage = await (0, external_async_storage_js_namespaceObject.getAsyncLocalStorage)();
asyncStorage?.useContext()?.monitors?.error(error.message);
} else console.error('Background revalidation failed:', error);
} finally{
ongoingRevalidations.delete(storageKey);
}
})();
ongoingRevalidations.set(storageKey, revalidationPromise);
}
return cacheItem.data;
}
missReason = 3;
} catch (error) {
console.warn('Failed to parse cached data:', error);
missReason = 4;
}
else missReason = 2;
}
const data = await fn(...args);
if (!shouldDisableCaching) {
let shouldCache = true;
if (unstable_shouldCache) shouldCache = await unstable_shouldCache({
params: args,
result: data
});
if (shouldCache) await setCacheItem(currentStorage, storageKey, data, tags, maxAge, revalidate);
}
onCache?.({
status: 'miss',
key: finalKey,
params: args,
result: data,
reason: missReason
});
return data;
} catch (error) {
console.warn('Cache operation failed, falling back to direct execution:', error);
const data = await fn(...args);
const { onCache } = options;
try {
onCache?.({
status: 'miss',
key: 'cache_failed',
params: args,
result: data,
reason: 5
});
} catch (callbackError) {
console.warn('Failed to call onCache callback:', callbackError);
}
return data;
}
else {
console.warn('The cache function will not work because it runs on the browser and there are no options are provided.');
return fn(...args);
}
};
}
async function setCacheItem(storage, storageKey, data, tags, maxAge, revalidate) {
const newItem = {
data,
timestamp: Date.now(),
tags: tags.length > 0 ? tags : void 0
};
const ttl = (maxAge + revalidate) / 1000;
await storage.set(storageKey, newItem, {
ttl: ttl > 0 ? ttl : void 0
});
await updateTagRelationships(storage, storageKey, tags);
}
async function updateTagRelationships(storage, storageKey, tags) {
for (const tag of tags){
const tagStoreKey = `${TAG_PREFIX}${tag}`;
const keyList = await storage.get(tagStoreKey);
const keyArray = keyList || [];
if (!keyArray.includes(storageKey)) keyArray.push(storageKey);
await storage.set(tagStoreKey, keyArray);
}
}
async function removeKeyFromTags(storage, storageKey, tags) {
for (const tag of tags){
const tagStoreKey = `${TAG_PREFIX}${tag}`;
const keyList = await storage.get(tagStoreKey);
if (keyList) try {
const keyArray = Array.isArray(keyList) ? keyList : [];
const updatedKeyList = keyArray.filter((key)=>key !== storageKey);
if (updatedKeyList.length > 0) await storage.set(tagStoreKey, updatedKeyList);
else await storage.delete(tagStoreKey);
} catch (error) {
console.warn(`Failed to process tag key list for tag ${tag}:`, error);
}
}
}
function withRequestCache(handler) {
if (!isServer) return handler;
return async (req, ...args)=>{
const storage = await (0, external_async_storage_js_namespaceObject.getAsyncLocalStorage)();
return storage.run({
request: req
}, ()=>handler(req, ...args));
};
}
async function revalidateTag(tag) {
const currentStorage = getStorage();
const tagStoreKey = `${TAG_PREFIX}${tag}`;
const keyList = await currentStorage.get(tagStoreKey);
if (keyList) try {
const keyArray = Array.isArray(keyList) ? keyList : [];
for (const cacheKey of keyArray){
const cached = await currentStorage.get(cacheKey);
if (cached) try {
const cacheItem = cached;
if (cacheItem.tags) {
const otherTags = cacheItem.tags.filter((t)=>t !== tag);
await removeKeyFromTags(currentStorage, cacheKey, otherTags);
}
} catch (error) {
console.warn('Failed to parse cached data while revalidating:', error);
}
await currentStorage.delete(cacheKey);
}
await currentStorage.delete(tagStoreKey);
} catch (error) {
console.warn('Failed to process tag key list:', error);
}
}
async function clearStore() {
const currentStorage = getStorage();
await currentStorage.clear();
cache_storage = void 0;
ongoingRevalidations.clear();
}
exports.CacheSize = __webpack_exports__.CacheSize;
exports.CacheTime = __webpack_exports__.CacheTime;
exports.cache = __webpack_exports__.cache;
exports.clearStore = __webpack_exports__.clearStore;
exports.configureCache = __webpack_exports__.configureCache;
exports.generateKey = __webpack_exports__.generateKey;
exports.revalidateTag = __webpack_exports__.revalidateTag;
exports.withRequestCache = __webpack_exports__.withRequestCache;
for(var __rspack_i in __webpack_exports__)if (-1 === [
"CacheSize",
"CacheTime",
"cache",
"clearStore",
"configureCache",
"generateKey",
"revalidateTag",
"withRequestCache"
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
Object.defineProperty(exports, '__esModule', {
value: true
});