import { EventEmitter } from "events"; import type { InvariantOf, ReadonlyDeep } from "type-fest"; import { type Entry, type NormalizeParamName, type NormalizeParamValue } from "./types/06_Normalization.js"; import { type AnyParams, type AnyValidators, type CacheSpec, type ConsumerRequest, type Logger, type ProducerResultResource, type SpecForId, type Store } from "./types/index.js"; type OnRequestAfterClose = "throw" | "return-nothing"; /** * The result of a cache lookup. MatchingSpecs is the subset of CacheSpec * variants whose id is compatible with the request's id (`Id`). */ export type CacheLookupResult = { usable?: Entry | undefined; usableWhileRevalidate?: Entry | undefined; usableIfError?: Entry | undefined; validatable: Entry[]; }; /** * This class implements a cache using a generalized version of HTTP's * underlying caching model, but w/o encoding HTTP-specific details (like header * parsing), so that it can be useful in more contexts. As part of this * generalization, this class talks about a cached value's "id and request * params" rather than its "URI and request headers", and cache directives are * provided as explicit arguments (not header strings). Similarly, it refers to * the "producer and consumer" of cached values, rather than the "server and the * client". Beyond renaming, it leaves open the set of available validators for * users to define (e.g., db row version numbers), rather than hard-coding HTTP * validators like etags and last-modified dates, and it supports a set of * directives somewhat more general than their HTTP equivalents. * * For (critical) background details on the HTTP caching model, see the docs. * * The cache is parameterized over a {@link CacheSpec}, which pairs each `id` * type with the corresponding `content` type. In the simple case, all ids * return the same kind of content, and `Spec` can stay as the default. To * support multiple id-to-content mappings within a single cache, pass a union * of CacheSpecs; the cache's get/store/getMany methods will then narrow the * content type based on the id of each request, and reject mismatched * (id, content) pairs at compile time. * * TODO: support the concept of warnings. * See https://tools.ietf.org/html/rfc7234#section-5.5 */ export default class Cache { #private; readonly emitter: EventEmitter; readonly normalizeParamName: NormalizeParamName; readonly normalizeParamValue: NormalizeParamValue; /** * @param dataStore The backing store that will actually hold cache entries. */ constructor(dataStore: Store>, options?: { logger?: Logger; onGetAfterClose?: OnRequestAfterClose; onStoreAfterClose?: OnRequestAfterClose; normalizeParamName?: NormalizeParamName; normalizeParamValue?: NormalizeParamValue; }); private static bestEntry; private normalizeParams; private normalizeVary; /** * Gets relevant items from the cache, always returning a promise for an * object with four possible keys: * * - `usable`: this is the cached value (if any) that satisfies the consumer's * request, given its cache directives, without requiring even background * revalidation. **If this key holds a value, all other keys in this object * will be undefined/empty.** This value will almost always be fresh, since * stale values aren't usable by defualt; the exception is if the consumer * allowed stale responses (sans revalidation) through the `maxStale` * directive. If multiple cached values would've have been suitable, this * holds the preferred one (which currently means the newest). * * - `usableWhileRevalidate`: this holds the preferred response (if any) * that's usable to satisfy the client's request, but that must be * (re-)validated in the background. * * - `usableIfError`: holds an entry (if any) that's usable only in case of an * error reaching the producer while trying to fetch/revalidate the cached * value. If there's a `usableWhileRevalidate` response, this key will * always be empty [because the usableWhileRevalidate response should be * returned before calling the producer, so there's no chance on an error.] * * - `validatable`: when validation is necessary (either because no usable * response is held by the cache, or the usable response requires * background re-validation), this array holds all entries in the cache * that have validation info -- including, possibly, responses present in * the other returned keys -- and that would be usable were the producer * to confirm (revalidate) that the resource's current state matches the * state identified by the validation info. Otherwise, this array is empty. * These are returned so that the user can make a conditional request for * the latest content that takes into account the validation info (e.g., * the etags w/ `If-None-Match`) of these saved responses. These responses * are probably stale, but it's possible they're not (e.g., if consumer * used a maxAge directive shorter than the producer's freshness lifetime). * * The result's content type is narrowed to the spec variants whose id is * compatible with `req.id`. So, e.g., if the cache's `Spec` is a union and * `req.id` is a literal that only matches one variant, callers don't have to * narrow the returned content themselves. */ get(req: ReadonlyDeep>, options?: { signal?: AbortSignal; }): Promise, Validators, Params>>; /** * Gets relevant items from the cache for multiple requests in a single * operation. This method is functionally equivalent to calling `get()` for * each individual request and concatenating the results, but is optimized by * using the store's `getMany` method to batch the underlying data store * operations. * * The result is a tuple typed per-request: each output slot's content type * is narrowed to the spec variants compatible with the corresponding input * request's id. * * @param requests Array of consumer requests to process * @returns Promise that resolves to an array of CacheLookupResult objects in * the same order as the input requests */ getMany>[]>(requests: Reqs, options?: { signal?: AbortSignal; }): Promise<{ -readonly [K in keyof Reqs]: CacheLookupResult, Validators, Params>; }>; /** * Stores ProducerResultResources that it assumes were _just now_ retrieved * from the producer. If the result wasn't retrieved just now, its retrieval * time can be specified. * * The (id, content) pairs in `data` are checked against the cache's `Spec` * at compile time, so a `Story[]` cannot be stored under a `story:...` id, * etc. */ store(data: readonly ProducerResultResource[]): Promise; close(timeout?: number): Promise; [Symbol.asyncDispose](): Promise; } export {}; //# sourceMappingURL=Cache.d.ts.map