@alxia/cache 0.2.0 → 0.3.0

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
@@ -156,3 +156,4 @@ across every process. A store of your own implements `CacheStore`: `get`,
156
156
  - [Guide](https://github.com/softistx/alxia/tree/develop/packages/cache/docs): a page per area — caching responses and its options, keys and `Vary`, invalidation by path and by tag, and stores, the memory one, Redis, or your own.
157
157
  - [Troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/cache/docs/troubleshooting.md): an error message, or a cache that does not hit, and what to do about it.
158
158
  - [Roadmap](https://github.com/softistx/alxia/blob/develop/packages/cache/docs/roadmap.md): what is coming, and what is not planned.
159
+ - [Recipes](https://github.com/softistx/alxia/blob/develop/docs/recipes/README.md): [Caching and rate limiting with Redis](https://github.com/softistx/alxia/blob/develop/docs/recipes/caching-and-rate-limiting.md).
package/dist/cache.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type BaseContext, type Empty, type Middleware, type MiddlewareMark, type Next } from '@alxia/core';
1
+ import { type BaseContext, type Empty, type Middleware, type Next } from '@alxia/core';
2
2
  import { type CacheStore } from './store';
3
3
  /**
4
4
  * `Requires` is what `key` and `tags` read from the context beyond
@@ -55,7 +55,7 @@ export interface Cache {
55
55
  */
56
56
  export type CacheMiddleware<Requires extends object = Empty> = Middleware<Requires, Promise<Response | Next<{
57
57
  cache: CacheControls;
58
- }>>> & MiddlewareMark & Cache;
58
+ }>>> & Cache;
59
59
  /**
60
60
  * Responses kept and served again, as a middleware: a `GET` to a route
61
61
  * declared after it is answered from the store while fresh, and from the
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,WAAW,EAEhB,KAAK,KAAK,EACV,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,IAAI,EACT,MAAM,aAAa,CAAC;AAQrB,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,SAAS,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,QAAQ,SAAS,MAAM,GAAG,KAAK;IAC5D,mCAAmC;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,QAAQ,KAAK,MAAM,GAAG,SAAS,CAAC;IACnE,yGAAyG;IACzG,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,wEAAwE;IACxE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,QAAQ,KAAK,SAAS,MAAM,EAAE,CAAC;IACnE,sHAAsH;IACtH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC,wEAAwE;IACxE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,6CAA6C;AAC7C,MAAM,WAAW,aAAa;IAC7B,iHAAiH;IACjH,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC7B,4CAA4C;IAC5C,IAAI,IAAI,IAAI,CAAC;CACb;AAED,uDAAuD;AACvD,MAAM,WAAW,KAAK;IACrB;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,2CAA2C;IAC3C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,QAAQ,SAAS,MAAM,GAAG,KAAK,IAAI,UAAU,CACxE,QAAQ,EACR,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC,CAClD,GACA,cAAc,GACd,KAAK,CAAC;AAEP;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,QAAQ,SAAS,MAAM,GAAG,KAAK,EACpD,OAAO,EAAE,YAAY,CAAC,QAAQ,CAAC,GAC7B,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAsEpC"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,WAAW,EAEhB,KAAK,KAAK,EACV,KAAK,UAAU,EAEf,KAAK,IAAI,EACT,MAAM,aAAa,CAAC;AAQrB,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,SAAS,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,QAAQ,SAAS,MAAM,GAAG,KAAK;IAC5D,mCAAmC;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,QAAQ,KAAK,MAAM,GAAG,SAAS,CAAC;IACnE,yGAAyG;IACzG,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,wEAAwE;IACxE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,QAAQ,KAAK,SAAS,MAAM,EAAE,CAAC;IACnE,sHAAsH;IACtH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC,wEAAwE;IACxE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,6CAA6C;AAC7C,MAAM,WAAW,aAAa;IAC7B,iHAAiH;IACjH,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC7B,4CAA4C;IAC5C,IAAI,IAAI,IAAI,CAAC;CACb;AAED,uDAAuD;AACvD,MAAM,WAAW,KAAK;IACrB;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,2CAA2C;IAC3C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,QAAQ,SAAS,MAAM,GAAG,KAAK,IAAI,UAAU,CACxE,QAAQ,EACR,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC,CAClD,GACA,KAAK,CAAC;AAEP;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,QAAQ,SAAS,MAAM,GAAG,KAAK,EACpD,OAAO,EAAE,YAAY,CAAC,QAAQ,CAAC,GAC7B,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAwEpC"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/cache.ts
2
2
  import {
3
- defineMiddleware
3
+ defineMiddleware,
4
+ markFactory
4
5
  } from "@alxia/core";
5
6
 
6
7
  // src/control.ts
@@ -232,7 +233,7 @@ function cache(options) {
232
233
  };
233
234
  const load = (key, ctx, next) => flight.load(key, () => keepOnMiss(key, ctx, next), next);
234
235
  const label = (says) => debug ? says : undefined;
235
- const middleware = defineMiddleware()(async (ctx, next) => {
236
+ const middleware = defineMiddleware()(async function cache(ctx, next) {
236
237
  const { request } = ctx;
237
238
  const added = { cache: controls.open(request) };
238
239
  const key = ctx.route === undefined || bypasses(request, honorNoCache) ? undefined : keyOf(ctx);
@@ -285,6 +286,7 @@ function handlesOf(store) {
285
286
  }
286
287
  };
287
288
  }
289
+ markFactory(cache);
288
290
  export {
289
291
  MemoryCacheStore,
290
292
  cache,
@@ -292,5 +294,5 @@ export {
292
294
  pathTag
293
295
  };
294
296
 
295
- //# debugId=4C215944C289F9AC64756E2164756E21
297
+ //# debugId=5612EC57D71825A264756E2164756E21
296
298
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -2,7 +2,7 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/cache.ts", "../src/control.ts", "../src/flight.ts", "../src/guard.ts", "../src/keep.ts", "../src/keys.ts", "../src/lookup.ts", "../src/respond.ts", "../src/store.ts"],
4
4
  "sourcesContent": [
5
- "import {\n\ttype BaseContext,\n\tdefineMiddleware,\n\ttype Empty,\n\ttype Middleware,\n\ttype MiddlewareMark,\n\ttype Next,\n} from '@alxia/core';\nimport { requestControls } from './control';\nimport { type Loaded, refreshBehind, singleFlight } from './flight';\nimport { storeGuard } from './guard';\nimport { type KeyedBy, keepable, shareable, toCached } from './keep';\nimport { defaultKey, pathTag } from './keys';\nimport { bypasses, freshness } from './lookup';\nimport { respond } from './respond';\nimport { type CacheStore, MemoryCacheStore } from './store';\n\n/**\n * `Requires` is what `key` and `tags` read from the context beyond\n * `BaseContext` — a `user` an earlier middleware adds — and what the app that\n * uses the cache must then give.\n */\nexport interface CacheOptions<Requires extends object = Empty> {\n\t/** Seconds a response is fresh. */\n\treadonly ttl: number;\n\t/**\n\t * Seconds a response is served stale after, while one request refreshes\n\t * it in the background: no client waits for a slow route. None by default.\n\t */\n\treadonly staleWhileRevalidate?: number;\n\t/** Where responses are kept: this process's memory by default. */\n\treadonly store?: CacheStore;\n\t/**\n\t * The key of a request: its path and query by default, and the headers\n\t * in `vary`. `undefined` is not cached: a request with a session, say.\n\t */\n\treadonly key?: (ctx: BaseContext & Requires) => string | undefined;\n\t/** Request headers the response varies by: `accept-language`. Each is part of the key, and of `Vary`. */\n\treadonly vary?: readonly string[];\n\t/** The statuses kept. `200` by default; a 404 may be worth keeping too. */\n\treadonly statuses?: readonly number[];\n\t/** Tags every response of these routes carries, for `invalidateTag`. */\n\treadonly tags?: (ctx: BaseContext & Requires) => readonly string[];\n\t/** Whether `Cache-Control: no-cache` from the client skips the cache. Off by default: a client cannot empty yours. */\n\treadonly honorClientNoCache?: boolean;\n\t/** Says `X-Cache: HIT`, `STALE` or `MISS`, and `Age`. On by default. */\n\treadonly debugHeaders?: boolean;\n}\n\n/** What the routes behind the cache read. */\nexport interface CacheControls {\n\t/** Tags the response being built, beyond the middleware's `tags`. Tags starting `alxia:` are the cache's own. */\n\ttag(...tags: string[]): void;\n\t/** Keeps this response out of the cache. */\n\tskip(): void;\n}\n\n/** A cache of responses, and the hands to empty it. */\nexport interface Cache {\n\t/**\n\t * Forgets every response kept for `path` — `/users/1?x=y`, as the request\n\t * asked it — whatever its key: each `vary` value, a `key` of your own.\n\t */\n\tinvalidate(path: string): Promise<void>;\n\t/** Forgets every response tagged `tag`. */\n\tinvalidateTag(tag: string): Promise<void>;\n\treadonly store: CacheStore;\n}\n\n/**\n * What `cache()` makes: a middleware that requires `Requires` of the app\n * and gives `cache`, with the hands to empty it.\n */\nexport type CacheMiddleware<Requires extends object = Empty> = Middleware<\n\tRequires,\n\tPromise<Response | Next<{ cache: CacheControls }>>\n> &\n\tMiddlewareMark &\n\tCache;\n\n/**\n * Responses kept and served again, as a middleware: a `GET` to a route\n * declared after it is answered from the store while fresh, and from the\n * route otherwise. Concurrent misses run the route once. Stale, it is served at\n * once and refreshed behind. A response that says `no-store` or `private`,\n * sets a cookie, or has another status is never kept; nor is the answer to a\n * request carrying `Authorization` or `Cookie`, unless it says `public`,\n * `s-maxage` or `must-revalidate`, or the key tells senders apart: `vary`\n * naming the header, or, for a cookie, a `key` of the app's own.\n *\n * Every kept response gets a weak `ETag` from its body when it has none, so\n * a client whose copy is current gets a 304.\n *\n * ```ts\n * const products = cache({ ttl: 60, staleWhileRevalidate: 300, tags: () => ['products'] });\n * app.use(products).get('/products', ...);\n * await products.invalidateTag('products');\n * ```\n *\n * A `key` or `tags` that reads what an earlier middleware added names it, and\n * the app must then give it: `cache<{ user: User }>({ tags: ({ user }) => [user.id], … })`.\n */\nexport function cache<Requires extends object = Empty>(\n\toptions: CacheOptions<Requires>,\n): NoInfer<CacheMiddleware<Requires>> {\n\tconst { store, ttl, stale, vary, statuses, debug, honorNoCache, ...keys } =\n\t\tsettingsOf(options);\n\tconst { keyOf, keyedBy } = keys;\n\tconst controls = requestControls();\n\tconst attempt = storeGuard();\n\tconst flight = singleFlight();\n\n\t/** The miss: the route runs, and what it answers is kept when it may be. */\n\tconst keepOnMiss = async (\n\t\tkey: string,\n\t\tctx: BaseContext & Requires,\n\t\tnext: () => Promise<Response>,\n\t): Promise<Loaded> => {\n\t\tconst response = await next();\n\t\tconst control = controls.get(ctx.request);\n\t\tif (\n\t\t\t!keepable(response, control, statuses) ||\n\t\t\t!shareable(ctx.request, response, keyedBy)\n\t\t) {\n\t\t\treturn { own: response };\n\t\t}\n\t\tconst cached = await toCached(response, {\n\t\t\tttl,\n\t\t\tstale,\n\t\t\tvary,\n\t\t\ttags: () => [\n\t\t\t\t...(options.tags?.(ctx) ?? []),\n\t\t\t\t...(control?.tags ?? []),\n\t\t\t\tpathTag(`${ctx.url.pathname}${ctx.url.search}`),\n\t\t\t],\n\t\t});\n\t\tawait attempt(() => store.set(key, cached, ttl + stale), undefined);\n\t\treturn { kept: cached };\n\t};\n\tconst load = (\n\t\tkey: string,\n\t\tctx: BaseContext & Requires,\n\t\tnext: () => Promise<Response>,\n\t) => flight.load(key, () => keepOnMiss(key, ctx, next), next);\n\n\tconst label = (says: 'HIT' | 'STALE' | 'MISS') => (debug ? says : undefined);\n\n\tconst middleware = defineMiddleware<Requires>()(async (ctx, next) => {\n\t\tconst { request } = ctx;\n\t\tconst added: { cache: CacheControls } = { cache: controls.open(request) };\n\t\t// A request no route matches is never kept: there is no route to answer it again.\n\t\tconst key =\n\t\t\tctx.route === undefined || bypasses(request, honorNoCache)\n\t\t\t\t? undefined\n\t\t\t\t: keyOf(ctx);\n\t\tif (key === undefined) return next(added);\n\t\tconst found = await attempt(() => store.get(key), undefined);\n\t\tconst worth = found === undefined ? undefined : freshness(found);\n\t\tif (found !== undefined && worth === 'fresh') {\n\t\t\treturn respond(request, found, label('HIT'));\n\t\t}\n\t\tif (found !== undefined && worth === 'stale') {\n\t\t\t// Served at once; the route runs behind it, unless a refresh already does.\n\t\t\tif (!flight.has(key)) {\n\t\t\t\trefreshBehind(load(key, ctx, () => next.behind(added)));\n\t\t\t}\n\t\t\treturn respond(request, found, label('STALE'));\n\t\t}\n\t\tconst loaded = await load(key, ctx, () => next(added));\n\t\tif (loaded instanceof Response) return loaded;\n\t\treturn respond(request, loaded, label('MISS'));\n\t});\n\n\treturn Object.assign(middleware, handlesOf(store));\n}\n\n/** The options with their defaults: milliseconds, lower-case header names, the key of a request and what it tells apart. */\nfunction settingsOf<Requires extends object>(options: CacheOptions<Requires>) {\n\tconst vary = (options.vary ?? []).map((name) => name.toLowerCase());\n\tconst keyedBy: KeyedBy = {\n\t\tauthorization: vary.includes('authorization'),\n\t\tcookie: options.key !== undefined || vary.includes('cookie'),\n\t};\n\treturn {\n\t\tstore: options.store ?? new MemoryCacheStore(),\n\t\tttl: options.ttl * 1000,\n\t\tstale: (options.staleWhileRevalidate ?? 0) * 1000,\n\t\tvary,\n\t\tstatuses: new Set(options.statuses ?? [200]),\n\t\tdebug: options.debugHeaders ?? true,\n\t\thonorNoCache: options.honorClientNoCache ?? false,\n\t\tkeyOf:\n\t\t\toptions.key ??\n\t\t\t((ctx: BaseContext & Requires) =>\n\t\t\t\tdefaultKey(\n\t\t\t\t\t`${ctx.url.pathname}${ctx.url.search}`,\n\t\t\t\t\tvary,\n\t\t\t\t\tctx.request.headers,\n\t\t\t\t)),\n\t\tkeyedBy,\n\t};\n}\n\n/** The hands to empty a cache: by the path a request asked, or by tag. */\nfunction handlesOf(store: CacheStore): Cache {\n\treturn {\n\t\tstore,\n\t\tinvalidate: async (path) => {\n\t\t\tawait store.deleteTag(pathTag(path));\n\t\t},\n\t\tinvalidateTag: async (tag) => {\n\t\t\tawait store.deleteTag(tag);\n\t\t},\n\t};\n}\n",
5
+ "import {\n\ttype BaseContext,\n\tdefineMiddleware,\n\ttype Empty,\n\ttype Middleware,\n\tmarkFactory,\n\ttype Next,\n} from '@alxia/core';\nimport { requestControls } from './control';\nimport { type Loaded, refreshBehind, singleFlight } from './flight';\nimport { storeGuard } from './guard';\nimport { type KeyedBy, keepable, shareable, toCached } from './keep';\nimport { defaultKey, pathTag } from './keys';\nimport { bypasses, freshness } from './lookup';\nimport { respond } from './respond';\nimport { type CacheStore, MemoryCacheStore } from './store';\n\n/**\n * `Requires` is what `key` and `tags` read from the context beyond\n * `BaseContext` — a `user` an earlier middleware adds — and what the app that\n * uses the cache must then give.\n */\nexport interface CacheOptions<Requires extends object = Empty> {\n\t/** Seconds a response is fresh. */\n\treadonly ttl: number;\n\t/**\n\t * Seconds a response is served stale after, while one request refreshes\n\t * it in the background: no client waits for a slow route. None by default.\n\t */\n\treadonly staleWhileRevalidate?: number;\n\t/** Where responses are kept: this process's memory by default. */\n\treadonly store?: CacheStore;\n\t/**\n\t * The key of a request: its path and query by default, and the headers\n\t * in `vary`. `undefined` is not cached: a request with a session, say.\n\t */\n\treadonly key?: (ctx: BaseContext & Requires) => string | undefined;\n\t/** Request headers the response varies by: `accept-language`. Each is part of the key, and of `Vary`. */\n\treadonly vary?: readonly string[];\n\t/** The statuses kept. `200` by default; a 404 may be worth keeping too. */\n\treadonly statuses?: readonly number[];\n\t/** Tags every response of these routes carries, for `invalidateTag`. */\n\treadonly tags?: (ctx: BaseContext & Requires) => readonly string[];\n\t/** Whether `Cache-Control: no-cache` from the client skips the cache. Off by default: a client cannot empty yours. */\n\treadonly honorClientNoCache?: boolean;\n\t/** Says `X-Cache: HIT`, `STALE` or `MISS`, and `Age`. On by default. */\n\treadonly debugHeaders?: boolean;\n}\n\n/** What the routes behind the cache read. */\nexport interface CacheControls {\n\t/** Tags the response being built, beyond the middleware's `tags`. Tags starting `alxia:` are the cache's own. */\n\ttag(...tags: string[]): void;\n\t/** Keeps this response out of the cache. */\n\tskip(): void;\n}\n\n/** A cache of responses, and the hands to empty it. */\nexport interface Cache {\n\t/**\n\t * Forgets every response kept for `path` — `/users/1?x=y`, as the request\n\t * asked it — whatever its key: each `vary` value, a `key` of your own.\n\t */\n\tinvalidate(path: string): Promise<void>;\n\t/** Forgets every response tagged `tag`. */\n\tinvalidateTag(tag: string): Promise<void>;\n\treadonly store: CacheStore;\n}\n\n/**\n * What `cache()` makes: a middleware that requires `Requires` of the app\n * and gives `cache`, with the hands to empty it.\n */\nexport type CacheMiddleware<Requires extends object = Empty> = Middleware<\n\tRequires,\n\tPromise<Response | Next<{ cache: CacheControls }>>\n> &\n\tCache;\n\n/**\n * Responses kept and served again, as a middleware: a `GET` to a route\n * declared after it is answered from the store while fresh, and from the\n * route otherwise. Concurrent misses run the route once. Stale, it is served at\n * once and refreshed behind. A response that says `no-store` or `private`,\n * sets a cookie, or has another status is never kept; nor is the answer to a\n * request carrying `Authorization` or `Cookie`, unless it says `public`,\n * `s-maxage` or `must-revalidate`, or the key tells senders apart: `vary`\n * naming the header, or, for a cookie, a `key` of the app's own.\n *\n * Every kept response gets a weak `ETag` from its body when it has none, so\n * a client whose copy is current gets a 304.\n *\n * ```ts\n * const products = cache({ ttl: 60, staleWhileRevalidate: 300, tags: () => ['products'] });\n * app.use(products).get('/products', ...);\n * await products.invalidateTag('products');\n * ```\n *\n * A `key` or `tags` that reads what an earlier middleware added names it, and\n * the app must then give it: `cache<{ user: User }>({ tags: ({ user }) => [user.id], … })`.\n */\nexport function cache<Requires extends object = Empty>(\n\toptions: CacheOptions<Requires>,\n): NoInfer<CacheMiddleware<Requires>> {\n\tconst { store, ttl, stale, vary, statuses, debug, honorNoCache, ...keys } =\n\t\tsettingsOf(options);\n\tconst { keyOf, keyedBy } = keys;\n\tconst controls = requestControls();\n\tconst attempt = storeGuard();\n\tconst flight = singleFlight();\n\n\t/** The miss: the route runs, and what it answers is kept when it may be. */\n\tconst keepOnMiss = async (\n\t\tkey: string,\n\t\tctx: BaseContext & Requires,\n\t\tnext: () => Promise<Response>,\n\t): Promise<Loaded> => {\n\t\tconst response = await next();\n\t\tconst control = controls.get(ctx.request);\n\t\tif (\n\t\t\t!keepable(response, control, statuses) ||\n\t\t\t!shareable(ctx.request, response, keyedBy)\n\t\t) {\n\t\t\treturn { own: response };\n\t\t}\n\t\tconst cached = await toCached(response, {\n\t\t\tttl,\n\t\t\tstale,\n\t\t\tvary,\n\t\t\ttags: () => [\n\t\t\t\t...(options.tags?.(ctx) ?? []),\n\t\t\t\t...(control?.tags ?? []),\n\t\t\t\tpathTag(`${ctx.url.pathname}${ctx.url.search}`),\n\t\t\t],\n\t\t});\n\t\tawait attempt(() => store.set(key, cached, ttl + stale), undefined);\n\t\treturn { kept: cached };\n\t};\n\tconst load = (\n\t\tkey: string,\n\t\tctx: BaseContext & Requires,\n\t\tnext: () => Promise<Response>,\n\t) => flight.load(key, () => keepOnMiss(key, ctx, next), next);\n\n\tconst label = (says: 'HIT' | 'STALE' | 'MISS') => (debug ? says : undefined);\n\n\tconst middleware = defineMiddleware<Requires>()(\n\t\tasync function cache(ctx, next) {\n\t\t\tconst { request } = ctx;\n\t\t\tconst added: { cache: CacheControls } = { cache: controls.open(request) };\n\t\t\t// A request no route matches is never kept: there is no route to answer it again.\n\t\t\tconst key =\n\t\t\t\tctx.route === undefined || bypasses(request, honorNoCache)\n\t\t\t\t\t? undefined\n\t\t\t\t\t: keyOf(ctx);\n\t\t\tif (key === undefined) return next(added);\n\t\t\tconst found = await attempt(() => store.get(key), undefined);\n\t\t\tconst worth = found === undefined ? undefined : freshness(found);\n\t\t\tif (found !== undefined && worth === 'fresh') {\n\t\t\t\treturn respond(request, found, label('HIT'));\n\t\t\t}\n\t\t\tif (found !== undefined && worth === 'stale') {\n\t\t\t\t// Served at once; the route runs behind it, unless a refresh already does.\n\t\t\t\tif (!flight.has(key)) {\n\t\t\t\t\trefreshBehind(load(key, ctx, () => next.behind(added)));\n\t\t\t\t}\n\t\t\t\treturn respond(request, found, label('STALE'));\n\t\t\t}\n\t\t\tconst loaded = await load(key, ctx, () => next(added));\n\t\t\tif (loaded instanceof Response) return loaded;\n\t\t\treturn respond(request, loaded, label('MISS'));\n\t\t},\n\t);\n\n\treturn Object.assign(middleware, handlesOf(store));\n}\n\n/** The options with their defaults: milliseconds, lower-case header names, the key of a request and what it tells apart. */\nfunction settingsOf<Requires extends object>(options: CacheOptions<Requires>) {\n\tconst vary = (options.vary ?? []).map((name) => name.toLowerCase());\n\tconst keyedBy: KeyedBy = {\n\t\tauthorization: vary.includes('authorization'),\n\t\tcookie: options.key !== undefined || vary.includes('cookie'),\n\t};\n\treturn {\n\t\tstore: options.store ?? new MemoryCacheStore(),\n\t\tttl: options.ttl * 1000,\n\t\tstale: (options.staleWhileRevalidate ?? 0) * 1000,\n\t\tvary,\n\t\tstatuses: new Set(options.statuses ?? [200]),\n\t\tdebug: options.debugHeaders ?? true,\n\t\thonorNoCache: options.honorClientNoCache ?? false,\n\t\tkeyOf:\n\t\t\toptions.key ??\n\t\t\t((ctx: BaseContext & Requires) =>\n\t\t\t\tdefaultKey(\n\t\t\t\t\t`${ctx.url.pathname}${ctx.url.search}`,\n\t\t\t\t\tvary,\n\t\t\t\t\tctx.request.headers,\n\t\t\t\t)),\n\t\tkeyedBy,\n\t};\n}\n\n/** The hands to empty a cache: by the path a request asked, or by tag. */\nfunction handlesOf(store: CacheStore): Cache {\n\treturn {\n\t\tstore,\n\t\tinvalidate: async (path) => {\n\t\t\tawait store.deleteTag(pathTag(path));\n\t\t},\n\t\tinvalidateTag: async (tag) => {\n\t\t\tawait store.deleteTag(tag);\n\t\t},\n\t};\n}\n\nmarkFactory(cache);\n",
6
6
  "/** What each route behind the cache says through `ctx.cache`, kept by request. */\n\n/** What a route said through `ctx.cache`, for the response it is building. */\nexport interface Control {\n\treadonly tags: string[];\n\tskipped: boolean;\n}\n\nexport function requestControls() {\n\tconst controls = new WeakMap<Request, Control>();\n\treturn {\n\t\t/** What the route said for `request`, once it has run. */\n\t\tget: (request: Request): Control | undefined => controls.get(request),\n\t\t/** A fresh control for `request`, and the hands a route is given to it. */\n\t\topen(request: Request) {\n\t\t\tconst control: Control = { tags: [], skipped: false };\n\t\t\tcontrols.set(request, control);\n\t\t\treturn {\n\t\t\t\ttag: (...tags: string[]) => {\n\t\t\t\t\tcontrol.tags.push(...tags);\n\t\t\t\t},\n\t\t\t\tskip: () => {\n\t\t\t\t\tcontrol.skipped = true;\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n",
7
7
  "import type { CachedResponse } from './store';\n\n/** What a run of the route gives: a response it kept, or its own when it kept nothing. */\nexport type Loaded = { kept: CachedResponse } | { own: Response };\n\n/**\n * Runs the route once for every concurrent miss of a key. Only a response\n * that is kept is shared: one that is not — private, skipped, a cookie,\n * another status — answers the request that ran the route, and every other\n * waiting request runs the route itself, through its own `next`.\n */\nexport function singleFlight() {\n\t/** The run of each key being loaded: what it kept, or `undefined` when it kept nothing. */\n\tconst loading = new Map<string, Promise<CachedResponse | undefined>>();\n\treturn {\n\t\t/** Whether a run of `key` is under way. */\n\t\thas: (key: string): boolean => loading.has(key),\n\t\tload(\n\t\t\tkey: string,\n\t\t\trun: () => Promise<Loaded>,\n\t\t\tnext: () => Promise<Response>,\n\t\t): Promise<CachedResponse | Response> {\n\t\t\tconst running = loading.get(key);\n\t\t\tif (running !== undefined) {\n\t\t\t\treturn running.then(\n\t\t\t\t\t(cached): Promise<CachedResponse | Response> | CachedResponse =>\n\t\t\t\t\t\tcached ?? next(),\n\t\t\t\t\t(): Promise<CachedResponse | Response> => next(),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst ran = run();\n\t\t\tconst shared = ran.then((loaded) =>\n\t\t\t\t'kept' in loaded ? loaded.kept : undefined,\n\t\t\t);\n\t\t\tloading.set(key, shared);\n\t\t\tshared.finally(() => loading.delete(key)).catch(() => {});\n\t\t\treturn ran.then((loaded) =>\n\t\t\t\t'kept' in loaded ? loaded.kept : loaded.own,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * A stale response's refresh, run behind it: the route's own error is its to\n * log, and a response it does not keep is read by no one.\n */\nexport function refreshBehind(\n\tloading: Promise<CachedResponse | Response>,\n): void {\n\tloading\n\t\t.then((loaded) =>\n\t\t\tloaded instanceof Response ? loaded.body?.cancel() : undefined,\n\t\t)\n\t\t.catch((error) => console.error(error));\n}\n",
8
8
  "/**\n * A store that cannot answer is a miss, or keeps nothing: a cache is never\n * worth a 500. Said to the log once per outage — again only after the store\n * has answered since.\n */\nexport function storeGuard() {\n\tlet failing = false;\n\treturn async <T>(work: () => Promise<T> | T, fallback: T): Promise<T> => {\n\t\ttry {\n\t\t\tconst value = await work();\n\t\t\tfailing = false;\n\t\t\treturn value;\n\t\t} catch (error) {\n\t\t\tif (!failing) console.error(error);\n\t\t\tfailing = true;\n\t\t\treturn fallback;\n\t\t}\n\t};\n}\n",
@@ -12,7 +12,7 @@
12
12
  "import type { CachedResponse } from './store';\n\n/** A kept response, or a 304 to a client that has it. */\nexport function respond(\n\trequest: Request,\n\tcached: CachedResponse,\n\tstate: string | undefined,\n): Response {\n\tconst headers = new Headers(cached.headers as [string, string][]);\n\tif (state !== undefined) {\n\t\theaders.set('x-cache', state);\n\t\theaders.set(\n\t\t\t'age',\n\t\t\tString(Math.max(0, Math.floor((Date.now() - cached.storedAt) / 1000))),\n\t\t);\n\t}\n\tconst etag = headers.get('etag');\n\tconst match = request.headers.get('if-none-match');\n\tif (etag !== null && match !== null) {\n\t\tconst weak = (tag: string) => tag.trim().replace(/^W\\//, '');\n\t\tif (\n\t\t\tmatch\n\t\t\t\t.split(',')\n\t\t\t\t.some((tag) => tag.trim() === '*' || weak(tag) === weak(etag))\n\t\t) {\n\t\t\treturn new Response(null, { status: 304, headers });\n\t\t}\n\t}\n\treturn new Response(\n\t\tcached.status === 204 ? null : (cached.body as Uint8Array<ArrayBuffer>),\n\t\t{\n\t\t\tstatus: cached.status,\n\t\t\theaders,\n\t\t},\n\t);\n}\n",
13
13
  "/** A response as a store keeps it. */\nexport interface CachedResponse {\n\treadonly status: number;\n\treadonly headers: readonly (readonly [string, string])[];\n\treadonly body: Uint8Array;\n\t/** Milliseconds since the epoch. */\n\treadonly storedAt: number;\n\t/** Milliseconds it is fresh for, from `storedAt`. */\n\treadonly ttl: number;\n\t/** Milliseconds it may be served stale after, while it is refreshed. */\n\treadonly stale: number;\n\treadonly tags: readonly string[];\n}\n\n/**\n * Where responses are kept. The memory store keeps them in one process;\n * `@alxia/redis`'s `redisCacheStore` across every process sharing a Redis.\n */\nexport interface CacheStore {\n\tget(\n\t\tkey: string,\n\t): Promise<CachedResponse | undefined> | CachedResponse | undefined;\n\t/** Keeps `value` for `keepFor` milliseconds: its freshness and its staleness together. */\n\tset(\n\t\tkey: string,\n\t\tvalue: CachedResponse,\n\t\tkeepFor: number,\n\t): Promise<void> | void;\n\tdelete(key: string): Promise<void> | void;\n\t/** Forgets every response tagged `tag`. */\n\tdeleteTag(tag: string): Promise<void> | void;\n}\n\nexport interface MemoryCacheOptions {\n\t/** The most responses kept: the least recently read goes first. 1 000 by default. */\n\treadonly maxEntries?: number;\n\t/** The most bytes of bodies kept. 64 MiB by default. */\n\treadonly maxBytes?: number;\n}\n\n/** A least-recently-used store in one process's memory. */\nexport class MemoryCacheStore implements CacheStore {\n\treadonly #entries = new Map<\n\t\tstring,\n\t\t{ value: CachedResponse; expiresAt: number }\n\t>();\n\treadonly #tags = new Map<string, Set<string>>();\n\treadonly #maxEntries: number;\n\treadonly #maxBytes: number;\n\t#bytes = 0;\n\n\tconstructor(options: MemoryCacheOptions = {}) {\n\t\tthis.#maxEntries = options.maxEntries ?? 1_000;\n\t\tthis.#maxBytes = options.maxBytes ?? 64 * 1024 * 1024;\n\t}\n\n\tget(key: string): CachedResponse | undefined {\n\t\tconst entry = this.#entries.get(key);\n\t\tif (entry === undefined) return undefined;\n\t\tif (entry.expiresAt <= Date.now()) {\n\t\t\tthis.delete(key);\n\t\t\treturn undefined;\n\t\t}\n\t\t// Read again: the most recently used goes to the end.\n\t\tthis.#entries.delete(key);\n\t\tthis.#entries.set(key, entry);\n\t\treturn entry.value;\n\t}\n\n\tset(key: string, value: CachedResponse, keepFor: number): void {\n\t\tthis.delete(key);\n\t\tif (value.body.byteLength > this.#maxBytes) return;\n\t\tthis.#entries.set(key, { value, expiresAt: Date.now() + keepFor });\n\t\tthis.#bytes += value.body.byteLength;\n\t\tfor (const tag of value.tags) {\n\t\t\tlet keys = this.#tags.get(tag);\n\t\t\tif (keys === undefined) {\n\t\t\t\tkeys = new Set();\n\t\t\t\tthis.#tags.set(tag, keys);\n\t\t\t}\n\t\t\tkeys.add(key);\n\t\t}\n\t\tfor (const oldest of this.#entries.keys()) {\n\t\t\tif (\n\t\t\t\tthis.#entries.size <= this.#maxEntries &&\n\t\t\t\tthis.#bytes <= this.#maxBytes\n\t\t\t)\n\t\t\t\tbreak;\n\t\t\tthis.delete(oldest);\n\t\t}\n\t}\n\n\tdelete(key: string): void {\n\t\tconst entry = this.#entries.get(key);\n\t\tif (entry === undefined) return;\n\t\tthis.#entries.delete(key);\n\t\tthis.#bytes -= entry.value.body.byteLength;\n\t\tfor (const tag of entry.value.tags) {\n\t\t\tconst keys = this.#tags.get(tag);\n\t\t\tkeys?.delete(key);\n\t\t\tif (keys?.size === 0) this.#tags.delete(tag);\n\t\t}\n\t}\n\n\tdeleteTag(tag: string): void {\n\t\tfor (const key of [...(this.#tags.get(tag) ?? [])]) this.delete(key);\n\t}\n\n\t/** How many responses are kept. */\n\tget size(): number {\n\t\treturn this.#entries.size;\n\t}\n}\n"
14
14
  ],
15
- "mappings": "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",
16
- "debugId": "4C215944C289F9AC64756E2164756E21",
15
+ "mappings": "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",
16
+ "debugId": "5612EC57D71825A264756E2164756E21",
17
17
  "names": []
18
18
  }
package/docs/README.md CHANGED
@@ -14,3 +14,9 @@ a realistic example for each.
14
14
  | [Stores](guide/stores.md) | sizing the memory store, sharing responses in Redis, or writing and testing a store of your own |
15
15
  | [Troubleshooting](troubleshooting.md) | something went wrong and you have the message, or the cache does not hit when you expected it to |
16
16
  | [Roadmap](roadmap.md) | wondering what is coming, and what is not planned |
17
+
18
+ ## Recipes
19
+
20
+ A task that crosses packages, in [the repository's recipes](https://github.com/softistx/alxia/blob/develop/docs/recipes/README.md), each with a complete example:
21
+
22
+ - [Caching and rate limiting with Redis](https://github.com/softistx/alxia/blob/develop/docs/recipes/caching-and-rate-limiting.md): a limit and a response cache shared by every process
@@ -21,7 +21,7 @@ is decided by the cache, not the store.
21
21
 
22
22
  ## In memory: `MemoryCacheStore`
23
23
 
24
- ```ts
24
+ ```ts no-check
25
25
  new MemoryCacheStore(options?: MemoryCacheOptions)
26
26
  ```
27
27
 
@@ -74,7 +74,7 @@ every process sharing the Redis serves what one of them kept, and one
74
74
  `invalidateTag` reaches them all.
75
75
 
76
76
  ```sh
77
- bun add @alxia/redis @nxgt/redis @nxgt/redis-guard zod
77
+ bun add @alxia/redis @nxgt/redis zod
78
78
  ```
79
79
 
80
80
  ```ts
package/docs/roadmap.md CHANGED
@@ -8,7 +8,7 @@ number on it. Every release, with each change it made, is in
8
8
  ## Now
9
9
 
10
10
  - **A middleware, not a plugin.** `app.use(cache({ ttl }))` is the form;
11
- `app.plugin(cache(…))` keeps working, deprecated. Stale responses are
11
+ `app.plugin(cache(…))`, deprecated in 0.4, was removed in 0.5. Stale responses are
12
12
  served at once with the refresh run behind them, and `CacheMiddleware<Requires>`
13
13
  names what `cache()` returns.
14
14
 
@@ -36,7 +36,7 @@ Nothing scheduled yet.
36
36
 
37
37
  ### 0.1.0
38
38
 
39
- - **Response caching as a plugin.** `plugin(cache({ ttl }))` answers the `GET`
39
+ - **Response caching as a plugin**, the 0.1 form. `cache({ ttl })` answers the `GET`
40
40
  and `HEAD` requests of every route declared after it from a store while
41
41
  they are fresh, and from the route otherwise, saying `X-Cache: HIT` or
42
42
  `MISS` and `Age`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alxia/cache",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "HTTP response caching for alxia: TTL, stale-while-revalidate, one load for concurrent misses, tags, ETags — in memory, or in Redis with @alxia/redis",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,11 +41,11 @@
41
41
  ]
42
42
  },
43
43
  "devDependencies": {
44
- "@alxia/core": "^0.4.0",
44
+ "@alxia/core": "^0.5.0",
45
45
  "@types/bun": "^1.4.2"
46
46
  },
47
47
  "peerDependencies": {
48
- "@alxia/core": "^0.4.0",
48
+ "@alxia/core": "^0.5.0",
49
49
  "typescript": "^6.0.3 || ^7.0.0"
50
50
  }
51
51
  }