UNPKG

@composite-fetcher/with-caching

Version:

withCaching is a simple fetcher core plugin to manage request caching with different drivers

180 lines (175 loc) 5.53 kB
"use strict"; 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, { InMemoryCacheDriver: () => InMemoryCacheDriver, SessionStorageDriver: () => SessionStorageDriver, withCaching: () => withCachingPlugin }); module.exports = __toCommonJS(src_exports); // src/drivers/InMemoryCacheDriver.ts var InMemoryCacheDriver = class { cache; constructor() { this.cache = /* @__PURE__ */ new Map(); } async has(key) { const item = this.cache.get(key); if (!item) return false; if (item.expiration && item.expiration <= /* @__PURE__ */ new Date()) { this.delete(key); return false; } return true; } async get(key) { const item = this.cache.get(key); if (!item) return null; if (item.expiration && item.expiration <= /* @__PURE__ */ new Date()) { this.delete(key); return null; } return this.recreateResponse(item.value); } async set(key, response, expiration) { const serializedResponse = await this.serializeResponse(response); this.cache.set(key, { value: serializedResponse, expiration }); } async delete(key) { this.cache.delete(key); } async clear() { this.cache.clear(); } async serializeResponse(response) { const body = await response.arrayBuffer(); return { url: response.url, status: response.status, statusText: response.statusText, headers: [...response.headers.entries()], body }; } recreateResponse(data) { return new Response(data.body, { status: data.status, statusText: data.statusText, headers: data.headers }); } }; // src/drivers/SessionStorageDriver.ts var SessionStorageDriver = class { async set(key, response, expirationDate) { const item = { url: response.url, value: await response.clone().text(), headers: Array.from(response.headers.entries()), status: response.status, statusText: response.statusText, expirationDate: expirationDate?.toISOString() }; sessionStorage.setItem(key, JSON.stringify(item)); } async get(key) { const itemStr = sessionStorage.getItem(key); if (!itemStr) return null; const item = JSON.parse(itemStr); if (item.expirationDate && new Date(item.expirationDate) < /* @__PURE__ */ new Date()) { await this.delete(key); return null; } const init = { status: item.status, statusText: item.statusText, headers: item.headers }; return new Response(item.value, init); } async has(key) { const value = await this.get(key); return value !== null; } async delete(key) { sessionStorage.removeItem(key); } async clear() { sessionStorage.clear(); } }; // src/withCaching.ts var import_core = require("@composite-fetcher/core"); var withCachingPlugin = class extends import_core.BasePlugin { cacheDriver; defaultTTL = 10 * 60 * 1e3; // 10 minutes constructor(options = {}) { super(); const { cacheDriver, defaultTTL } = options; this.cacheDriver = cacheDriver || new InMemoryCacheDriver(); if (defaultTTL) { this.defaultTTL = defaultTTL; } } async generateCacheKey(request) { const url = new URL(request.url); const sortedParams = Array.from(url.searchParams.entries()).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, val]) => `${key}=${val}`).join("&"); const bodyString = request.body ? JSON.stringify(request.body) : ""; const cacheKey = `${request.method}-${url.origin}${url.pathname}?${sortedParams}-${bodyString}`; return cacheKey; } getCacheTTL(milliseconds) { return new Date(Date.now() + milliseconds); } async onPreRequest(context) { if (context.request.headers.has("x-fetcher-no-cache")) { return; } const cacheKey = await this.generateCacheKey(context.request); if (await this.cacheDriver.has(cacheKey)) { const cachedResponse = await this.cacheDriver.get(cacheKey); if (cachedResponse !== null) { return cachedResponse.clone(); } } } async onPostRequest(context) { const { response, originalRequest } = context; if (!originalRequest.headers.has("x-fetcher-no-cache") && response.ok) { const cacheTTL = originalRequest.headers.has("x-fetcher-cache-ttl") ? Number(originalRequest.headers.get("x-fetcher-cache-ttl")) : this.defaultTTL; const cacheKey = await this.generateCacheKey(context.originalRequest); await this.cacheDriver.set( cacheKey, response, this.getCacheTTL(cacheTTL) ); } } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { InMemoryCacheDriver, SessionStorageDriver, withCaching });