@dotpress/cache 2.0.0-alpha.0 → 2.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ Caching for [Dotpress](https://dotpress.dev), at two levels:
7
7
 
8
8
  Works without a Dotpress app: in a script, a worker or a Lambda function.
9
9
 
10
- > 🚧 Dotpress V2 is under active development and not published yet.
10
+ > 🚧 Dotpress V2 is in alpha (`2.0.0-alpha`): the API may still change before 2.0.0.
11
11
 
12
12
  ```sh
13
13
  pnpm add @dotpress/cache
@@ -83,7 +83,7 @@ await cache.set(userCache, id, user)
83
83
  await cache.delete(userCache, id) // after an update
84
84
  ```
85
85
 
86
- - **`getOrSet`** reads the value, or loads it with the last argument and caches it. Calls made while it loads share the load (single flight, per cache). A failed load is not cached: the error reaches the caller, unless the grace period serves a stale value (see [Resilience](#resilience)).
86
+ - **`getOrSet`** reads the value, or loads it with the last argument and caches it. Calls made while it loads share the load (single flight, per cache). A failed load is not cached: the error reaches the caller, unless the grace period serves a stale value (see [Resilience](#resilience)). A load that returns `undefined` (not found) is not cached either: `getOrSet` returns `undefined`, and the type of the result includes it.
87
87
  - **`get`** returns fresh values only: undefined when missing, expired or invalidated by a tag.
88
88
  - **Keys**: `prefix:name:vVersion:key`, like `app:user:v1:42`. `key` returns a string, a number, or a value compared like the arguments of a memo; without `key`, the argument (or the arguments) is the key. Keys longer than the store accepts are hashed (SHA-256).
89
89
  - **`version`** (1 by default) is part of the key: increase it when the shape of the values changes, and the values written by the previous version are ignored.
package/dist/index.d.ts CHANGED
@@ -357,9 +357,12 @@ interface Cache {
357
357
  * The value of the arguments, loaded by the last argument when missing
358
358
  * and cached. Calls made while it loads share the load. Applies the
359
359
  * grace period, stale-while-revalidate and timeouts of the entry.
360
+ *
361
+ * A loader may return `undefined` (not found): it is not cached, and
362
+ * `getOrSet` returns it.
360
363
  */
361
364
  getOrSet<A extends readonly unknown[], R>(entry: CacheEntry<A, Untyped>, ...argsAndLoader: [...A, () => R | Promise<R>]): Promise<R>;
362
- getOrSet<A extends readonly unknown[], T>(entry: CacheEntry<A, T>, ...argsAndLoader: [...A, () => T | Promise<T>]): Promise<T>;
365
+ getOrSet<A extends readonly unknown[], T, R extends T | undefined = T>(entry: CacheEntry<A, T>, ...argsAndLoader: [...A, () => R | Promise<R>]): Promise<[R] extends [T] ? T : T | undefined>;
363
366
  /**
364
367
  * Expires every value with one of the tags (they can still be served by
365
368
  * the grace period when the loader fails).
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/logger.ts","../src/store.ts","../src/cache.ts"],"sourcesContent":["/**\n * Logger of the cache. A Dotpress or Pino logger fits; `cachePlugin` uses\n * the logger of the request.\n */\nexport interface CacheLogger {\n debug?(data: object, message: string): void\n info(data: object, message: string): void\n warn(data: object, message: string): void\n error(data: object, message: string): void\n}\n\nconst noop = () => undefined\n\n/** Logger that discards everything. */\nexport const silentLogger: CacheLogger = {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n}\n\n/** Checks a logger option. */\nexport const checkLogger = (label: string, logger: unknown) => {\n for (const level of ['info', 'warn', 'error'] as const) {\n if (\n typeof (logger as Record<string, unknown> | null)?.[level] !== 'function'\n ) {\n throw new TypeError(\n `${label}: logger must have info(), warn() and error() methods.`\n )\n }\n }\n return logger as CacheLogger\n}\n","/**\n * Where the cache keeps its entries: Redis, Upstash, Cloudflare KV,\n * memory… Values are strings, serialized by the cache.\n */\nexport interface CacheStore {\n /** Name of the store, in logs and errors. */\n readonly name: string\n /**\n * Longest key accepted, in bytes (UTF-8). Longer keys are hashed by the\n * cache. Undefined without limit.\n */\n readonly maxKeyLength?: number | undefined\n /** Value of a key, or undefined when it is missing or expired. */\n get(key: string): Promise<string | undefined>\n /**\n * Values of several keys, in their order: one round trip for the tags of\n * an entry. Optional: the cache calls `get` for each key without it.\n */\n getMany?(keys: readonly string[]): Promise<(string | undefined)[]>\n /** Sets a value that expires after `ttl` milliseconds (at least 1). */\n set(key: string, value: string, ttl: number): Promise<void>\n /** Removes a key; missing keys are ignored. */\n delete(key: string): Promise<void>\n /**\n * Takes a lock for `ttl` milliseconds: returns its token, or undefined\n * when another holder has it. Optional: the `lock` option of the cache\n * needs it.\n */\n lock?(key: string, ttl: number): Promise<string | undefined>\n /** Releases a lock, if the token still holds it. */\n unlock?(key: string, token: string): Promise<void>\n}\n\n/** Checks a store option. */\nexport const isStore = (value: unknown): value is CacheStore => {\n const store = value as Partial<CacheStore> | null\n return (\n typeof store?.name === 'string' &&\n typeof store.get === 'function' &&\n typeof store.set === 'function' &&\n typeof store.delete === 'function'\n )\n}\n\n/** Options of `createMemoryStore()`. */\nexport interface MemoryStoreOptions {\n /**\n * Entries kept at most: the least recently used ones are removed\n * first. Defaults to 10,000.\n */\n maxEntries?: number | undefined\n}\n\n/** Store in the memory of the process: for one instance, and for tests. */\nexport interface MemoryStore extends CacheStore {\n /** Number of entries, expired ones included until they are read. */\n readonly size: number\n /** Removes every entry. */\n clear(): void\n}\n\n/**\n * Creates a store in the memory of the process, least recently used\n * entries removed first. Each instance has its own: use a shared store\n * (Redis…) for several instances.\n */\nexport const createMemoryStore = (\n options: MemoryStoreOptions = {}\n): MemoryStore => {\n const label = 'createMemoryStore()'\n if (typeof options !== 'object' || options === null) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n for (const key of Object.keys(options)) {\n if (key !== 'maxEntries') {\n throw new TypeError(`${label}: unknown option \"${key}\".`)\n }\n }\n const { maxEntries = 10_000 } = options\n if (!Number.isInteger(maxEntries) || maxEntries < 1) {\n throw new TypeError(`${label}: maxEntries must be a positive integer.`)\n }\n\n // Insertion order of a Map: the first entry is the least recently used.\n const entries = new Map<string, { value: string; expiresAt: number }>()\n const locks = new Map<string, { token: string; expiresAt: number }>()\n\n const read = (key: string) => {\n const entry = entries.get(key)\n if (!entry) return undefined\n entries.delete(key)\n if (entry.expiresAt <= Date.now()) return undefined\n entries.set(key, entry)\n return entry.value\n }\n\n return Object.freeze({\n name: 'memory',\n get size() {\n return entries.size\n },\n get: async (key: string) => read(key),\n getMany: async (keys: readonly string[]) => keys.map(read),\n set: async (key: string, value: string, ttl: number) => {\n entries.delete(key)\n entries.set(key, { value, expiresAt: Date.now() + ttl })\n for (const oldest of entries.keys()) {\n if (entries.size <= maxEntries) break\n entries.delete(oldest)\n }\n },\n delete: async (key: string) => {\n entries.delete(key)\n },\n lock: async (key: string, ttl: number) => {\n const held = locks.get(key)\n if (held && held.expiresAt > Date.now()) return undefined\n const token = crypto.randomUUID()\n locks.set(key, { token, expiresAt: Date.now() + ttl })\n return token\n },\n unlock: async (key: string, token: string) => {\n if (locks.get(key)?.token === token) locks.delete(key)\n },\n clear: () => {\n entries.clear()\n locks.clear()\n },\n })\n}\n","import { toMilliseconds, type Duration } from './duration.js'\nimport {\n CacheStoreError,\n CacheTimeoutError,\n type StoreOperation,\n} from './errors.js'\nimport { keyOf } from './keys.js'\nimport { checkLogger, silentLogger, type CacheLogger } from './logger.js'\nimport { isStore, type CacheStore } from './store.js'\n\n/**\n * A schema that validates cached values when they are read: a Zod schema,\n * or anything with the same `safeParseAsync()`.\n */\nexport interface CacheSchema<T> {\n safeParseAsync(\n value: unknown\n ): Promise<{ success: true; data: T } | { success: false }>\n}\n\ndeclare const untyped: unique symbol\n/** Value type of an entry without schema: given by the loader. */\nexport interface Untyped {\n readonly [untyped]: true\n}\n\ndeclare const entryTypes: unique symbol\n\n/** A kind of cached value, created by `defineCacheEntry()`. */\nexport interface CacheEntry<A extends readonly unknown[], T> {\n readonly name: string\n readonly version: number\n /** Types only. */\n readonly [entryTypes]?: { args: A; value: (value: T) => T }\n}\n\n/** Value of an entry: `unknown` without schema. */\nexport type CacheValue<T> = T extends Untyped ? unknown : T\n\n/**\n * Lifetime and resilience of values: options of an entry, or defaults of\n * the cache.\n */\nexport interface CachePolicy {\n /** Lifetime of the values. Defaults to 1 hour. */\n ttl?: Duration | undefined\n /**\n * Stale-if-error: how long after expiry a value can still be served when\n * the loader fails (or times out). Defaults to 0 (disabled).\n */\n grace?: Duration | undefined\n /**\n * After a failed load served from the grace period, the loader is not\n * called again before this delay: the stale value is served. Defaults to\n * 10 seconds.\n */\n graceBackoff?: Duration | undefined\n /**\n * How long after expiry a value is still served while a background load\n * refreshes it. Defaults to 0 (disabled).\n */\n staleWhileRevalidate?: Duration | undefined\n /**\n * Soft timeout: a load slower than this serves the stale value of the\n * grace period, if any, and goes on in the background.\n */\n timeout?: Duration | undefined\n /**\n * Hard timeout: a load slower than this fails (`CacheTimeoutError`), or\n * serves the stale value of the grace period, if any. It goes on in the\n * background.\n */\n hardTimeout?: Duration | undefined\n /**\n * Distributed single flight: before loading a missing value, takes a\n * lock in the store (Redis…) for at most this duration; the other\n * instances wait for the value meanwhile, then load it themselves.\n * Ignored by stores without locks. Disabled by default.\n */\n lock?: Duration | undefined\n}\n\n/** Options of `defineCacheEntry()`. */\nexport interface CacheEntryOptions<\n A extends readonly unknown[],\n T,\n> extends CachePolicy {\n /**\n * Name of the entry, in keys and logs: letters, digits, `.`, `_` and\n * `-`.\n */\n name: string\n /**\n * Key of the arguments: a string, a number, or a value compared like the\n * arguments of a memo. Defaults to the argument (or the arguments).\n */\n key?: ((...args: A) => unknown) | undefined\n /**\n * Validates cached values when they are read (entries from an older\n * version of the app) and written. JSON turns dates into strings: use\n * `z.coerce.date()`.\n */\n schema?: CacheSchema<T> | undefined\n /**\n * Version of the values, in the key: increase it when their shape\n * changes, to ignore the values written before. Defaults to 1.\n */\n version?: number | undefined\n /**\n * Tags of the arguments: `invalidateTags()` expires every value with one\n * of them.\n */\n tags?: ((...args: A) => readonly string[]) | undefined\n}\n\n/** A policy in milliseconds; undefined when not set. */\ntype Policy = { [K in keyof CachePolicy]: number | undefined }\n\ninterface EntryConfig {\n name: string\n version: number\n key: ((...args: readonly unknown[]) => unknown) | undefined\n schema: CacheSchema<unknown> | undefined\n tags: ((...args: readonly unknown[]) => readonly string[]) | undefined\n policy: Policy\n}\n\nconst entries = new WeakMap<object, EntryConfig>()\n\nconst NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/\nconst POLICY = [\n 'ttl',\n 'grace',\n 'graceBackoff',\n 'staleWhileRevalidate',\n 'timeout',\n 'hardTimeout',\n 'lock',\n] as const\nconst ENTRY_OPTIONS = new Set([\n 'name',\n 'key',\n 'schema',\n 'version',\n 'tags',\n ...POLICY,\n])\n\n/** Reads the durations of a policy (`ttl` and timeouts at least 1 ms). */\nconst policyOf = (label: string, values: Record<string, unknown>): Policy => {\n const policy: Policy = {}\n for (const option of POLICY) {\n const value = values[option]\n if (value === undefined) continue\n policy[option] =\n option === 'ttl' ||\n option === 'timeout' ||\n option === 'hardTimeout' ||\n option === 'lock'\n ? positive(`${label}: ${option}`, value)\n : toMilliseconds(`${label}: ${option}`, value)\n }\n return policy\n}\n\n/**\n * Defines a kind of cached value: its key, its lifetime, and optionally\n * its schema.\n *\n * ```ts\n * export const userCache = defineCacheEntry({\n * name: 'user',\n * key: (id: string) => id,\n * schema: userSchema,\n * ttl: '1h',\n * })\n * ```\n */\nexport function defineCacheEntry<A extends readonly unknown[], T>(\n options: CacheEntryOptions<A, T> & { schema: CacheSchema<T> }\n): CacheEntry<A, T>\nexport function defineCacheEntry<A extends readonly unknown[]>(\n options: CacheEntryOptions<A, never> & { schema?: undefined }\n): CacheEntry<A, Untyped>\nexport function defineCacheEntry<A extends readonly unknown[], T>(\n options: CacheEntryOptions<A, T>\n): CacheEntry<A, T>\nexport function defineCacheEntry(options: unknown): {\n readonly name: string\n readonly version: number\n} {\n const label = 'defineCacheEntry()'\n if (typeof options !== 'object' || options === null) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n const values = options as Record<string, unknown>\n const { name, key, schema, tags, version = 1 } = values\n if (typeof name !== 'string' || !NAME.test(name)) {\n throw new TypeError(\n `${label}: name must have 1 to 128 letters, digits, \".\", \"_\" or \"-\", starting with a letter or a digit.`\n )\n }\n const named = `defineCacheEntry(\"${name}\")`\n for (const option of Object.keys(values)) {\n if (!ENTRY_OPTIONS.has(option)) {\n throw new TypeError(`${named}: unknown option \"${option}\".`)\n }\n }\n if (key !== undefined && typeof key !== 'function') {\n throw new TypeError(`${named}: key must be a function.`)\n }\n if (\n schema !== undefined &&\n typeof (schema as Partial<CacheSchema<unknown>> | null)?.safeParseAsync !==\n 'function'\n ) {\n throw new TypeError(\n `${named}: schema must have a safeParseAsync() method, like a Zod schema.`\n )\n }\n if (!Number.isInteger(version) || (version as number) < 0) {\n throw new TypeError(`${named}: version must be a non-negative integer.`)\n }\n if (tags !== undefined && typeof tags !== 'function') {\n throw new TypeError(`${named}: tags must be a function.`)\n }\n\n const entry = Object.freeze({ name, version: version as number })\n entries.set(entry, {\n name,\n version: version as number,\n key: key as EntryConfig['key'],\n schema: schema as CacheSchema<unknown> | undefined,\n tags: tags as EntryConfig['tags'],\n policy: policyOf(named, values),\n })\n return entry\n}\n\nconst positive = (label: string, value: unknown) => {\n const ms = toMilliseconds(label, value)\n if (ms < 1) throw new TypeError(`${label} must be at least 1 millisecond.`)\n return ms\n}\n\n/** Turns values into strings for the store, and back. */\nexport interface CacheSerializer {\n serialize(value: unknown): string\n deserialize(text: string): unknown\n}\n\n/** Details of a store failure, for `onError`. */\nexport interface CacheErrorInfo {\n readonly operation: StoreOperation\n /** Key of the store. */\n readonly key: string\n /** Name of the entry. */\n readonly entry: string\n}\n\n/** Something that happened to a value, for `onEvent`. */\nexport interface CacheEvent {\n /**\n * `hit`: fresh value; `miss`: loaded; `stale`: a value past its `ttl`\n * served, for the `reason`.\n */\n readonly type: 'hit' | 'miss' | 'stale'\n readonly reason?: 'revalidate' | 'error' | 'timeout' | 'backoff'\n /** Name of the entry. */\n readonly entry: string\n /** Key of the store. */\n readonly key: string\n}\n\n/** Options of `createCache()`: the policy is the default of the entries. */\nexport interface CacheOptions extends CachePolicy {\n store: CacheStore\n /** Prefix of every key, like the name of the app. */\n prefix?: string | undefined\n /** Defaults to JSON. */\n serializer?: CacheSerializer | undefined\n logger?: CacheLogger | undefined\n /**\n * Called for each store failure, besides the log: metrics, alerts. Its\n * own errors are logged and ignored.\n */\n onError?:\n | ((error: CacheStoreError, info: CacheErrorInfo) => void | Promise<void>)\n | undefined\n /**\n * When the store fails, behaves as a cache miss (reads) or ignores the\n * write, after logging it. Set false to throw a `CacheStoreError`.\n * Defaults to true.\n */\n failOpen?: boolean | undefined\n /** A store operation slower than this counts as a failure. */\n storeTimeout?: Duration | undefined\n /**\n * Called for each hit, miss and stale value served: metrics. Its errors\n * are logged and ignored.\n */\n onEvent?: ((event: CacheEvent) => void) | undefined\n}\n\n/** A cache, created by `createCache()`. */\nexport interface Cache {\n /**\n * The fresh value of the arguments, or undefined when missing, expired\n * or invalidated.\n */\n get<A extends readonly unknown[], T>(\n entry: CacheEntry<A, T>,\n ...args: A\n ): Promise<CacheValue<T> | undefined>\n /** Sets the value of the arguments (the last argument). */\n set<A extends readonly unknown[], T>(\n entry: CacheEntry<A, T>,\n ...argsAndValue: [...A, CacheValue<T>]\n ): Promise<void>\n /** Removes the value of the arguments. */\n delete<A extends readonly unknown[], T>(\n entry: CacheEntry<A, T>,\n ...args: A\n ): Promise<void>\n /**\n * The value of the arguments, loaded by the last argument when missing\n * and cached. Calls made while it loads share the load. Applies the\n * grace period, stale-while-revalidate and timeouts of the entry.\n */\n getOrSet<A extends readonly unknown[], R>(\n entry: CacheEntry<A, Untyped>,\n ...argsAndLoader: [...A, () => R | Promise<R>]\n ): Promise<R>\n getOrSet<A extends readonly unknown[], T>(\n entry: CacheEntry<A, T>,\n ...argsAndLoader: [...A, () => T | Promise<T>]\n ): Promise<T>\n /**\n * Expires every value with one of the tags (they can still be served by\n * the grace period when the loader fails).\n */\n invalidateTags(tags: readonly string[]): Promise<void>\n /** The same cache, logging with another logger. */\n withLogger(logger: CacheLogger): Cache\n}\n\nconst CACHE_OPTIONS = new Set([\n 'store',\n 'prefix',\n 'ttl',\n 'serializer',\n 'logger',\n 'onError',\n 'failOpen',\n 'storeTimeout',\n 'onEvent',\n ...POLICY,\n])\n\nconst jsonSerializer: CacheSerializer = {\n serialize: (value) => JSON.stringify(value),\n deserialize: (text) => JSON.parse(text) as unknown,\n}\n\n/**\n * Stored form of a value: `{ v, c (created), e (expires), d (data),\n * t (token of each tag) }`.\n */\ninterface StoredEntry {\n v: 1\n c: number\n e: number\n d: string\n t?: Record<string, string>\n}\n\nconst parseStored = (text: string): StoredEntry | undefined => {\n try {\n const stored = JSON.parse(text) as Partial<StoredEntry> | null\n if (\n stored?.v === 1 &&\n typeof stored.c === 'number' &&\n typeof stored.e === 'number' &&\n typeof stored.d === 'string' &&\n (stored.t === undefined ||\n (typeof stored.t === 'object' && stored.t !== null))\n ) {\n return stored as StoredEntry\n }\n } catch {\n // Not an entry of this cache.\n }\n return undefined\n}\n\nconst encoder = new TextEncoder()\n\n// Tokens of tags outlive the values that use them: 30 days at least.\nconst TAG_LIFETIME = 30 * 86_400_000\n// While another instance holds the lock, the value is read again so often.\nconst LOCK_POLL = 50\n\n/** SHA-256 of a text, in hexadecimal. */\nconst sha256 = async (text: string) => {\n const digest = await crypto.subtle.digest('SHA-256', encoder.encode(text))\n return [...new Uint8Array(digest)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/**\n * Creates a cache on a store. Reads and writes fail open by default: a\n * store failure is logged (and passed to `onError`), and the app keeps\n * working without cache.\n *\n * ```ts\n * const cache = createCache({ store: createMemoryStore(), prefix: 'app', ttl: '10m' })\n * const user = await cache.getOrSet(userCache, id, () => db.users.findById(id))\n * ```\n */\nexport const createCache = (options: CacheOptions): Cache => {\n const label = 'createCache()'\n if (typeof options !== 'object' || options === null) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n for (const key of Object.keys(options)) {\n if (!CACHE_OPTIONS.has(key)) {\n throw new TypeError(`${label}: unknown option \"${key}\".`)\n }\n }\n const {\n store,\n prefix,\n serializer = jsonSerializer,\n onError,\n onEvent,\n failOpen = true,\n } = options\n if (!isStore(store)) {\n throw new TypeError(\n `${label}: store must be a cache store (createMemoryStore(), createRedisStore()…).`\n )\n }\n if (prefix !== undefined && (typeof prefix !== 'string' || prefix === '')) {\n throw new TypeError(`${label}: prefix must be a non-empty string.`)\n }\n const defaults = policyOf(\n label,\n options as unknown as Record<string, unknown>\n )\n if (\n typeof serializer?.serialize !== 'function' ||\n typeof serializer.deserialize !== 'function'\n ) {\n throw new TypeError(\n `${label}: serializer must have serialize() and deserialize() methods.`\n )\n }\n if (onError !== undefined && typeof onError !== 'function') {\n throw new TypeError(`${label}: onError must be a function.`)\n }\n if (onEvent !== undefined && typeof onEvent !== 'function') {\n throw new TypeError(`${label}: onEvent must be a function.`)\n }\n if (typeof failOpen !== 'boolean') {\n throw new TypeError(`${label}: failOpen must be a boolean.`)\n }\n const storeTimeout =\n options.storeTimeout === undefined\n ? undefined\n : positive(`${label}: storeTimeout`, options.storeTimeout)\n const rootLogger =\n options.logger === undefined\n ? silentLogger\n : checkLogger(label, options.logger)\n\n // Shared by every logger of the cache: loads in progress, and the end of\n // the backoff of the keys whose loader failed.\n const loading = new Map<string, Promise<unknown>>()\n const backoffs = new Map<string, number>()\n\n const configOf = (method: string, entry: unknown) => {\n const config =\n typeof entry === 'object' && entry !== null\n ? entries.get(entry)\n : undefined\n if (!config) {\n throw new TypeError(\n `cache.${method}(): expected an entry created by defineCacheEntry().`\n )\n }\n return config\n }\n\n /** Policy of an entry, with the defaults of the cache. */\n const policyFor = (config: EntryConfig) => {\n const value = (option: keyof CachePolicy) =>\n config.policy[option] ?? defaults[option]\n return {\n ttl: value('ttl') ?? 3_600_000,\n grace: value('grace') ?? 0,\n graceBackoff: value('graceBackoff') ?? 10_000,\n staleWhileRevalidate: value('staleWhileRevalidate') ?? 0,\n timeout: value('timeout'),\n hardTimeout: value('hardTimeout'),\n lock: value('lock'),\n }\n }\n\n /** A key of the store, hashed when longer than the store accepts. */\n const fit = async (base: string, part: string) => {\n const key = `${base}:${part}`\n return encoder.encode(key).length > (store.maxKeyLength ?? Infinity)\n ? `${base}:#${await sha256(part)}`\n : key\n }\n\n /** Key of the store for the arguments of an entry. */\n const keyFor = async (config: EntryConfig, args: readonly unknown[]) => {\n const raw = config.key\n ? config.key(...args)\n : args.length === 1\n ? args[0]\n : args\n const base = [prefix, config.name, `v${config.version}`]\n .filter((item) => item !== undefined)\n .join(':')\n if (!config.key && args.length === 0) return base\n return fit(\n base,\n typeof raw === 'string' ? raw : keyOf(`Cache entry \"${config.name}\"`, raw)\n )\n }\n\n /** Checks the tags returned by an entry, or given to invalidate. */\n const checkTags = (label: string, tags: unknown) => {\n if (\n !Array.isArray(tags) ||\n !tags.every((tag) => typeof tag === 'string' && tag !== '')\n ) {\n throw new TypeError(\n `${label}: tags must be an array of non-empty strings.`\n )\n }\n return [...new Set(tags as string[])]\n }\n\n /** Keys of the tokens of tags (`_` cannot start the name of an entry). */\n const tagKeysOf = (tags: readonly string[]) => {\n const base = prefix === undefined ? '_tag' : `${prefix}:_tag`\n return Promise.all(\n tags.map(async (tag) => ({ tag, key: await fit(base, tag) }))\n )\n }\n\n const create = (logger: CacheLogger): Cache => {\n const describe = (error: unknown) =>\n error instanceof Error ? error.message : String(error)\n\n /** Runs a store operation: failures are logged, and fail open. */\n const guard = async <R>(\n operation: StoreOperation,\n key: string,\n entry: string,\n run: () => Promise<R>,\n fallback: R\n ): Promise<R> => {\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n const running = run()\n return storeTimeout === undefined\n ? await running\n : await Promise.race([\n running,\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () =>\n reject(\n new Error(`no answer within ${storeTimeout} milliseconds`)\n ),\n storeTimeout\n )\n }),\n ])\n } catch (cause) {\n const error = new CacheStoreError(store.name, operation, key, {\n cause,\n })\n const data = {\n store: store.name,\n operation,\n key,\n entry,\n error: error.message,\n }\n // A failed deletion or invalidation may leave outdated values.\n if (operation === 'delete' || operation === 'invalidate') {\n logger.error(data, 'Cache store failed')\n } else {\n logger.warn(data, 'Cache store failed')\n }\n if (onError) {\n try {\n await onError(error, { operation, key, entry })\n } catch (hookError) {\n logger.error(\n { error: describe(hookError) },\n 'Cache onError hook failed'\n )\n }\n }\n if (!failOpen) throw error\n return fallback\n } finally {\n clearTimeout(timer)\n }\n }\n\n /** Reports an event to the log (debug) and `onEvent`. */\n const report = (event: CacheEvent) => {\n const { type, reason, entry, key } = event\n logger.debug?.(\n { entry, key, ...(reason ? { reason } : {}) },\n type === 'hit'\n ? 'Cache hit'\n : type === 'miss'\n ? 'Cache miss'\n : 'Cache served a stale value'\n )\n if (!onEvent) return\n try {\n onEvent(event)\n } catch (error) {\n logger.error({ error: describe(error) }, 'Cache onEvent hook failed')\n }\n }\n\n /**\n * Current tokens of tags (undefined for a missing tag), or undefined\n * when the store cannot read them.\n */\n const tokensOf = async (\n entry: string,\n tagKeys: readonly { tag: string; key: string }[]\n ) => {\n const keys = tagKeys.map(({ key }) => key)\n const values = await guard<(string | undefined)[] | undefined>(\n 'get',\n keys.join(' '),\n entry,\n () =>\n store.getMany\n ? store.getMany(keys)\n : Promise.all(keys.map((key) => store.get(key))),\n undefined\n )\n return values?.map((token, index) => ({\n tag: (tagKeys[index] as { tag: string }).tag,\n token,\n }))\n }\n\n /** Deserializes and validates a value: `{ value }`, or undefined. */\n const decode = async (config: EntryConfig, key: string, data: string) => {\n let value: unknown\n try {\n value = serializer.deserialize(data)\n } catch (error) {\n logger.warn(\n { entry: config.name, key, error: describe(error) },\n 'Cache value cannot be deserialized'\n )\n return undefined\n }\n if (!config.schema) return { value }\n const result = await config.schema.safeParseAsync(value)\n return result.success ? { value: result.data } : undefined\n }\n\n /**\n * Reads a stored value: its value, expiry, and whether its tags are\n * still valid. Undefined when missing, invalid, or past every stale\n * window.\n */\n const lookup = async (\n config: EntryConfig,\n policy: ReturnType<typeof policyFor>,\n key: string\n ) => {\n const text = await guard(\n 'get',\n key,\n config.name,\n () => store.get(key),\n undefined\n )\n if (text === undefined) return undefined\n const stored = parseStored(text)\n const now = Date.now()\n if (\n !stored ||\n stored.e + Math.max(policy.grace, policy.staleWhileRevalidate) <= now\n ) {\n return undefined\n }\n const decoded = await decode(config, key, stored.d)\n if (!decoded) return undefined\n const tags = Object.entries(stored.t ?? {})\n let tagsValid = true\n if (tags.length > 0) {\n const current = await tokensOf(\n config.name,\n await tagKeysOf(tags.map(([tag]) => tag))\n )\n // Unreadable tokens: the value may have been invalidated.\n tagsValid =\n current?.every(({ tag, token }) => token === stored.t?.[tag]) ?? false\n }\n return {\n value: decoded.value,\n expiresAt: stored.e,\n fresh: stored.e > now && tagsValid,\n tagsValid,\n }\n }\n\n /**\n * Writes a value, after checking that it reads back: returns the value\n * as read later, or undefined when it cannot be cached.\n */\n const write = async (\n config: EntryConfig,\n key: string,\n args: readonly unknown[],\n value: unknown\n ): Promise<{ value: unknown } | undefined> => {\n if (value === undefined) return undefined\n let data: string\n try {\n data = serializer.serialize(value)\n if (typeof data !== 'string') throw new TypeError('not a string')\n } catch (error) {\n logger.warn(\n { entry: config.name, key, error: describe(error) },\n 'Cache value cannot be serialized'\n )\n return undefined\n }\n const checked = config.schema\n ? await decode(config, key, data)\n : { value }\n if (!checked) {\n logger.warn(\n { entry: config.name, key },\n 'Cache value does not match the schema of the entry: not cached'\n )\n return undefined\n }\n const policy = policyFor(config)\n const lifetime =\n policy.ttl + Math.max(policy.grace, policy.staleWhileRevalidate)\n const now = Date.now()\n const stored: StoredEntry = {\n v: 1,\n c: now,\n e: now + policy.ttl,\n d: data,\n }\n if (config.tags) {\n const tags = checkTags(\n `defineCacheEntry(\"${config.name}\")`,\n config.tags(...args)\n )\n if (tags.length > 0) {\n const tagKeys = await tagKeysOf(tags)\n const current = await tokensOf(config.name, tagKeys)\n // Unreadable tokens: creating new ones would invalidate every\n // value of these tags, so the value is not cached.\n if (!current) return checked\n stored.t = {}\n for (const [index, { tag, token }] of current.entries()) {\n if (token !== undefined) {\n stored.t[tag] = token\n continue\n }\n // First value with this tag: its token is created.\n const created = crypto.randomUUID()\n const tagKey = (tagKeys[index] as { key: string }).key\n await guard(\n 'set',\n tagKey,\n config.name,\n () =>\n store.set(tagKey, created, Math.max(lifetime, TAG_LIFETIME)),\n undefined\n )\n stored.t[tag] = created\n }\n }\n }\n await guard(\n 'set',\n key,\n config.name,\n () => store.set(key, JSON.stringify(stored), lifetime),\n undefined\n )\n return checked\n }\n\n /**\n * Loads and writes a value, once per key at a time, and once for every\n * instance with the `lock` option.\n */\n const load = (\n config: EntryConfig,\n key: string,\n args: readonly unknown[],\n loader: () => unknown\n ) => {\n const running = loading.get(key)\n if (running) return running\n const loaded = (async () => {\n const policy = policyFor(config)\n const lockKey = `${key}:lock`\n let token: string | undefined\n if (policy.lock !== undefined && store.lock) {\n const lock = store.lock.bind(store)\n const ttl = policy.lock\n // null: the store failed, load without lock.\n const taken = await guard<string | undefined | null>(\n 'lock',\n lockKey,\n config.name,\n () => lock(lockKey, ttl),\n null\n )\n if (taken === undefined) {\n // Another instance loads it: wait for its value.\n const until = Date.now() + ttl\n while (Date.now() < until) {\n await new Promise((resolve) => setTimeout(resolve, LOCK_POLL))\n const found = await lookup(config, policy, key)\n if (found?.fresh) return found.value\n }\n logger.warn(\n { entry: config.name, key, lock: ttl },\n 'Cache lock not released in time: loading anyway'\n )\n } else if (taken !== null) {\n token = taken\n }\n }\n try {\n const value: unknown = await loader()\n const written = await write(config, key, args, value)\n backoffs.delete(key)\n return written ? written.value : value\n } finally {\n if (token !== undefined && store.unlock) {\n const unlock = store.unlock.bind(store)\n const held = token\n await guard(\n 'unlock',\n lockKey,\n config.name,\n () => unlock(lockKey, held),\n undefined\n )\n }\n }\n })()\n loading.set(key, loaded)\n void loaded.then(\n () => loading.delete(key),\n () => loading.delete(key)\n )\n return loaded\n }\n\n /** Logs the failure of a load that nobody waits for anymore. */\n const inBackground = (\n config: EntryConfig,\n key: string,\n loaded: Promise<unknown>\n ) => {\n loaded.catch((error: unknown) => {\n logger.warn(\n { entry: config.name, key, error: describe(error) },\n 'Cache refresh failed'\n )\n })\n }\n\n /** Rejects after `ms` (never when undefined), until cleared. */\n const deadline = (ms: number | undefined, error: () => unknown) => {\n let timer: ReturnType<typeof setTimeout> | undefined\n const promise = new Promise<never>((_, reject) => {\n if (ms !== undefined) timer = setTimeout(() => reject(error()), ms)\n })\n return { promise, clear: () => clearTimeout(timer) }\n }\n\n const cache: Cache = {\n get: (async (entry: unknown, ...args: unknown[]) => {\n const config = configOf('get', entry)\n const key = await keyFor(config, args)\n const found = await lookup(config, policyFor(config), key)\n report({ type: found?.fresh ? 'hit' : 'miss', entry: config.name, key })\n return found?.fresh ? found.value : undefined\n }) as Cache['get'],\n\n set: (async (entry: unknown, ...argsAndValue: unknown[]) => {\n const config = configOf('set', entry)\n const args = argsAndValue.slice(0, -1)\n const key = await keyFor(config, args)\n await write(config, key, args, argsAndValue.at(-1))\n }) as Cache['set'],\n\n delete: (async (entry: unknown, ...args: unknown[]) => {\n const config = configOf('delete', entry)\n const key = await keyFor(config, args)\n await guard(\n 'delete',\n key,\n config.name,\n () => store.delete(key),\n undefined\n )\n }) as Cache['delete'],\n\n getOrSet: (async (entry: unknown, ...argsAndLoader: unknown[]) => {\n const config = configOf('getOrSet', entry)\n const loader = argsAndLoader.at(-1)\n if (typeof loader !== 'function') {\n throw new TypeError(\n 'cache.getOrSet(): the last argument must be the function that loads the value.'\n )\n }\n const args = argsAndLoader.slice(0, -1)\n const key = await keyFor(config, args)\n const policy = policyFor(config)\n const found = await lookup(config, policy, key)\n const event = { entry: config.name, key }\n if (found?.fresh) {\n report({ type: 'hit', ...event })\n return found.value\n }\n const now = Date.now()\n\n // Stale-while-revalidate: serve, and refresh in the background.\n if (\n found?.tagsValid &&\n found.expiresAt + policy.staleWhileRevalidate > now\n ) {\n report({ type: 'stale', reason: 'revalidate', ...event })\n inBackground(\n config,\n key,\n load(config, key, args, loader as () => unknown)\n )\n return found.value\n }\n\n // Grace period: stale value served if the loader fails. Without it,\n // an invalidated value is never served.\n const stale =\n found && policy.grace > 0 && found.expiresAt + policy.grace > now\n ? found\n : undefined\n if (stale && (backoffs.get(key) ?? 0) > now) {\n report({ type: 'stale', reason: 'backoff', ...event })\n return stale.value\n }\n\n report({ type: 'miss', ...event })\n const loaded = load(config, key, args, loader as () => unknown)\n const slow = Symbol('slow')\n const soft = deadline(stale ? policy.timeout : undefined, () => slow)\n const hard = deadline(\n policy.hardTimeout,\n () =>\n new CacheTimeoutError(\n config.name,\n key,\n policy.hardTimeout as number\n )\n )\n try {\n return await Promise.race([loaded, soft.promise, hard.promise])\n } catch (error) {\n // The load goes on, and caches its value.\n if (error === slow || error instanceof CacheTimeoutError) {\n inBackground(config, key, loaded)\n }\n if (!stale) throw error\n if (error === slow) {\n logger.warn(\n { ...event, timeout: policy.timeout },\n 'Cache served a stale value: the loader is slow'\n )\n report({ type: 'stale', reason: 'timeout', ...event })\n return stale.value\n }\n backoffs.set(key, Date.now() + policy.graceBackoff)\n logger.warn(\n { ...event, error: describe(error) },\n 'Cache served a stale value: the loader failed'\n )\n report({ type: 'stale', reason: 'error', ...event })\n return stale.value\n } finally {\n soft.clear()\n hard.clear()\n }\n }) as Cache['getOrSet'],\n\n invalidateTags: async (tags) => {\n const tagKeys = await tagKeysOf(\n checkTags('cache.invalidateTags()', tags)\n )\n await Promise.all(\n tagKeys.map(({ tag, key }) =>\n guard(\n 'invalidate',\n key,\n `tag:${tag}`,\n () => store.set(key, crypto.randomUUID(), TAG_LIFETIME),\n undefined\n )\n )\n )\n logger.debug?.({ tags }, 'Cache tags invalidated')\n },\n\n withLogger: (other) => create(checkLogger('withLogger()', other)),\n }\n return Object.freeze(cache)\n }\n\n return create(rootLogger)\n}\n"],"mappings":"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","names":["entries","label"]}
1
+ {"version":3,"sources":["../src/logger.ts","../src/store.ts","../src/cache.ts"],"sourcesContent":["/**\n * Logger of the cache. A Dotpress or Pino logger fits; `cachePlugin` uses\n * the logger of the request.\n */\nexport interface CacheLogger {\n debug?(data: object, message: string): void\n info(data: object, message: string): void\n warn(data: object, message: string): void\n error(data: object, message: string): void\n}\n\nconst noop = () => undefined\n\n/** Logger that discards everything. */\nexport const silentLogger: CacheLogger = {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n}\n\n/** Checks a logger option. */\nexport const checkLogger = (label: string, logger: unknown) => {\n for (const level of ['info', 'warn', 'error'] as const) {\n if (\n typeof (logger as Record<string, unknown> | null)?.[level] !== 'function'\n ) {\n throw new TypeError(\n `${label}: logger must have info(), warn() and error() methods.`\n )\n }\n }\n return logger as CacheLogger\n}\n","/**\n * Where the cache keeps its entries: Redis, Upstash, Cloudflare KV,\n * memory… Values are strings, serialized by the cache.\n */\nexport interface CacheStore {\n /** Name of the store, in logs and errors. */\n readonly name: string\n /**\n * Longest key accepted, in bytes (UTF-8). Longer keys are hashed by the\n * cache. Undefined without limit.\n */\n readonly maxKeyLength?: number | undefined\n /** Value of a key, or undefined when it is missing or expired. */\n get(key: string): Promise<string | undefined>\n /**\n * Values of several keys, in their order: one round trip for the tags of\n * an entry. Optional: the cache calls `get` for each key without it.\n */\n getMany?(keys: readonly string[]): Promise<(string | undefined)[]>\n /** Sets a value that expires after `ttl` milliseconds (at least 1). */\n set(key: string, value: string, ttl: number): Promise<void>\n /** Removes a key; missing keys are ignored. */\n delete(key: string): Promise<void>\n /**\n * Takes a lock for `ttl` milliseconds: returns its token, or undefined\n * when another holder has it. Optional: the `lock` option of the cache\n * needs it.\n */\n lock?(key: string, ttl: number): Promise<string | undefined>\n /** Releases a lock, if the token still holds it. */\n unlock?(key: string, token: string): Promise<void>\n}\n\n/** Checks a store option. */\nexport const isStore = (value: unknown): value is CacheStore => {\n const store = value as Partial<CacheStore> | null\n return (\n typeof store?.name === 'string' &&\n typeof store.get === 'function' &&\n typeof store.set === 'function' &&\n typeof store.delete === 'function'\n )\n}\n\n/** Options of `createMemoryStore()`. */\nexport interface MemoryStoreOptions {\n /**\n * Entries kept at most: the least recently used ones are removed\n * first. Defaults to 10,000.\n */\n maxEntries?: number | undefined\n}\n\n/** Store in the memory of the process: for one instance, and for tests. */\nexport interface MemoryStore extends CacheStore {\n /** Number of entries, expired ones included until they are read. */\n readonly size: number\n /** Removes every entry. */\n clear(): void\n}\n\n/**\n * Creates a store in the memory of the process, least recently used\n * entries removed first. Each instance has its own: use a shared store\n * (Redis…) for several instances.\n */\nexport const createMemoryStore = (\n options: MemoryStoreOptions = {}\n): MemoryStore => {\n const label = 'createMemoryStore()'\n if (typeof options !== 'object' || options === null) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n for (const key of Object.keys(options)) {\n if (key !== 'maxEntries') {\n throw new TypeError(`${label}: unknown option \"${key}\".`)\n }\n }\n const { maxEntries = 10_000 } = options\n if (!Number.isInteger(maxEntries) || maxEntries < 1) {\n throw new TypeError(`${label}: maxEntries must be a positive integer.`)\n }\n\n // Insertion order of a Map: the first entry is the least recently used.\n const entries = new Map<string, { value: string; expiresAt: number }>()\n const locks = new Map<string, { token: string; expiresAt: number }>()\n\n const read = (key: string) => {\n const entry = entries.get(key)\n if (!entry) return undefined\n entries.delete(key)\n if (entry.expiresAt <= Date.now()) return undefined\n entries.set(key, entry)\n return entry.value\n }\n\n return Object.freeze({\n name: 'memory',\n get size() {\n return entries.size\n },\n get: async (key: string) => read(key),\n getMany: async (keys: readonly string[]) => keys.map(read),\n set: async (key: string, value: string, ttl: number) => {\n entries.delete(key)\n entries.set(key, { value, expiresAt: Date.now() + ttl })\n for (const oldest of entries.keys()) {\n if (entries.size <= maxEntries) break\n entries.delete(oldest)\n }\n },\n delete: async (key: string) => {\n entries.delete(key)\n },\n lock: async (key: string, ttl: number) => {\n const held = locks.get(key)\n if (held && held.expiresAt > Date.now()) return undefined\n const token = crypto.randomUUID()\n locks.set(key, { token, expiresAt: Date.now() + ttl })\n return token\n },\n unlock: async (key: string, token: string) => {\n if (locks.get(key)?.token === token) locks.delete(key)\n },\n clear: () => {\n entries.clear()\n locks.clear()\n },\n })\n}\n","import { toMilliseconds, type Duration } from './duration.js'\nimport {\n CacheStoreError,\n CacheTimeoutError,\n type StoreOperation,\n} from './errors.js'\nimport { keyOf } from './keys.js'\nimport { checkLogger, silentLogger, type CacheLogger } from './logger.js'\nimport { isStore, type CacheStore } from './store.js'\n\n/**\n * A schema that validates cached values when they are read: a Zod schema,\n * or anything with the same `safeParseAsync()`.\n */\nexport interface CacheSchema<T> {\n safeParseAsync(\n value: unknown\n ): Promise<{ success: true; data: T } | { success: false }>\n}\n\ndeclare const untyped: unique symbol\n/** Value type of an entry without schema: given by the loader. */\nexport interface Untyped {\n readonly [untyped]: true\n}\n\ndeclare const entryTypes: unique symbol\n\n/** A kind of cached value, created by `defineCacheEntry()`. */\nexport interface CacheEntry<A extends readonly unknown[], T> {\n readonly name: string\n readonly version: number\n /** Types only. */\n readonly [entryTypes]?: { args: A; value: (value: T) => T }\n}\n\n/** Value of an entry: `unknown` without schema. */\nexport type CacheValue<T> = T extends Untyped ? unknown : T\n\n/**\n * Lifetime and resilience of values: options of an entry, or defaults of\n * the cache.\n */\nexport interface CachePolicy {\n /** Lifetime of the values. Defaults to 1 hour. */\n ttl?: Duration | undefined\n /**\n * Stale-if-error: how long after expiry a value can still be served when\n * the loader fails (or times out). Defaults to 0 (disabled).\n */\n grace?: Duration | undefined\n /**\n * After a failed load served from the grace period, the loader is not\n * called again before this delay: the stale value is served. Defaults to\n * 10 seconds.\n */\n graceBackoff?: Duration | undefined\n /**\n * How long after expiry a value is still served while a background load\n * refreshes it. Defaults to 0 (disabled).\n */\n staleWhileRevalidate?: Duration | undefined\n /**\n * Soft timeout: a load slower than this serves the stale value of the\n * grace period, if any, and goes on in the background.\n */\n timeout?: Duration | undefined\n /**\n * Hard timeout: a load slower than this fails (`CacheTimeoutError`), or\n * serves the stale value of the grace period, if any. It goes on in the\n * background.\n */\n hardTimeout?: Duration | undefined\n /**\n * Distributed single flight: before loading a missing value, takes a\n * lock in the store (Redis…) for at most this duration; the other\n * instances wait for the value meanwhile, then load it themselves.\n * Ignored by stores without locks. Disabled by default.\n */\n lock?: Duration | undefined\n}\n\n/** Options of `defineCacheEntry()`. */\nexport interface CacheEntryOptions<\n A extends readonly unknown[],\n T,\n> extends CachePolicy {\n /**\n * Name of the entry, in keys and logs: letters, digits, `.`, `_` and\n * `-`.\n */\n name: string\n /**\n * Key of the arguments: a string, a number, or a value compared like the\n * arguments of a memo. Defaults to the argument (or the arguments).\n */\n key?: ((...args: A) => unknown) | undefined\n /**\n * Validates cached values when they are read (entries from an older\n * version of the app) and written. JSON turns dates into strings: use\n * `z.coerce.date()`.\n */\n schema?: CacheSchema<T> | undefined\n /**\n * Version of the values, in the key: increase it when their shape\n * changes, to ignore the values written before. Defaults to 1.\n */\n version?: number | undefined\n /**\n * Tags of the arguments: `invalidateTags()` expires every value with one\n * of them.\n */\n tags?: ((...args: A) => readonly string[]) | undefined\n}\n\n/** A policy in milliseconds; undefined when not set. */\ntype Policy = { [K in keyof CachePolicy]: number | undefined }\n\ninterface EntryConfig {\n name: string\n version: number\n key: ((...args: readonly unknown[]) => unknown) | undefined\n schema: CacheSchema<unknown> | undefined\n tags: ((...args: readonly unknown[]) => readonly string[]) | undefined\n policy: Policy\n}\n\nconst entries = new WeakMap<object, EntryConfig>()\n\nconst NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/\nconst POLICY = [\n 'ttl',\n 'grace',\n 'graceBackoff',\n 'staleWhileRevalidate',\n 'timeout',\n 'hardTimeout',\n 'lock',\n] as const\nconst ENTRY_OPTIONS = new Set([\n 'name',\n 'key',\n 'schema',\n 'version',\n 'tags',\n ...POLICY,\n])\n\n/** Reads the durations of a policy (`ttl` and timeouts at least 1 ms). */\nconst policyOf = (label: string, values: Record<string, unknown>): Policy => {\n const policy: Policy = {}\n for (const option of POLICY) {\n const value = values[option]\n if (value === undefined) continue\n policy[option] =\n option === 'ttl' ||\n option === 'timeout' ||\n option === 'hardTimeout' ||\n option === 'lock'\n ? positive(`${label}: ${option}`, value)\n : toMilliseconds(`${label}: ${option}`, value)\n }\n return policy\n}\n\n/**\n * Defines a kind of cached value: its key, its lifetime, and optionally\n * its schema.\n *\n * ```ts\n * export const userCache = defineCacheEntry({\n * name: 'user',\n * key: (id: string) => id,\n * schema: userSchema,\n * ttl: '1h',\n * })\n * ```\n */\nexport function defineCacheEntry<A extends readonly unknown[], T>(\n options: CacheEntryOptions<A, T> & { schema: CacheSchema<T> }\n): CacheEntry<A, T>\nexport function defineCacheEntry<A extends readonly unknown[]>(\n options: CacheEntryOptions<A, never> & { schema?: undefined }\n): CacheEntry<A, Untyped>\nexport function defineCacheEntry<A extends readonly unknown[], T>(\n options: CacheEntryOptions<A, T>\n): CacheEntry<A, T>\nexport function defineCacheEntry(options: unknown): {\n readonly name: string\n readonly version: number\n} {\n const label = 'defineCacheEntry()'\n if (typeof options !== 'object' || options === null) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n const values = options as Record<string, unknown>\n const { name, key, schema, tags, version = 1 } = values\n if (typeof name !== 'string' || !NAME.test(name)) {\n throw new TypeError(\n `${label}: name must have 1 to 128 letters, digits, \".\", \"_\" or \"-\", starting with a letter or a digit.`\n )\n }\n const named = `defineCacheEntry(\"${name}\")`\n for (const option of Object.keys(values)) {\n if (!ENTRY_OPTIONS.has(option)) {\n throw new TypeError(`${named}: unknown option \"${option}\".`)\n }\n }\n if (key !== undefined && typeof key !== 'function') {\n throw new TypeError(`${named}: key must be a function.`)\n }\n if (\n schema !== undefined &&\n typeof (schema as Partial<CacheSchema<unknown>> | null)?.safeParseAsync !==\n 'function'\n ) {\n throw new TypeError(\n `${named}: schema must have a safeParseAsync() method, like a Zod schema.`\n )\n }\n if (!Number.isInteger(version) || (version as number) < 0) {\n throw new TypeError(`${named}: version must be a non-negative integer.`)\n }\n if (tags !== undefined && typeof tags !== 'function') {\n throw new TypeError(`${named}: tags must be a function.`)\n }\n\n const entry = Object.freeze({ name, version: version as number })\n entries.set(entry, {\n name,\n version: version as number,\n key: key as EntryConfig['key'],\n schema: schema as CacheSchema<unknown> | undefined,\n tags: tags as EntryConfig['tags'],\n policy: policyOf(named, values),\n })\n return entry\n}\n\nconst positive = (label: string, value: unknown) => {\n const ms = toMilliseconds(label, value)\n if (ms < 1) throw new TypeError(`${label} must be at least 1 millisecond.`)\n return ms\n}\n\n/** Turns values into strings for the store, and back. */\nexport interface CacheSerializer {\n serialize(value: unknown): string\n deserialize(text: string): unknown\n}\n\n/** Details of a store failure, for `onError`. */\nexport interface CacheErrorInfo {\n readonly operation: StoreOperation\n /** Key of the store. */\n readonly key: string\n /** Name of the entry. */\n readonly entry: string\n}\n\n/** Something that happened to a value, for `onEvent`. */\nexport interface CacheEvent {\n /**\n * `hit`: fresh value; `miss`: loaded; `stale`: a value past its `ttl`\n * served, for the `reason`.\n */\n readonly type: 'hit' | 'miss' | 'stale'\n readonly reason?: 'revalidate' | 'error' | 'timeout' | 'backoff'\n /** Name of the entry. */\n readonly entry: string\n /** Key of the store. */\n readonly key: string\n}\n\n/** Options of `createCache()`: the policy is the default of the entries. */\nexport interface CacheOptions extends CachePolicy {\n store: CacheStore\n /** Prefix of every key, like the name of the app. */\n prefix?: string | undefined\n /** Defaults to JSON. */\n serializer?: CacheSerializer | undefined\n logger?: CacheLogger | undefined\n /**\n * Called for each store failure, besides the log: metrics, alerts. Its\n * own errors are logged and ignored.\n */\n onError?:\n | ((error: CacheStoreError, info: CacheErrorInfo) => void | Promise<void>)\n | undefined\n /**\n * When the store fails, behaves as a cache miss (reads) or ignores the\n * write, after logging it. Set false to throw a `CacheStoreError`.\n * Defaults to true.\n */\n failOpen?: boolean | undefined\n /** A store operation slower than this counts as a failure. */\n storeTimeout?: Duration | undefined\n /**\n * Called for each hit, miss and stale value served: metrics. Its errors\n * are logged and ignored.\n */\n onEvent?: ((event: CacheEvent) => void) | undefined\n}\n\n/** A cache, created by `createCache()`. */\nexport interface Cache {\n /**\n * The fresh value of the arguments, or undefined when missing, expired\n * or invalidated.\n */\n get<A extends readonly unknown[], T>(\n entry: CacheEntry<A, T>,\n ...args: A\n ): Promise<CacheValue<T> | undefined>\n /** Sets the value of the arguments (the last argument). */\n set<A extends readonly unknown[], T>(\n entry: CacheEntry<A, T>,\n ...argsAndValue: [...A, CacheValue<T>]\n ): Promise<void>\n /** Removes the value of the arguments. */\n delete<A extends readonly unknown[], T>(\n entry: CacheEntry<A, T>,\n ...args: A\n ): Promise<void>\n /**\n * The value of the arguments, loaded by the last argument when missing\n * and cached. Calls made while it loads share the load. Applies the\n * grace period, stale-while-revalidate and timeouts of the entry.\n *\n * A loader may return `undefined` (not found): it is not cached, and\n * `getOrSet` returns it.\n */\n getOrSet<A extends readonly unknown[], R>(\n entry: CacheEntry<A, Untyped>,\n ...argsAndLoader: [...A, () => R | Promise<R>]\n ): Promise<R>\n getOrSet<A extends readonly unknown[], T, R extends T | undefined = T>(\n entry: CacheEntry<A, T>,\n ...argsAndLoader: [...A, () => R | Promise<R>]\n ): Promise<[R] extends [T] ? T : T | undefined>\n /**\n * Expires every value with one of the tags (they can still be served by\n * the grace period when the loader fails).\n */\n invalidateTags(tags: readonly string[]): Promise<void>\n /** The same cache, logging with another logger. */\n withLogger(logger: CacheLogger): Cache\n}\n\nconst CACHE_OPTIONS = new Set([\n 'store',\n 'prefix',\n 'ttl',\n 'serializer',\n 'logger',\n 'onError',\n 'failOpen',\n 'storeTimeout',\n 'onEvent',\n ...POLICY,\n])\n\nconst jsonSerializer: CacheSerializer = {\n serialize: (value) => JSON.stringify(value),\n deserialize: (text) => JSON.parse(text) as unknown,\n}\n\n/**\n * Stored form of a value: `{ v, c (created), e (expires), d (data),\n * t (token of each tag) }`.\n */\ninterface StoredEntry {\n v: 1\n c: number\n e: number\n d: string\n t?: Record<string, string>\n}\n\nconst parseStored = (text: string): StoredEntry | undefined => {\n try {\n const stored = JSON.parse(text) as Partial<StoredEntry> | null\n if (\n stored?.v === 1 &&\n typeof stored.c === 'number' &&\n typeof stored.e === 'number' &&\n typeof stored.d === 'string' &&\n (stored.t === undefined ||\n (typeof stored.t === 'object' && stored.t !== null))\n ) {\n return stored as StoredEntry\n }\n } catch {\n // Not an entry of this cache.\n }\n return undefined\n}\n\nconst encoder = new TextEncoder()\n\n// Tokens of tags outlive the values that use them: 30 days at least.\nconst TAG_LIFETIME = 30 * 86_400_000\n// While another instance holds the lock, the value is read again so often.\nconst LOCK_POLL = 50\n\n/** SHA-256 of a text, in hexadecimal. */\nconst sha256 = async (text: string) => {\n const digest = await crypto.subtle.digest('SHA-256', encoder.encode(text))\n return [...new Uint8Array(digest)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/**\n * Creates a cache on a store. Reads and writes fail open by default: a\n * store failure is logged (and passed to `onError`), and the app keeps\n * working without cache.\n *\n * ```ts\n * const cache = createCache({ store: createMemoryStore(), prefix: 'app', ttl: '10m' })\n * const user = await cache.getOrSet(userCache, id, () => db.users.findById(id))\n * ```\n */\nexport const createCache = (options: CacheOptions): Cache => {\n const label = 'createCache()'\n if (typeof options !== 'object' || options === null) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n for (const key of Object.keys(options)) {\n if (!CACHE_OPTIONS.has(key)) {\n throw new TypeError(`${label}: unknown option \"${key}\".`)\n }\n }\n const {\n store,\n prefix,\n serializer = jsonSerializer,\n onError,\n onEvent,\n failOpen = true,\n } = options\n if (!isStore(store)) {\n throw new TypeError(\n `${label}: store must be a cache store (createMemoryStore(), createRedisStore()…).`\n )\n }\n if (prefix !== undefined && (typeof prefix !== 'string' || prefix === '')) {\n throw new TypeError(`${label}: prefix must be a non-empty string.`)\n }\n const defaults = policyOf(\n label,\n options as unknown as Record<string, unknown>\n )\n if (\n typeof serializer?.serialize !== 'function' ||\n typeof serializer.deserialize !== 'function'\n ) {\n throw new TypeError(\n `${label}: serializer must have serialize() and deserialize() methods.`\n )\n }\n if (onError !== undefined && typeof onError !== 'function') {\n throw new TypeError(`${label}: onError must be a function.`)\n }\n if (onEvent !== undefined && typeof onEvent !== 'function') {\n throw new TypeError(`${label}: onEvent must be a function.`)\n }\n if (typeof failOpen !== 'boolean') {\n throw new TypeError(`${label}: failOpen must be a boolean.`)\n }\n const storeTimeout =\n options.storeTimeout === undefined\n ? undefined\n : positive(`${label}: storeTimeout`, options.storeTimeout)\n const rootLogger =\n options.logger === undefined\n ? silentLogger\n : checkLogger(label, options.logger)\n\n // Shared by every logger of the cache: loads in progress, and the end of\n // the backoff of the keys whose loader failed.\n const loading = new Map<string, Promise<unknown>>()\n const backoffs = new Map<string, number>()\n\n const configOf = (method: string, entry: unknown) => {\n const config =\n typeof entry === 'object' && entry !== null\n ? entries.get(entry)\n : undefined\n if (!config) {\n throw new TypeError(\n `cache.${method}(): expected an entry created by defineCacheEntry().`\n )\n }\n return config\n }\n\n /** Policy of an entry, with the defaults of the cache. */\n const policyFor = (config: EntryConfig) => {\n const value = (option: keyof CachePolicy) =>\n config.policy[option] ?? defaults[option]\n return {\n ttl: value('ttl') ?? 3_600_000,\n grace: value('grace') ?? 0,\n graceBackoff: value('graceBackoff') ?? 10_000,\n staleWhileRevalidate: value('staleWhileRevalidate') ?? 0,\n timeout: value('timeout'),\n hardTimeout: value('hardTimeout'),\n lock: value('lock'),\n }\n }\n\n /** A key of the store, hashed when longer than the store accepts. */\n const fit = async (base: string, part: string) => {\n const key = `${base}:${part}`\n return encoder.encode(key).length > (store.maxKeyLength ?? Infinity)\n ? `${base}:#${await sha256(part)}`\n : key\n }\n\n /** Key of the store for the arguments of an entry. */\n const keyFor = async (config: EntryConfig, args: readonly unknown[]) => {\n const raw = config.key\n ? config.key(...args)\n : args.length === 1\n ? args[0]\n : args\n const base = [prefix, config.name, `v${config.version}`]\n .filter((item) => item !== undefined)\n .join(':')\n if (!config.key && args.length === 0) return base\n return fit(\n base,\n typeof raw === 'string' ? raw : keyOf(`Cache entry \"${config.name}\"`, raw)\n )\n }\n\n /** Checks the tags returned by an entry, or given to invalidate. */\n const checkTags = (label: string, tags: unknown) => {\n if (\n !Array.isArray(tags) ||\n !tags.every((tag) => typeof tag === 'string' && tag !== '')\n ) {\n throw new TypeError(\n `${label}: tags must be an array of non-empty strings.`\n )\n }\n return [...new Set(tags as string[])]\n }\n\n /** Keys of the tokens of tags (`_` cannot start the name of an entry). */\n const tagKeysOf = (tags: readonly string[]) => {\n const base = prefix === undefined ? '_tag' : `${prefix}:_tag`\n return Promise.all(\n tags.map(async (tag) => ({ tag, key: await fit(base, tag) }))\n )\n }\n\n const create = (logger: CacheLogger): Cache => {\n const describe = (error: unknown) =>\n error instanceof Error ? error.message : String(error)\n\n /** Runs a store operation: failures are logged, and fail open. */\n const guard = async <R>(\n operation: StoreOperation,\n key: string,\n entry: string,\n run: () => Promise<R>,\n fallback: R\n ): Promise<R> => {\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n const running = run()\n return storeTimeout === undefined\n ? await running\n : await Promise.race([\n running,\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () =>\n reject(\n new Error(`no answer within ${storeTimeout} milliseconds`)\n ),\n storeTimeout\n )\n }),\n ])\n } catch (cause) {\n const error = new CacheStoreError(store.name, operation, key, {\n cause,\n })\n const data = {\n store: store.name,\n operation,\n key,\n entry,\n error: error.message,\n }\n // A failed deletion or invalidation may leave outdated values.\n if (operation === 'delete' || operation === 'invalidate') {\n logger.error(data, 'Cache store failed')\n } else {\n logger.warn(data, 'Cache store failed')\n }\n if (onError) {\n try {\n await onError(error, { operation, key, entry })\n } catch (hookError) {\n logger.error(\n { error: describe(hookError) },\n 'Cache onError hook failed'\n )\n }\n }\n if (!failOpen) throw error\n return fallback\n } finally {\n clearTimeout(timer)\n }\n }\n\n /** Reports an event to the log (debug) and `onEvent`. */\n const report = (event: CacheEvent) => {\n const { type, reason, entry, key } = event\n logger.debug?.(\n { entry, key, ...(reason ? { reason } : {}) },\n type === 'hit'\n ? 'Cache hit'\n : type === 'miss'\n ? 'Cache miss'\n : 'Cache served a stale value'\n )\n if (!onEvent) return\n try {\n onEvent(event)\n } catch (error) {\n logger.error({ error: describe(error) }, 'Cache onEvent hook failed')\n }\n }\n\n /**\n * Current tokens of tags (undefined for a missing tag), or undefined\n * when the store cannot read them.\n */\n const tokensOf = async (\n entry: string,\n tagKeys: readonly { tag: string; key: string }[]\n ) => {\n const keys = tagKeys.map(({ key }) => key)\n const values = await guard<(string | undefined)[] | undefined>(\n 'get',\n keys.join(' '),\n entry,\n () =>\n store.getMany\n ? store.getMany(keys)\n : Promise.all(keys.map((key) => store.get(key))),\n undefined\n )\n return values?.map((token, index) => ({\n tag: (tagKeys[index] as { tag: string }).tag,\n token,\n }))\n }\n\n /** Deserializes and validates a value: `{ value }`, or undefined. */\n const decode = async (config: EntryConfig, key: string, data: string) => {\n let value: unknown\n try {\n value = serializer.deserialize(data)\n } catch (error) {\n logger.warn(\n { entry: config.name, key, error: describe(error) },\n 'Cache value cannot be deserialized'\n )\n return undefined\n }\n if (!config.schema) return { value }\n const result = await config.schema.safeParseAsync(value)\n return result.success ? { value: result.data } : undefined\n }\n\n /**\n * Reads a stored value: its value, expiry, and whether its tags are\n * still valid. Undefined when missing, invalid, or past every stale\n * window.\n */\n const lookup = async (\n config: EntryConfig,\n policy: ReturnType<typeof policyFor>,\n key: string\n ) => {\n const text = await guard(\n 'get',\n key,\n config.name,\n () => store.get(key),\n undefined\n )\n if (text === undefined) return undefined\n const stored = parseStored(text)\n const now = Date.now()\n if (\n !stored ||\n stored.e + Math.max(policy.grace, policy.staleWhileRevalidate) <= now\n ) {\n return undefined\n }\n const decoded = await decode(config, key, stored.d)\n if (!decoded) return undefined\n const tags = Object.entries(stored.t ?? {})\n let tagsValid = true\n if (tags.length > 0) {\n const current = await tokensOf(\n config.name,\n await tagKeysOf(tags.map(([tag]) => tag))\n )\n // Unreadable tokens: the value may have been invalidated.\n tagsValid =\n current?.every(({ tag, token }) => token === stored.t?.[tag]) ?? false\n }\n return {\n value: decoded.value,\n expiresAt: stored.e,\n fresh: stored.e > now && tagsValid,\n tagsValid,\n }\n }\n\n /**\n * Writes a value, after checking that it reads back: returns the value\n * as read later, or undefined when it cannot be cached.\n */\n const write = async (\n config: EntryConfig,\n key: string,\n args: readonly unknown[],\n value: unknown\n ): Promise<{ value: unknown } | undefined> => {\n if (value === undefined) return undefined\n let data: string\n try {\n data = serializer.serialize(value)\n if (typeof data !== 'string') throw new TypeError('not a string')\n } catch (error) {\n logger.warn(\n { entry: config.name, key, error: describe(error) },\n 'Cache value cannot be serialized'\n )\n return undefined\n }\n const checked = config.schema\n ? await decode(config, key, data)\n : { value }\n if (!checked) {\n logger.warn(\n { entry: config.name, key },\n 'Cache value does not match the schema of the entry: not cached'\n )\n return undefined\n }\n const policy = policyFor(config)\n const lifetime =\n policy.ttl + Math.max(policy.grace, policy.staleWhileRevalidate)\n const now = Date.now()\n const stored: StoredEntry = {\n v: 1,\n c: now,\n e: now + policy.ttl,\n d: data,\n }\n if (config.tags) {\n const tags = checkTags(\n `defineCacheEntry(\"${config.name}\")`,\n config.tags(...args)\n )\n if (tags.length > 0) {\n const tagKeys = await tagKeysOf(tags)\n const current = await tokensOf(config.name, tagKeys)\n // Unreadable tokens: creating new ones would invalidate every\n // value of these tags, so the value is not cached.\n if (!current) return checked\n stored.t = {}\n for (const [index, { tag, token }] of current.entries()) {\n if (token !== undefined) {\n stored.t[tag] = token\n continue\n }\n // First value with this tag: its token is created.\n const created = crypto.randomUUID()\n const tagKey = (tagKeys[index] as { key: string }).key\n await guard(\n 'set',\n tagKey,\n config.name,\n () =>\n store.set(tagKey, created, Math.max(lifetime, TAG_LIFETIME)),\n undefined\n )\n stored.t[tag] = created\n }\n }\n }\n await guard(\n 'set',\n key,\n config.name,\n () => store.set(key, JSON.stringify(stored), lifetime),\n undefined\n )\n return checked\n }\n\n /**\n * Loads and writes a value, once per key at a time, and once for every\n * instance with the `lock` option.\n */\n const load = (\n config: EntryConfig,\n key: string,\n args: readonly unknown[],\n loader: () => unknown\n ) => {\n const running = loading.get(key)\n if (running) return running\n const loaded = (async () => {\n const policy = policyFor(config)\n const lockKey = `${key}:lock`\n let token: string | undefined\n if (policy.lock !== undefined && store.lock) {\n const lock = store.lock.bind(store)\n const ttl = policy.lock\n // null: the store failed, load without lock.\n const taken = await guard<string | undefined | null>(\n 'lock',\n lockKey,\n config.name,\n () => lock(lockKey, ttl),\n null\n )\n if (taken === undefined) {\n // Another instance loads it: wait for its value.\n const until = Date.now() + ttl\n while (Date.now() < until) {\n await new Promise((resolve) => setTimeout(resolve, LOCK_POLL))\n const found = await lookup(config, policy, key)\n if (found?.fresh) return found.value\n }\n logger.warn(\n { entry: config.name, key, lock: ttl },\n 'Cache lock not released in time: loading anyway'\n )\n } else if (taken !== null) {\n token = taken\n }\n }\n try {\n const value: unknown = await loader()\n const written = await write(config, key, args, value)\n backoffs.delete(key)\n return written ? written.value : value\n } finally {\n if (token !== undefined && store.unlock) {\n const unlock = store.unlock.bind(store)\n const held = token\n await guard(\n 'unlock',\n lockKey,\n config.name,\n () => unlock(lockKey, held),\n undefined\n )\n }\n }\n })()\n loading.set(key, loaded)\n void loaded.then(\n () => loading.delete(key),\n () => loading.delete(key)\n )\n return loaded\n }\n\n /** Logs the failure of a load that nobody waits for anymore. */\n const inBackground = (\n config: EntryConfig,\n key: string,\n loaded: Promise<unknown>\n ) => {\n loaded.catch((error: unknown) => {\n logger.warn(\n { entry: config.name, key, error: describe(error) },\n 'Cache refresh failed'\n )\n })\n }\n\n /** Rejects after `ms` (never when undefined), until cleared. */\n const deadline = (ms: number | undefined, error: () => unknown) => {\n let timer: ReturnType<typeof setTimeout> | undefined\n const promise = new Promise<never>((_, reject) => {\n if (ms !== undefined) timer = setTimeout(() => reject(error()), ms)\n })\n return { promise, clear: () => clearTimeout(timer) }\n }\n\n const cache: Cache = {\n get: (async (entry: unknown, ...args: unknown[]) => {\n const config = configOf('get', entry)\n const key = await keyFor(config, args)\n const found = await lookup(config, policyFor(config), key)\n report({ type: found?.fresh ? 'hit' : 'miss', entry: config.name, key })\n return found?.fresh ? found.value : undefined\n }) as Cache['get'],\n\n set: (async (entry: unknown, ...argsAndValue: unknown[]) => {\n const config = configOf('set', entry)\n const args = argsAndValue.slice(0, -1)\n const key = await keyFor(config, args)\n await write(config, key, args, argsAndValue.at(-1))\n }) as Cache['set'],\n\n delete: (async (entry: unknown, ...args: unknown[]) => {\n const config = configOf('delete', entry)\n const key = await keyFor(config, args)\n await guard(\n 'delete',\n key,\n config.name,\n () => store.delete(key),\n undefined\n )\n }) as Cache['delete'],\n\n getOrSet: (async (entry: unknown, ...argsAndLoader: unknown[]) => {\n const config = configOf('getOrSet', entry)\n const loader = argsAndLoader.at(-1)\n if (typeof loader !== 'function') {\n throw new TypeError(\n 'cache.getOrSet(): the last argument must be the function that loads the value.'\n )\n }\n const args = argsAndLoader.slice(0, -1)\n const key = await keyFor(config, args)\n const policy = policyFor(config)\n const found = await lookup(config, policy, key)\n const event = { entry: config.name, key }\n if (found?.fresh) {\n report({ type: 'hit', ...event })\n return found.value\n }\n const now = Date.now()\n\n // Stale-while-revalidate: serve, and refresh in the background.\n if (\n found?.tagsValid &&\n found.expiresAt + policy.staleWhileRevalidate > now\n ) {\n report({ type: 'stale', reason: 'revalidate', ...event })\n inBackground(\n config,\n key,\n load(config, key, args, loader as () => unknown)\n )\n return found.value\n }\n\n // Grace period: stale value served if the loader fails. Without it,\n // an invalidated value is never served.\n const stale =\n found && policy.grace > 0 && found.expiresAt + policy.grace > now\n ? found\n : undefined\n if (stale && (backoffs.get(key) ?? 0) > now) {\n report({ type: 'stale', reason: 'backoff', ...event })\n return stale.value\n }\n\n report({ type: 'miss', ...event })\n const loaded = load(config, key, args, loader as () => unknown)\n const slow = Symbol('slow')\n const soft = deadline(stale ? policy.timeout : undefined, () => slow)\n const hard = deadline(\n policy.hardTimeout,\n () =>\n new CacheTimeoutError(\n config.name,\n key,\n policy.hardTimeout as number\n )\n )\n try {\n return await Promise.race([loaded, soft.promise, hard.promise])\n } catch (error) {\n // The load goes on, and caches its value.\n if (error === slow || error instanceof CacheTimeoutError) {\n inBackground(config, key, loaded)\n }\n if (!stale) throw error\n if (error === slow) {\n logger.warn(\n { ...event, timeout: policy.timeout },\n 'Cache served a stale value: the loader is slow'\n )\n report({ type: 'stale', reason: 'timeout', ...event })\n return stale.value\n }\n backoffs.set(key, Date.now() + policy.graceBackoff)\n logger.warn(\n { ...event, error: describe(error) },\n 'Cache served a stale value: the loader failed'\n )\n report({ type: 'stale', reason: 'error', ...event })\n return stale.value\n } finally {\n soft.clear()\n hard.clear()\n }\n }) as Cache['getOrSet'],\n\n invalidateTags: async (tags) => {\n const tagKeys = await tagKeysOf(\n checkTags('cache.invalidateTags()', tags)\n )\n await Promise.all(\n tagKeys.map(({ tag, key }) =>\n guard(\n 'invalidate',\n key,\n `tag:${tag}`,\n () => store.set(key, crypto.randomUUID(), TAG_LIFETIME),\n undefined\n )\n )\n )\n logger.debug?.({ tags }, 'Cache tags invalidated')\n },\n\n withLogger: (other) => create(checkLogger('withLogger()', other)),\n }\n return Object.freeze(cache)\n }\n\n return create(rootLogger)\n}\n"],"mappings":"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","names":["entries","label"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotpress/cache",
3
- "version": "2.0.0-alpha.0",
3
+ "version": "2.0.0-alpha.2",
4
4
  "description": "Request memoization with batching, and a resilient cache with interchangeable stores",
5
5
  "author": "Renaud Guyon",
6
6
  "license": "MIT",
@@ -50,7 +50,7 @@
50
50
  "node": ">=22.12"
51
51
  },
52
52
  "peerDependencies": {
53
- "@dotpress/core": "^2.0.0-alpha.0"
53
+ "@dotpress/core": "^2.0.0-alpha.2"
54
54
  },
55
55
  "peerDependenciesMeta": {
56
56
  "@dotpress/core": {
@@ -58,8 +58,8 @@
58
58
  }
59
59
  },
60
60
  "devDependencies": {
61
- "@dotpress/core": "2.0.0-alpha.0",
62
- "@dotpress/testing": "2.0.0-alpha.0",
61
+ "@dotpress/core": "2.0.0-alpha.2",
62
+ "@dotpress/testing": "2.0.0-alpha.2",
63
63
  "ioredis": "~6.0.0",
64
64
  "miniflare": "~4.20260730.0",
65
65
  "redis": "~6.3.1",