UNPKG

@solomonai/cache

Version:

<div align="center"> <h1 align="center">@solomonai/cache</h1> <h5>Cache all the things</h5> </div>

272 lines (266 loc) 7.25 kB
'use strict'; var error = require('@unkey/error'); var superjson = require('superjson'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var superjson__default = /*#__PURE__*/_interopDefault(superjson); // src/stores/cloudflare.ts var CacheError = class extends error.BaseError { name = "CacheError"; retry = false; tier; key; constructor(opts) { super(opts); this.name = "CacheError"; this.tier = opts.tier; this.key = opts.key; } }; // src/stores/cloudflare.ts var CloudflareStore = class { config; name = "cloudflare"; constructor(config) { this.config = config; } createCacheKey(namespace, key) { return new URL( `https://${this.config.domain}/cache/${this.config.cacheBuster ?? "v1"}/${String( namespace )}/${key}` ); } async get(namespace, key) { let res; try { res = await caches.default.match(new Request(this.createCacheKey(namespace, key))); } catch (err) { return error.Err( new CacheError({ tier: this.name, key, message: err.message }) ); } if (!res) { return error.Ok(void 0); } const raw = await res.text(); try { const entry = superjson__default.default.parse(raw); return error.Ok(entry); } catch (err) { return error.Err( new CacheError({ tier: this.name, key, message: err.message }) ); } } async set(namespace, key, entry) { const req = new Request(this.createCacheKey(namespace, key)); const res = new Response(superjson__default.default.stringify(entry), { headers: { "Content-Type": "application/json", "Cache-Control": `public, max-age=${Math.floor(entry.staleUntil / 1e3)}` } }); try { await caches.default.put(req, res); return error.Ok(); } catch (err) { return error.Err( new CacheError({ tier: this.name, key, message: err.message }) ); } } async remove(namespace, keys) { const cacheKeys = (Array.isArray(keys) ? keys : [keys]).map( (key) => this.createCacheKey(namespace, key) ); await Promise.all(cacheKeys.map((key) => caches.default.delete(key))); return await fetch( `https://api.cloudflare.com/client/v4zones/${this.config.zoneId}/purge_cache`, { method: "POST", headers: { Authorization: `Bearer ${this.config.cloudflareApiKey}`, "Content-Type": "application/json" }, body: JSON.stringify({ files: cacheKeys.map((k) => k.toString()) }) } ).then((res) => res.body?.cancel()).then(() => error.Ok()).catch( (err) => error.Err( new CacheError({ tier: this.name, key: keys.toString(), message: err.message }) ) ); } }; var MemoryStore = class { state; unstableEvictOnSet; name = "memory"; constructor(config) { this.state = config.persistentMap; this.unstableEvictOnSet = config.unstableEvictOnSet; } buildCacheKey(namespace, key) { return [namespace, key].join("::"); } async get(namespace, key) { const value = this.state.get(this.buildCacheKey(namespace, key)); if (!value) { return Promise.resolve(error.Ok(void 0)); } if (value.expires <= Date.now()) { await this.remove(namespace, key); } return Promise.resolve(error.Ok(value.entry)); } async set(namespace, key, entry) { this.state.set(this.buildCacheKey(namespace, key), { expires: entry.staleUntil, entry }); if (this.unstableEvictOnSet && Math.random() < this.unstableEvictOnSet.frequency) { const now = Date.now(); this.state.forEach((value, key2, map) => { if (value.expires < now) { map.delete(key2); } }); for (const [k] of this.state) { if (this.state.size <= this.unstableEvictOnSet.maxItems) { break; } this.state.delete(k); } } return Promise.resolve(error.Ok()); } async remove(namespace, keys) { const cacheKeys = (Array.isArray(keys) ? keys : [keys]).map( (key) => this.buildCacheKey(namespace, key).toString() ); for (const key of cacheKeys) { this.state.delete(key); } return Promise.resolve(error.Ok()); } }; var UpstashRedisStore = class { redis; name = "upstash-redis"; constructor(config) { this.redis = config.redis; } buildCacheKey(namespace, key) { return [namespace, key].join("::"); } async get(namespace, key) { const value = await this.redis.get(this.buildCacheKey(namespace, key)); if (!value) { return error.Ok(void 0); } return error.Ok(value); } async set(namespace, key, entry) { await this.redis.set(this.buildCacheKey(namespace, key), entry, { pxat: entry.staleUntil }); return error.Ok(); } async remove(namespace, keys) { const cacheKeys = (Array.isArray(keys) ? keys : [keys]).map( (key) => this.buildCacheKey(namespace, key).toString() ); this.redis.del(...cacheKeys); return error.Ok(); } }; var LibSQLStore = class { tableName; client; name = "libsql"; constructor(config) { this.client = config.client; this.tableName = config.tableName || "cache"; } buildCacheKey(namespace, key) { return `${namespace}::${key}`; } async get(namespace, key) { try { const result = await this.client.execute({ sql: `SELECT value, freshUntil, staleUntil FROM ${this.tableName} WHERE key = ?`, args: [this.buildCacheKey(namespace, key)] }); if (result.rows.length === 0) { return error.Ok(void 0); } const row = result.rows[0]; const entry = { value: JSON.parse(row.value), freshUntil: Number(row.freshUntil), staleUntil: Number(row.staleUntil) }; return error.Ok(entry); } catch { return error.Ok(void 0); } } async set(namespace, key, entry) { try { await this.client.execute({ sql: `INSERT OR REPLACE INTO ${this.tableName} (key, value, freshUntil, staleUntil) VALUES (?, ?, ?, ?)`, args: [ this.buildCacheKey(namespace, key), JSON.stringify(entry.value), entry.freshUntil, entry.staleUntil ] }); return error.Ok(); } catch { return error.Ok(); } } async remove(namespace, keys) { const cacheKeys = (Array.isArray(keys) ? keys : [keys]).map( (key) => this.buildCacheKey(namespace, key).toString() ); try { await Promise.all( cacheKeys.map( (cacheKey) => this.client.execute({ sql: `DELETE FROM ${this.tableName} WHERE key = ?`, args: [cacheKey] }) ) ); return error.Ok(); } catch { return error.Ok(); } } }; exports.CloudflareStore = CloudflareStore; exports.LibSQLStore = LibSQLStore; exports.MemoryStore = MemoryStore; exports.UpstashRedisStore = UpstashRedisStore; //# sourceMappingURL=stores.js.map //# sourceMappingURL=stores.js.map