UNPKG

@modern-js/runtime-utils

Version:

A Progressive React Framework for modern web development.

382 lines (381 loc) • 14.8 kB
"use strict"; 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 });