UNPKG

vite-plugin-vanjs

Version:

An async first mini meta-framework for VanJS powered by Vite

169 lines (153 loc) 4.48 kB
/** @typedef {{ data: unknown; error: Error | null; status: "idle" | "pending" | "success" | "error"; timestamp: number }} CacheEntry */ /** @typedef {CacheEntry["status"]} DataStatus */ /** * @param {string} pathname * @returns {string} */ export const normalizePath = (pathname) => { if (!pathname || pathname === "/") return "/"; return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname; }; /** @type {Map<string, Map<string, CacheEntry>>} */ const dataCacheMap = new Map(); const DEFAULT_MAX_ROUTES = 20; let maxRoutes = DEFAULT_MAX_ROUTES; /** * @param {number} n */ export const setMaxRoutes = (n) => { maxRoutes = n; }; const evictIfNeeded = () => { if (maxRoutes <= 0) return; while (dataCacheMap.size > maxRoutes) { const firstKey = dataCacheMap.keys().next().value; if (firstKey !== undefined) { dataCacheMap.delete(firstKey); } } }; /** * @param {string} pathname * @param {string} key * @returns {CacheEntry | undefined} */ export const get = (pathname, key) => { const path = normalizePath(pathname); const routeMap = dataCacheMap.get(path); if (!routeMap) return undefined; return routeMap.get(key); }; /** * @param {string} pathname * @param {string} key * @param {Partial<CacheEntry>} entry */ export const set = (pathname, key, entry) => { const path = normalizePath(pathname); let routeMap = dataCacheMap.get(path); if (!routeMap) { routeMap = new Map(); dataCacheMap.set(path, routeMap); evictIfNeeded(); } routeMap.set(key, { ...entry, timestamp: entry?.timestamp || Date.now(), }); }; /** * @param {string} pathname * @param {string} [key] * @returns {boolean} */ export const has = (pathname, key) => { const path = normalizePath(pathname); const routeMap = dataCacheMap.get(path); if (!routeMap) return false; if (key === undefined) return routeMap.size > 0; return routeMap.has(key); }; /** * @param {string} pathname * @returns {Record<string, CacheEntry> | undefined} */ export const getRoute = (pathname) => { const path = normalizePath(pathname); const routeMap = dataCacheMap.get(path); if (!routeMap) return undefined; return Object.fromEntries(routeMap.entries()); }; /** * @param {string} pathname * @param {string} [key] * @returns {boolean} */ export const del = (pathname, key) => { const path = normalizePath(pathname); if (key === undefined) { const deleted = dataCacheMap.delete(path); return deleted; } const routeMap = dataCacheMap.get(path); if (!routeMap) return false; const deleted = routeMap.delete(key); if (routeMap.size === 0) { dataCacheMap.delete(path); } return deleted; }; export const clear = () => { dataCacheMap.clear(); }; /** * @param {string} pathname */ export const touch = (pathname) => { const path = normalizePath(pathname); const routeMap = dataCacheMap.get(path); if (!routeMap) return; dataCacheMap.delete(path); dataCacheMap.set(path, routeMap); }; /** * @returns {Record<string, Record<string, { data: unknown; status: string; timestamp: number; error: { message: string } | null }>>} */ export const toJSON = () => { /** @type {Record<string, Record<string, { data: unknown; status: string; timestamp: number; error: { message: string } | null }>>} */ const result = {}; for (const [path, routeMap] of dataCacheMap.entries()) { result[path] = {}; for (const [key, entry] of routeMap.entries()) { result[path][key] = { data: entry.data, timestamp: entry.timestamp, error: entry.error ? { message: entry.error.message } : null, }; } } return result; }; /** * @param {Record<string, Record<string, { data: unknown; status?: string; timestamp?: number; error?: { message: string } | null }>>} json */ export const hydrateFromJSON = (json) => { if (!json || typeof json !== "object") return; for (const [path, routeEntries] of Object.entries(json)) { const normalized = normalizePath(path); let routeMap = dataCacheMap.get(normalized); if (!routeMap) { routeMap = new Map(); dataCacheMap.set(normalized, routeMap); } for (const [key, entry] of Object.entries(routeEntries)) { routeMap.set(key, { data: entry.data, timestamp: entry.timestamp || Date.now(), error: entry.error ? new Error(entry.error.message) : null, }); } } }; /** @returns {number} */ export const size = () => dataCacheMap.size;