UNPKG

@cf-wasm/og

Version:

Generate Open Graph Images dynamically from HTML/CSS without a browser.

142 lines (141 loc) 4.61 kB
// src/core/cache.ts var CACHE_INSTANCE_MAP = /* @__PURE__ */ new Map(); var CACHE_INTERFACE = { add: () => Promise.resolve(), addAll: () => Promise.resolve(), delete: () => Promise.resolve(false), keys: () => Promise.resolve([]), match: () => Promise.resolve(void 0), matchAll: () => Promise.resolve([]), put: () => Promise.resolve() }; var CacheUtils = class { constructor() { /** Indicates whether cache is enabled */ this._enabled = true; /** * The {@link Cache} instance or a * string representing the name of {@link Cache} to be opened */ this.store = "cf-wasm-og-cache"; /** * The `Cache-Control` header to set for asset responses for caching * * @default 'public, max-age=604800, s-maxage=43200' */ this.cacheControlHeader = "public, max-age=604800, s-maxage=43200"; } /** * Indicates whether {@link Cache} api is supported * in current environment or not */ get supported() { return typeof caches !== "undefined"; } /** Enables cache */ enable() { this._enabled = true; } /** Disables cache */ disable() { this._enabled = false; } /** * Sets execution context * * Example for Cloudflare workers: * * ```ts * import { cache } from "@cf-wasm/og"; * * export interface Env {} * * export default { * fetch(req: Request, env: Env, ctx: ExecutionContext) { * cache.setExecutionContext(ctx); * * // ... * } * } * ``` */ setExecutionContext(ctx) { if (typeof ctx?.waitUntil !== "function") { throw new TypeError("Provided object is not an execution context object."); } this._waitUntil = (promise) => ctx.waitUntil(promise); } /** * Opens a cache * * @param cacheName If provided, the name of cache to be opened * * @returns A promise which resolves to {@link Cache} */ async open(cacheName) { if (typeof this.store === "object") { return this.store; } const name = typeof cacheName === "string" ? cacheName : this.store; const store = this.supported ? CACHE_INSTANCE_MAP.get(name) ?? await caches.open(name) : CACHE_INTERFACE; CACHE_INSTANCE_MAP.set(name, store); return store; } /** * Serve cached assets * * @param key The cache key * @param fallback The fallback function which provides the {@link Response} when cache key is not matched * @param param2 Options * * @returns A promise which resolves to {@link Response} */ async serve(key, fallback, { cache: cacheStore, preserveHeaders, overwriteCacheControl = true } = {}) { const store = cacheStore ?? await this.open(); let response; if (!this._enabled || !this.supported || !this._waitUntil) { const fallbackResponse = await fallback(key, store); if (fallbackResponse instanceof Response) { response = new Response(fallbackResponse.body, fallbackResponse); } } else { response = await store.match(key).then((res) => res?.ok ? res : void 0); if (!response) { const fallbackResponse = await fallback(key, store); if (fallbackResponse instanceof Response) { response = new Response(fallbackResponse.body, fallbackResponse); if (preserveHeaders !== true) { response.headers.forEach((value, _key) => { if (preserveHeaders === false) { response?.headers.delete(_key); } else if (typeof preserveHeaders === "function" && !preserveHeaders(_key, value, response, store, key)) { response?.headers.delete(_key); } else { const preservedHeaders = (Array.isArray(preserveHeaders) ? preserveHeaders : ["content-type", "cache-control"]).map( (e) => e.toLowerCase() ); if (!preservedHeaders.includes(_key.toLowerCase())) { response?.headers.delete(_key); } } }); } if (overwriteCacheControl === true || typeof overwriteCacheControl === "string" || !response.headers.has("Cache-Control")) { response.headers.set("Cache-Control", typeof overwriteCacheControl === "string" ? overwriteCacheControl : this.cacheControlHeader); } const promise = store.put(key, response.clone()); this._waitUntil(promise); } } else { response = new Response(response.body, response); } } return response; } }; var cache = new CacheUtils(); export { CACHE_INSTANCE_MAP, CACHE_INTERFACE, cache };