rust-node-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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../js/express.ts"],"sourcesContent":["/**\n * Integração com Express: cache de respostas JSON.\n *\n * Uso:\n * import { Cache } from \"rust-node-cache\";\n * import { cacheMiddleware } from \"rust-node-cache/express\";\n *\n * const cache = new Cache();\n * app.get(\"/users/:id\", cacheMiddleware({ cache, ttlSeconds: 60 }), handler);\n *\n * O middleware intercepta `res.json`: num HIT responde direto do cache; num\n * MISS deixa o handler rodar e guarda o corpo da resposta (apenas em 2xx).\n */\n\nimport type { Request, Response, NextFunction } from \"express\";\nimport type { Cache } from \"./index\";\n\nexport interface CacheMiddlewareOptions {\n /** Instância de cache a usar. */\n cache: Cache;\n /** TTL aplicado às respostas guardadas (em segundos). */\n ttlSeconds?: number;\n /** Métodos HTTP elegíveis para cache. Padrão: `[\"GET\"]`. */\n methods?: string[];\n /** Gera a chave de cache a partir da requisição. Padrão: `METHOD:url`. */\n keyGenerator?: (req: Request) => string;\n}\n\n/**\n * Cria um middleware Express que cacheia respostas JSON. Adiciona o cabeçalho\n * `X-Cache: HIT|MISS` para facilitar a observabilidade.\n */\nexport function cacheMiddleware(options: CacheMiddlewareOptions) {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: Request) => `${req.method}:${req.originalUrl}`);\n\n return function rustNodeCache(\n req: Request,\n res: Response,\n next: NextFunction,\n ): void {\n if (!methods.includes(req.method)) {\n next();\n return;\n }\n\n const key = keyGenerator(req);\n const cached = cache.get(key);\n if (cached !== null) {\n res.setHeader(\"X-Cache\", \"HIT\");\n res.json(cached);\n return;\n }\n\n res.setHeader(\"X-Cache\", \"MISS\");\n const originalJson = res.json.bind(res);\n res.json = (body: unknown): Response => {\n // Só guardamos respostas bem-sucedidas (2xx).\n if (res.statusCode >= 200 && res.statusCode < 300) {\n cache.set(key, body, ttlSeconds ? { ttlSeconds } : undefined);\n }\n return originalJson(body);\n };\n\n next();\n };\n}\n"],"mappings":";AAgCO,SAAS,gBAAgB,SAAiC;AAC/D,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAiB,GAAG,IAAI,MAAM,IAAI,IAAI,WAAW;AAE7E,SAAO,SAAS,cACd,KACA,KACA,MACM;AACN,QAAI,CAAC,QAAQ,SAAS,IAAI,MAAM,GAAG;AACjC,WAAK;AACL;AAAA,IACF;AAEA,UAAM,MAAM,aAAa,GAAG;AAC5B,UAAM,SAAS,MAAM,IAAI,GAAG;AAC5B,QAAI,WAAW,MAAM;AACnB,UAAI,UAAU,WAAW,KAAK;AAC9B,UAAI,KAAK,MAAM;AACf;AAAA,IACF;AAEA,QAAI,UAAU,WAAW,MAAM;AAC/B,UAAM,eAAe,IAAI,KAAK,KAAK,GAAG;AACtC,QAAI,OAAO,CAAC,SAA4B;AAEtC,UAAI,IAAI,cAAc,OAAO,IAAI,aAAa,KAAK;AACjD,cAAM,IAAI,KAAK,MAAM,aAAa,EAAE,WAAW,IAAI,MAAS;AAAA,MAC9D;AACA,aAAO,aAAa,IAAI;AAAA,IAC1B;AAEA,SAAK;AAAA,EACP;AACF;","names":[]}
1
+ {"version":3,"sources":["../js/express.ts"],"sourcesContent":["/**\n * Express integration: caching of JSON responses.\n *\n * Usage:\n * import { Cache } from \"rust-node-cache\";\n * import { cacheMiddleware } from \"rust-node-cache/express\";\n *\n * const cache = new Cache();\n * app.get(\"/users/:id\", cacheMiddleware({ cache, ttlSeconds: 60 }), handler);\n *\n * The middleware intercepts `res.json`: on a HIT it responds straight from the\n * cache; on a MISS it lets the handler run and stores the response body (2xx only).\n */\n\nimport type { Request, Response, NextFunction } from \"express\";\nimport type { Cache } from \"./index\";\n\nexport interface CacheMiddlewareOptions {\n /** Cache instance to use. */\n cache: Cache;\n /** TTL applied to stored responses (in seconds). */\n ttlSeconds?: number;\n /** HTTP methods eligible for caching. Default: `[\"GET\"]`. */\n methods?: string[];\n /** Builds the cache key from the request. Default: `METHOD:url`. */\n keyGenerator?: (req: Request) => string;\n}\n\n/**\n * Creates an Express middleware that caches JSON responses. Adds an\n * `X-Cache: HIT|MISS` header for observability.\n *\n * What is stored is the JSON the client received (a `Date` in the body is\n * stored as its ISO string, as `res.json` sends it), and a HIT sends that text\n * as is, without parsing or re-serializing it.\n */\nexport function cacheMiddleware(options: CacheMiddlewareOptions) {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: Request) => `${req.method}:${req.originalUrl}`);\n\n return function rustNodeCache(\n req: Request,\n res: Response,\n next: NextFunction,\n ): void {\n if (!methods.includes(req.method)) {\n next();\n return;\n }\n\n const key = keyGenerator(req);\n const cached = cache.getJson(key);\n if (cached !== null) {\n res.setHeader(\"X-Cache\", \"HIT\");\n res.type(\"application/json\").send(cached);\n return;\n }\n\n res.setHeader(\"X-Cache\", \"MISS\");\n const originalJson = res.json.bind(res);\n res.json = (body: unknown): Response => {\n // Only successful responses (2xx) are stored.\n if (res.statusCode >= 200 && res.statusCode < 300) {\n const json = toJson(body);\n if (json !== undefined) {\n cache.setJson(key, json, ttlSeconds ? { ttlSeconds } : undefined);\n }\n }\n return originalJson(body);\n };\n\n next();\n };\n}\n\n/** The body as `res.json` would send it, or `undefined` when it cannot be cached. */\nfunction toJson(body: unknown): string | undefined {\n try {\n return JSON.stringify(body);\n } catch {\n return undefined; // circular or BigInt: `res.json` reports it, not the cache.\n }\n}\n"],"mappings":";AAoCO,SAAS,gBAAgB,SAAiC;AAC/D,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAiB,GAAG,IAAI,MAAM,IAAI,IAAI,WAAW;AAE7E,SAAO,SAAS,cACd,KACA,KACA,MACM;AACN,QAAI,CAAC,QAAQ,SAAS,IAAI,MAAM,GAAG;AACjC,WAAK;AACL;AAAA,IACF;AAEA,UAAM,MAAM,aAAa,GAAG;AAC5B,UAAM,SAAS,MAAM,QAAQ,GAAG;AAChC,QAAI,WAAW,MAAM;AACnB,UAAI,UAAU,WAAW,KAAK;AAC9B,UAAI,KAAK,kBAAkB,EAAE,KAAK,MAAM;AACxC;AAAA,IACF;AAEA,QAAI,UAAU,WAAW,MAAM;AAC/B,UAAM,eAAe,IAAI,KAAK,KAAK,GAAG;AACtC,QAAI,OAAO,CAAC,SAA4B;AAEtC,UAAI,IAAI,cAAc,OAAO,IAAI,aAAa,KAAK;AACjD,cAAM,OAAO,OAAO,IAAI;AACxB,YAAI,SAAS,QAAW;AACtB,gBAAM,QAAQ,KAAK,MAAM,aAAa,EAAE,WAAW,IAAI,MAAS;AAAA,QAClE;AAAA,MACF;AACA,aAAO,aAAa,IAAI;AAAA,IAC1B;AAEA,SAAK;AAAA,EACP;AACF;AAGA,SAAS,OAAO,MAAmC;AACjD,MAAI;AACF,WAAO,KAAK,UAAU,IAAI;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -2,31 +2,31 @@ import { FastifyRequest, FastifyInstance } from 'fastify';
2
2
  import { Cache } from './index.mjs';
3
3
 
4
4
  /**
5
- * Integração com Fastify: cache de respostas JSON via plugin.
5
+ * Fastify integration: caching of JSON responses through a plugin.
6
6
  *
7
- * Uso:
7
+ * Usage:
8
8
  * import { Cache } from "rust-node-cache";
9
9
  * import { cachePlugin } from "rust-node-cache/fastify";
10
10
  *
11
11
  * const cache = new Cache();
12
12
  * fastify.register(cachePlugin, { cache, ttlSeconds: 60 });
13
13
  *
14
- * Fluxo: o hook `onRequest` checa o cache e, num HIT, responde imediatamente
15
- * (curto-circuito). Num MISS, o hook `onSend` guarda o payload serializado.
14
+ * Flow: the `onRequest` hook checks the cache and, on a HIT, responds right away
15
+ * (short-circuit). On a MISS, the `onSend` hook stores the serialized payload.
16
16
  */
17
17
 
18
18
  interface CachePluginOptions {
19
- /** Instância de cache a usar. */
19
+ /** Cache instance to use. */
20
20
  cache: Cache;
21
- /** TTL aplicado às respostas guardadas (em segundos). */
21
+ /** TTL applied to stored responses (in seconds). */
22
22
  ttlSeconds?: number;
23
- /** Métodos HTTP elegíveis para cache. Padrão: `["GET"]`. */
23
+ /** HTTP methods eligible for caching. Default: `["GET"]`. */
24
24
  methods?: string[];
25
- /** Gera a chave de cache a partir da requisição. Padrão: `METHOD:url`. */
25
+ /** Builds the cache key from the request. Default: `METHOD:url`. */
26
26
  keyGenerator?: (req: FastifyRequest) => string;
27
27
  }
28
28
  /**
29
- * Plugin Fastify que cacheia respostas JSON. Use com `fastify.register`.
29
+ * Fastify plugin that caches JSON responses. Use it with `fastify.register`.
30
30
  */
31
31
  declare function cachePlugin(fastify: FastifyInstance, options: CachePluginOptions): Promise<void>;
32
32
 
package/dist/fastify.d.ts CHANGED
@@ -2,31 +2,31 @@ import { FastifyRequest, FastifyInstance } from 'fastify';
2
2
  import { Cache } from './index.js';
3
3
 
4
4
  /**
5
- * Integração com Fastify: cache de respostas JSON via plugin.
5
+ * Fastify integration: caching of JSON responses through a plugin.
6
6
  *
7
- * Uso:
7
+ * Usage:
8
8
  * import { Cache } from "rust-node-cache";
9
9
  * import { cachePlugin } from "rust-node-cache/fastify";
10
10
  *
11
11
  * const cache = new Cache();
12
12
  * fastify.register(cachePlugin, { cache, ttlSeconds: 60 });
13
13
  *
14
- * Fluxo: o hook `onRequest` checa o cache e, num HIT, responde imediatamente
15
- * (curto-circuito). Num MISS, o hook `onSend` guarda o payload serializado.
14
+ * Flow: the `onRequest` hook checks the cache and, on a HIT, responds right away
15
+ * (short-circuit). On a MISS, the `onSend` hook stores the serialized payload.
16
16
  */
17
17
 
18
18
  interface CachePluginOptions {
19
- /** Instância de cache a usar. */
19
+ /** Cache instance to use. */
20
20
  cache: Cache;
21
- /** TTL aplicado às respostas guardadas (em segundos). */
21
+ /** TTL applied to stored responses (in seconds). */
22
22
  ttlSeconds?: number;
23
- /** Métodos HTTP elegíveis para cache. Padrão: `["GET"]`. */
23
+ /** HTTP methods eligible for caching. Default: `["GET"]`. */
24
24
  methods?: string[];
25
- /** Gera a chave de cache a partir da requisição. Padrão: `METHOD:url`. */
25
+ /** Builds the cache key from the request. Default: `METHOD:url`. */
26
26
  keyGenerator?: (req: FastifyRequest) => string;
27
27
  }
28
28
  /**
29
- * Plugin Fastify que cacheia respostas JSON. Use com `fastify.register`.
29
+ * Fastify plugin that caches JSON responses. Use it with `fastify.register`.
30
30
  */
31
31
  declare function cachePlugin(fastify: FastifyInstance, options: CachePluginOptions): Promise<void>;
32
32
 
package/dist/fastify.js CHANGED
@@ -37,10 +37,10 @@ async function cachePlugin(fastify, options) {
37
37
  return;
38
38
  }
39
39
  const key = keyGenerator(request);
40
- const cached = cache.get(key);
40
+ const cached = cache.getJson(key);
41
41
  if (cached !== null) {
42
42
  reply.header("X-Cache", "HIT");
43
- reply.send(cached);
43
+ reply.type("application/json; charset=utf-8").send(cached);
44
44
  return;
45
45
  }
46
46
  reply.header("X-Cache", "MISS");
@@ -52,20 +52,14 @@ async function cachePlugin(fastify, options) {
52
52
  "onSend",
53
53
  (request, reply, payload, done) => {
54
54
  const key = request[CACHE_KEY];
55
- if (key && typeof payload === "string" && reply.statusCode >= 200 && reply.statusCode < 300) {
56
- try {
57
- cache.set(
58
- key,
59
- JSON.parse(payload),
60
- ttlSeconds ? { ttlSeconds } : void 0
61
- );
62
- } catch {
63
- }
55
+ if (key && typeof payload === "string" && reply.statusCode >= 200 && reply.statusCode < 300 && String(reply.getHeader("content-type") ?? "").startsWith("application/json")) {
56
+ cache.setJson(key, payload, ttlSeconds ? { ttlSeconds } : void 0);
64
57
  }
65
58
  done(null, payload);
66
59
  }
67
60
  );
68
61
  }
62
+ cachePlugin[/* @__PURE__ */ Symbol.for("skip-override")] = true;
69
63
  // Annotate the CommonJS export names for ESM import in node:
70
64
  0 && (module.exports = {
71
65
  cachePlugin
@@ -1 +1 @@
1
- {"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n * Integração com Fastify: cache de respostas JSON via plugin.\n *\n * Uso:\n * import { Cache } from \"rust-node-cache\";\n * import { cachePlugin } from \"rust-node-cache/fastify\";\n *\n * const cache = new Cache();\n * fastify.register(cachePlugin, { cache, ttlSeconds: 60 });\n *\n * Fluxo: o hook `onRequest` checa o cache e, num HIT, responde imediatamente\n * (curto-circuito). Num MISS, o hook `onSend` guarda o payload serializado.\n */\n\nimport type {\n FastifyInstance,\n FastifyReply,\n FastifyRequest,\n} from \"fastify\";\nimport type { Cache } from \"./index\";\n\nexport interface CachePluginOptions {\n /** Instância de cache a usar. */\n cache: Cache;\n /** TTL aplicado às respostas guardadas (em segundos). */\n ttlSeconds?: number;\n /** Métodos HTTP elegíveis para cache. Padrão: `[\"GET\"]`. */\n methods?: string[];\n /** Gera a chave de cache a partir da requisição. Padrão: `METHOD:url`. */\n keyGenerator?: (req: FastifyRequest) => string;\n}\n\n// Guardamos a chave calculada na própria request entre os hooks.\nconst CACHE_KEY = Symbol(\"rustNodeCacheKey\");\n\n/**\n * Plugin Fastify que cacheia respostas JSON. Use com `fastify.register`.\n */\nexport async function cachePlugin(\n fastify: FastifyInstance,\n options: CachePluginOptions,\n): Promise<void> {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: FastifyRequest) => `${req.method}:${req.url}`);\n\n fastify.addHook(\n \"onRequest\",\n (request: FastifyRequest, reply: FastifyReply, done: () => void) => {\n if (!methods.includes(request.method)) {\n done();\n return;\n }\n\n const key = keyGenerator(request);\n const cached = cache.get(key);\n if (cached !== null) {\n reply.header(\"X-Cache\", \"HIT\");\n reply.send(cached);\n return; // não chama done(): a resposta já foi enviada.\n }\n\n reply.header(\"X-Cache\", \"MISS\");\n (request as unknown as Record<symbol, string>)[CACHE_KEY] = key;\n done();\n },\n );\n\n fastify.addHook(\n \"onSend\",\n (\n request: FastifyRequest,\n reply: FastifyReply,\n payload: unknown,\n done: (err: Error | null, value?: unknown) => void,\n ) => {\n const key = (request as unknown as Record<symbol, string | undefined>)[\n CACHE_KEY\n ];\n if (\n key &&\n typeof payload === \"string\" &&\n reply.statusCode >= 200 &&\n reply.statusCode < 300\n ) {\n try {\n cache.set(\n key,\n JSON.parse(payload),\n ttlSeconds ? { ttlSeconds } : undefined,\n );\n } catch {\n // payload não-JSON: simplesmente não cacheia.\n }\n }\n done(null, payload);\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCA,IAAM,YAAY,uBAAO,kBAAkB;AAK3C,eAAsB,YACpB,SACA,SACe;AACf,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAwB,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG;AAE5E,UAAQ;AAAA,IACN;AAAA,IACA,CAAC,SAAyB,OAAqB,SAAqB;AAClE,UAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,GAAG;AACrC,aAAK;AACL;AAAA,MACF;AAEA,YAAM,MAAM,aAAa,OAAO;AAChC,YAAM,SAAS,MAAM,IAAI,GAAG;AAC5B,UAAI,WAAW,MAAM;AACnB,cAAM,OAAO,WAAW,KAAK;AAC7B,cAAM,KAAK,MAAM;AACjB;AAAA,MACF;AAEA,YAAM,OAAO,WAAW,MAAM;AAC9B,MAAC,QAA8C,SAAS,IAAI;AAC5D,WAAK;AAAA,IACP;AAAA,EACF;AAEA,UAAQ;AAAA,IACN;AAAA,IACA,CACE,SACA,OACA,SACA,SACG;AACH,YAAM,MAAO,QACX,SACF;AACA,UACE,OACA,OAAO,YAAY,YACnB,MAAM,cAAc,OACpB,MAAM,aAAa,KACnB;AACA,YAAI;AACF,gBAAM;AAAA,YACJ;AAAA,YACA,KAAK,MAAM,OAAO;AAAA,YAClB,aAAa,EAAE,WAAW,IAAI;AAAA,UAChC;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AACA,WAAK,MAAM,OAAO;AAAA,IACpB;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n * Fastify integration: caching of JSON responses through a plugin.\n *\n * Usage:\n * import { Cache } from \"rust-node-cache\";\n * import { cachePlugin } from \"rust-node-cache/fastify\";\n *\n * const cache = new Cache();\n * fastify.register(cachePlugin, { cache, ttlSeconds: 60 });\n *\n * Flow: the `onRequest` hook checks the cache and, on a HIT, responds right away\n * (short-circuit). On a MISS, the `onSend` hook stores the serialized payload.\n */\n\nimport type {\n FastifyInstance,\n FastifyReply,\n FastifyRequest,\n} from \"fastify\";\nimport type { Cache } from \"./index\";\n\nexport interface CachePluginOptions {\n /** Cache instance to use. */\n cache: Cache;\n /** TTL applied to stored responses (in seconds). */\n ttlSeconds?: number;\n /** HTTP methods eligible for caching. Default: `[\"GET\"]`. */\n methods?: string[];\n /** Builds the cache key from the request. Default: `METHOD:url`. */\n keyGenerator?: (req: FastifyRequest) => string;\n}\n\n// The computed key is kept on the request itself between hooks.\nconst CACHE_KEY = Symbol(\"rustNodeCacheKey\");\n\n/**\n * Fastify plugin that caches JSON responses. Use it with `fastify.register`.\n */\nexport async function cachePlugin(\n fastify: FastifyInstance,\n options: CachePluginOptions,\n): Promise<void> {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: FastifyRequest) => `${req.method}:${req.url}`);\n\n fastify.addHook(\n \"onRequest\",\n (request: FastifyRequest, reply: FastifyReply, done: () => void) => {\n if (!methods.includes(request.method)) {\n done();\n return;\n }\n\n const key = keyGenerator(request);\n const cached = cache.getJson(key);\n if (cached !== null) {\n reply.header(\"X-Cache\", \"HIT\");\n reply.type(\"application/json; charset=utf-8\").send(cached);\n return; // done() is not called: the response was already sent.\n }\n\n reply.header(\"X-Cache\", \"MISS\");\n (request as unknown as Record<symbol, string>)[CACHE_KEY] = key;\n done();\n },\n );\n\n fastify.addHook(\n \"onSend\",\n (\n request: FastifyRequest,\n reply: FastifyReply,\n payload: unknown,\n done: (err: Error | null, value?: unknown) => void,\n ) => {\n const key = (request as unknown as Record<symbol, string | undefined>)[\n CACHE_KEY\n ];\n // The payload is already the serialized body: store that text as is.\n // Only JSON responses are cached.\n if (\n key &&\n typeof payload === \"string\" &&\n reply.statusCode >= 200 &&\n reply.statusCode < 300 &&\n String(reply.getHeader(\"content-type\") ?? \"\").startsWith(\"application/json\")\n ) {\n cache.setJson(key, payload, ttlSeconds ? { ttlSeconds } : undefined);\n }\n done(null, payload);\n },\n );\n}\n\n// Disables Fastify encapsulation so the hooks apply to the scope where the\n// plugin is registered (the same as wrapping it with `fastify-plugin`). Without\n// it, routes registered as \"siblings\" of the plugin are never cached.\n(cachePlugin as unknown as Record<symbol, boolean>)[\n Symbol.for(\"skip-override\")\n] = true;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCA,IAAM,YAAY,uBAAO,kBAAkB;AAK3C,eAAsB,YACpB,SACA,SACe;AACf,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAwB,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG;AAE5E,UAAQ;AAAA,IACN;AAAA,IACA,CAAC,SAAyB,OAAqB,SAAqB;AAClE,UAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,GAAG;AACrC,aAAK;AACL;AAAA,MACF;AAEA,YAAM,MAAM,aAAa,OAAO;AAChC,YAAM,SAAS,MAAM,QAAQ,GAAG;AAChC,UAAI,WAAW,MAAM;AACnB,cAAM,OAAO,WAAW,KAAK;AAC7B,cAAM,KAAK,iCAAiC,EAAE,KAAK,MAAM;AACzD;AAAA,MACF;AAEA,YAAM,OAAO,WAAW,MAAM;AAC9B,MAAC,QAA8C,SAAS,IAAI;AAC5D,WAAK;AAAA,IACP;AAAA,EACF;AAEA,UAAQ;AAAA,IACN;AAAA,IACA,CACE,SACA,OACA,SACA,SACG;AACH,YAAM,MAAO,QACX,SACF;AAGA,UACE,OACA,OAAO,YAAY,YACnB,MAAM,cAAc,OACpB,MAAM,aAAa,OACnB,OAAO,MAAM,UAAU,cAAc,KAAK,EAAE,EAAE,WAAW,kBAAkB,GAC3E;AACA,cAAM,QAAQ,KAAK,SAAS,aAAa,EAAE,WAAW,IAAI,MAAS;AAAA,MACrE;AACA,WAAK,MAAM,OAAO;AAAA,IACpB;AAAA,EACF;AACF;AAKC,YACC,uBAAO,IAAI,eAAe,CAC5B,IAAI;","names":[]}
package/dist/fastify.mjs CHANGED
@@ -13,10 +13,10 @@ async function cachePlugin(fastify, options) {
13
13
  return;
14
14
  }
15
15
  const key = keyGenerator(request);
16
- const cached = cache.get(key);
16
+ const cached = cache.getJson(key);
17
17
  if (cached !== null) {
18
18
  reply.header("X-Cache", "HIT");
19
- reply.send(cached);
19
+ reply.type("application/json; charset=utf-8").send(cached);
20
20
  return;
21
21
  }
22
22
  reply.header("X-Cache", "MISS");
@@ -28,20 +28,14 @@ async function cachePlugin(fastify, options) {
28
28
  "onSend",
29
29
  (request, reply, payload, done) => {
30
30
  const key = request[CACHE_KEY];
31
- if (key && typeof payload === "string" && reply.statusCode >= 200 && reply.statusCode < 300) {
32
- try {
33
- cache.set(
34
- key,
35
- JSON.parse(payload),
36
- ttlSeconds ? { ttlSeconds } : void 0
37
- );
38
- } catch {
39
- }
31
+ if (key && typeof payload === "string" && reply.statusCode >= 200 && reply.statusCode < 300 && String(reply.getHeader("content-type") ?? "").startsWith("application/json")) {
32
+ cache.setJson(key, payload, ttlSeconds ? { ttlSeconds } : void 0);
40
33
  }
41
34
  done(null, payload);
42
35
  }
43
36
  );
44
37
  }
38
+ cachePlugin[/* @__PURE__ */ Symbol.for("skip-override")] = true;
45
39
  export {
46
40
  cachePlugin
47
41
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n * Integração com Fastify: cache de respostas JSON via plugin.\n *\n * Uso:\n * import { Cache } from \"rust-node-cache\";\n * import { cachePlugin } from \"rust-node-cache/fastify\";\n *\n * const cache = new Cache();\n * fastify.register(cachePlugin, { cache, ttlSeconds: 60 });\n *\n * Fluxo: o hook `onRequest` checa o cache e, num HIT, responde imediatamente\n * (curto-circuito). Num MISS, o hook `onSend` guarda o payload serializado.\n */\n\nimport type {\n FastifyInstance,\n FastifyReply,\n FastifyRequest,\n} from \"fastify\";\nimport type { Cache } from \"./index\";\n\nexport interface CachePluginOptions {\n /** Instância de cache a usar. */\n cache: Cache;\n /** TTL aplicado às respostas guardadas (em segundos). */\n ttlSeconds?: number;\n /** Métodos HTTP elegíveis para cache. Padrão: `[\"GET\"]`. */\n methods?: string[];\n /** Gera a chave de cache a partir da requisição. Padrão: `METHOD:url`. */\n keyGenerator?: (req: FastifyRequest) => string;\n}\n\n// Guardamos a chave calculada na própria request entre os hooks.\nconst CACHE_KEY = Symbol(\"rustNodeCacheKey\");\n\n/**\n * Plugin Fastify que cacheia respostas JSON. Use com `fastify.register`.\n */\nexport async function cachePlugin(\n fastify: FastifyInstance,\n options: CachePluginOptions,\n): Promise<void> {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: FastifyRequest) => `${req.method}:${req.url}`);\n\n fastify.addHook(\n \"onRequest\",\n (request: FastifyRequest, reply: FastifyReply, done: () => void) => {\n if (!methods.includes(request.method)) {\n done();\n return;\n }\n\n const key = keyGenerator(request);\n const cached = cache.get(key);\n if (cached !== null) {\n reply.header(\"X-Cache\", \"HIT\");\n reply.send(cached);\n return; // não chama done(): a resposta já foi enviada.\n }\n\n reply.header(\"X-Cache\", \"MISS\");\n (request as unknown as Record<symbol, string>)[CACHE_KEY] = key;\n done();\n },\n );\n\n fastify.addHook(\n \"onSend\",\n (\n request: FastifyRequest,\n reply: FastifyReply,\n payload: unknown,\n done: (err: Error | null, value?: unknown) => void,\n ) => {\n const key = (request as unknown as Record<symbol, string | undefined>)[\n CACHE_KEY\n ];\n if (\n key &&\n typeof payload === \"string\" &&\n reply.statusCode >= 200 &&\n reply.statusCode < 300\n ) {\n try {\n cache.set(\n key,\n JSON.parse(payload),\n ttlSeconds ? { ttlSeconds } : undefined,\n );\n } catch {\n // payload não-JSON: simplesmente não cacheia.\n }\n }\n done(null, payload);\n },\n );\n}\n"],"mappings":";AAiCA,IAAM,YAAY,uBAAO,kBAAkB;AAK3C,eAAsB,YACpB,SACA,SACe;AACf,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAwB,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG;AAE5E,UAAQ;AAAA,IACN;AAAA,IACA,CAAC,SAAyB,OAAqB,SAAqB;AAClE,UAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,GAAG;AACrC,aAAK;AACL;AAAA,MACF;AAEA,YAAM,MAAM,aAAa,OAAO;AAChC,YAAM,SAAS,MAAM,IAAI,GAAG;AAC5B,UAAI,WAAW,MAAM;AACnB,cAAM,OAAO,WAAW,KAAK;AAC7B,cAAM,KAAK,MAAM;AACjB;AAAA,MACF;AAEA,YAAM,OAAO,WAAW,MAAM;AAC9B,MAAC,QAA8C,SAAS,IAAI;AAC5D,WAAK;AAAA,IACP;AAAA,EACF;AAEA,UAAQ;AAAA,IACN;AAAA,IACA,CACE,SACA,OACA,SACA,SACG;AACH,YAAM,MAAO,QACX,SACF;AACA,UACE,OACA,OAAO,YAAY,YACnB,MAAM,cAAc,OACpB,MAAM,aAAa,KACnB;AACA,YAAI;AACF,gBAAM;AAAA,YACJ;AAAA,YACA,KAAK,MAAM,OAAO;AAAA,YAClB,aAAa,EAAE,WAAW,IAAI;AAAA,UAChC;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AACA,WAAK,MAAM,OAAO;AAAA,IACpB;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n * Fastify integration: caching of JSON responses through a plugin.\n *\n * Usage:\n * import { Cache } from \"rust-node-cache\";\n * import { cachePlugin } from \"rust-node-cache/fastify\";\n *\n * const cache = new Cache();\n * fastify.register(cachePlugin, { cache, ttlSeconds: 60 });\n *\n * Flow: the `onRequest` hook checks the cache and, on a HIT, responds right away\n * (short-circuit). On a MISS, the `onSend` hook stores the serialized payload.\n */\n\nimport type {\n FastifyInstance,\n FastifyReply,\n FastifyRequest,\n} from \"fastify\";\nimport type { Cache } from \"./index\";\n\nexport interface CachePluginOptions {\n /** Cache instance to use. */\n cache: Cache;\n /** TTL applied to stored responses (in seconds). */\n ttlSeconds?: number;\n /** HTTP methods eligible for caching. Default: `[\"GET\"]`. */\n methods?: string[];\n /** Builds the cache key from the request. Default: `METHOD:url`. */\n keyGenerator?: (req: FastifyRequest) => string;\n}\n\n// The computed key is kept on the request itself between hooks.\nconst CACHE_KEY = Symbol(\"rustNodeCacheKey\");\n\n/**\n * Fastify plugin that caches JSON responses. Use it with `fastify.register`.\n */\nexport async function cachePlugin(\n fastify: FastifyInstance,\n options: CachePluginOptions,\n): Promise<void> {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: FastifyRequest) => `${req.method}:${req.url}`);\n\n fastify.addHook(\n \"onRequest\",\n (request: FastifyRequest, reply: FastifyReply, done: () => void) => {\n if (!methods.includes(request.method)) {\n done();\n return;\n }\n\n const key = keyGenerator(request);\n const cached = cache.getJson(key);\n if (cached !== null) {\n reply.header(\"X-Cache\", \"HIT\");\n reply.type(\"application/json; charset=utf-8\").send(cached);\n return; // done() is not called: the response was already sent.\n }\n\n reply.header(\"X-Cache\", \"MISS\");\n (request as unknown as Record<symbol, string>)[CACHE_KEY] = key;\n done();\n },\n );\n\n fastify.addHook(\n \"onSend\",\n (\n request: FastifyRequest,\n reply: FastifyReply,\n payload: unknown,\n done: (err: Error | null, value?: unknown) => void,\n ) => {\n const key = (request as unknown as Record<symbol, string | undefined>)[\n CACHE_KEY\n ];\n // The payload is already the serialized body: store that text as is.\n // Only JSON responses are cached.\n if (\n key &&\n typeof payload === \"string\" &&\n reply.statusCode >= 200 &&\n reply.statusCode < 300 &&\n String(reply.getHeader(\"content-type\") ?? \"\").startsWith(\"application/json\")\n ) {\n cache.setJson(key, payload, ttlSeconds ? { ttlSeconds } : undefined);\n }\n done(null, payload);\n },\n );\n}\n\n// Disables Fastify encapsulation so the hooks apply to the scope where the\n// plugin is registered (the same as wrapping it with `fastify-plugin`). Without\n// it, routes registered as \"siblings\" of the plugin are never cached.\n(cachePlugin as unknown as Record<symbol, boolean>)[\n Symbol.for(\"skip-override\")\n] = true;\n"],"mappings":";AAiCA,IAAM,YAAY,uBAAO,kBAAkB;AAK3C,eAAsB,YACpB,SACA,SACe;AACf,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAwB,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG;AAE5E,UAAQ;AAAA,IACN;AAAA,IACA,CAAC,SAAyB,OAAqB,SAAqB;AAClE,UAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,GAAG;AACrC,aAAK;AACL;AAAA,MACF;AAEA,YAAM,MAAM,aAAa,OAAO;AAChC,YAAM,SAAS,MAAM,QAAQ,GAAG;AAChC,UAAI,WAAW,MAAM;AACnB,cAAM,OAAO,WAAW,KAAK;AAC7B,cAAM,KAAK,iCAAiC,EAAE,KAAK,MAAM;AACzD;AAAA,MACF;AAEA,YAAM,OAAO,WAAW,MAAM;AAC9B,MAAC,QAA8C,SAAS,IAAI;AAC5D,WAAK;AAAA,IACP;AAAA,EACF;AAEA,UAAQ;AAAA,IACN;AAAA,IACA,CACE,SACA,OACA,SACA,SACG;AACH,YAAM,MAAO,QACX,SACF;AAGA,UACE,OACA,OAAO,YAAY,YACnB,MAAM,cAAc,OACpB,MAAM,aAAa,OACnB,OAAO,MAAM,UAAU,cAAc,KAAK,EAAE,EAAE,WAAW,kBAAkB,GAC3E;AACA,cAAM,QAAQ,KAAK,SAAS,aAAa,EAAE,WAAW,IAAI,MAAS;AAAA,MACrE;AACA,WAAK,MAAM,OAAO;AAAA,IACpB;AAAA,EACF;AACF;AAKC,YACC,uBAAO,IAAI,eAAe,CAC5B,IAAI;","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,63 +1,66 @@
1
1
  /**
2
- * rust-node-cache — API pública (TypeScript).
2
+ * rust-node-cache — public API (TypeScript).
3
3
  *
4
- * "Ultra-fast in-memory cache powered by Rust."
4
+ * "In-memory cache for Node.js that keeps your data off the V8 heap."
5
5
  *
6
- * Esta camada é um wrapper fino e tipado sobre a classe `Cache` nativa gerada
7
- * pelo Rust/napi-rs. Por que envolver em vez de re-exportar direto?
8
- * - permite **genéricos** ergonômicos: `cache.get<User>("user:1")`;
9
- * - normaliza `undefined`/`null` para sempre devolver `null` num miss;
10
- * - dá um único lugar para JSDoc e para evoluir a API sem quebrar o core Rust.
11
- *
12
- * O custo é uma chamada de função extra por operação — desprezível frente ao
13
- * trabalho real (serialização + acesso ao DashMap) que acontece no Rust.
6
+ * This layer is a typed wrapper over the native `Cache` class generated by
7
+ * Rust/napi-rs. It does the JSON encoding on the JavaScript side, where V8's
8
+ * `JSON.stringify`/`JSON.parse` are fastest, and hands Rust plain strings:
9
+ * - `set` validates the value (see `./encode`) and stores its JSON text;
10
+ * - `get` parses the stored text back, so every read returns a fresh copy;
11
+ * - it allows ergonomic **generics**: `cache.get<User>("user:1")`.
14
12
  */
15
- /** Estratégia de evicção quando `maxSize` é atingido. */
13
+ /** Eviction strategy when `maxSize` is reached. */
16
14
  type EvictionPolicy = "reject" | "lru";
17
- /** Opções do construtor do cache. */
15
+ /** Cache constructor options. */
18
16
  interface CacheOptions {
19
17
  /**
20
- * Limite máximo de chaves. O que acontece ao atingir depende de
21
- * `evictionPolicy`. Sem valor => cache ilimitado.
18
+ * Maximum number of keys (a positive integer). What happens when it is
19
+ * reached depends on `evictionPolicy`. No value => unlimited cache.
22
20
  */
23
21
  maxSize?: number;
24
22
  /**
25
- * Política ao atingir `maxSize` numa chave **nova**:
26
- * - `"reject"` (padrão): `set` retorna `false` (sobrescrever continua ok);
27
- * - `"lru"`: remove a entrada menos recentemente usada e prossegue.
23
+ * Policy when `maxSize` is reached on a **new** key:
24
+ * - `"reject"` (default): `set` returns `false` (overwriting still works);
25
+ * - `"lru"`: removes the least recently used entry and goes on.
28
26
  */
29
27
  evictionPolicy?: EvictionPolicy;
30
28
  /**
31
- * Se definido (> 0), liga uma thread que varre entradas expiradas a cada
32
- * N segundos (expiração em background, além da preguiçosa por acesso).
29
+ * When set (> 0), starts a thread that sweeps expired entries every N
30
+ * seconds (background expiration, on top of the lazy one on access).
31
+ * Fractions are allowed; `0` or no value leaves it off.
33
32
  */
34
33
  cleanupIntervalSeconds?: number;
35
34
  }
36
- /** Opções por operação de escrita. */
35
+ /** Per-write options. */
37
36
  interface SetOptions {
38
- /** Tempo de vida em segundos. Ausente => a entrada não expira por tempo. */
37
+ /**
38
+ * Time to live in seconds; fractions are allowed (`0.5` = 500 ms, minimum
39
+ * 1 ms). Absent => the entry does not expire by time.
40
+ */
39
41
  ttlSeconds?: number;
40
42
  }
41
- /** Estatísticas acumuladas do cache (mais o tamanho atual). */
43
+ /** Accumulated cache statistics (plus the current size). */
42
44
  interface CacheStats {
43
- /** Leituras que encontraram uma entrada válida. */
45
+ /** Reads that found a valid entry. */
44
46
  hits: number;
45
- /** Leituras sem chave válida (ausente ou expirada). */
47
+ /** Reads without a valid key (missing or expired). */
46
48
  misses: number;
47
- /** Total de escritas (`set`). */
49
+ /** Total writes (`set`). */
48
50
  sets: number;
49
- /** Total de remoções explícitas (`delete`). */
51
+ /** Total explicit removals (`delete`). */
50
52
  deletes: number;
51
- /** Total de entradas removidas por expiração. */
53
+ /** Total entries removed by expiration. */
52
54
  expired: number;
53
- /** Total de entradas removidas por evicção (política LRU ao atingir `maxSize`). */
55
+ /** Total entries removed by eviction (LRU policy when `maxSize` is reached). */
54
56
  evicted: number;
55
- /** Número de chaves armazenadas agora. */
57
+ /** Number of keys stored right now. */
56
58
  size: number;
57
59
  }
58
60
  /**
59
- * Cache em memória ultrarrápido com núcleo em Rust (DashMap + contadores
60
- * atômicos). Thread-safe e com suporte a TTL por entrada.
61
+ * In-memory cache whose entries live in Rust memory, outside the V8 heap.
62
+ * Exact LRU eviction (O(1)), an optional TTL per entry and an optional
63
+ * background sweeper.
61
64
  *
62
65
  * @example
63
66
  * ```ts
@@ -69,38 +72,64 @@ interface CacheStats {
69
72
  * ```
70
73
  */
71
74
  declare class Cache {
72
- /** Instância da classe nativa (Rust). */
75
+ /** Instance of the native (Rust) class. */
73
76
  private readonly native;
77
+ /** @throws if `maxSize`, `evictionPolicy` or `cleanupIntervalSeconds` is invalid. */
74
78
  constructor(options?: CacheOptions);
75
- /** A política de evicção ativa: `"reject"` ou `"lru"`. */
79
+ /** The active eviction policy: `"reject"` or `"lru"`. */
76
80
  get evictionPolicy(): EvictionPolicy;
77
81
  /**
78
- * Armazena (ou sobrescreve) um valor. O valor é serializado como JSON no core.
82
+ * Stores (or overwrites) a value, kept as JSON. Accepts plain objects,
83
+ * arrays, strings, finite numbers, booleans and `null`; object properties
84
+ * set to `undefined` are left out, as in JSON.
79
85
  *
80
- * @returns `true` em sucesso; `false` se `maxSize` foi atingido e a chave é nova.
86
+ * @returns `true` on success; `false` if `maxSize` was reached and the key is new.
87
+ * @throws TypeError naming the offending path if the value holds something
88
+ * JSON cannot represent (a `Date`, `Map`, `Set`, `BigInt`, class instance,
89
+ * function, `NaN`, `Infinity`, an `undefined` array item, or `undefined`
90
+ * itself) — instead of storing a silently different value.
81
91
  */
82
92
  set<T>(key: string, value: T, options?: SetOptions): boolean;
83
93
  /**
84
- * Lê um valor. Aplica expiração preguiçosa: chaves vencidas contam como miss.
94
+ * Reads a value. Applies lazy expiration: expired keys count as a miss.
95
+ * Each call returns a new copy, so mutating it does not change the cache.
85
96
  *
86
- * @returns o valor desserializado, ou `null` se ausente/expirado.
97
+ * @returns the stored value, or `null` if missing or expired.
87
98
  */
88
99
  get<T = unknown>(key: string): T | null;
89
- /** Remove uma chave. Retorna `true` se ela existia. */
100
+ /**
101
+ * Stores text that is **already JSON**, as is — no validation, no
102
+ * re-encoding. Meant for payloads serialized elsewhere, such as an HTTP
103
+ * response body. Storing text that is not valid JSON makes `get()` throw.
104
+ *
105
+ * @returns `true` on success; `false` if `maxSize` was reached and the key is new.
106
+ */
107
+ setJson(key: string, json: string, options?: SetOptions): boolean;
108
+ /**
109
+ * Reads a value as its JSON text, without parsing it — e.g. to send it as
110
+ * an HTTP response body directly. Same expiration and stats as `get()`.
111
+ *
112
+ * @returns the JSON text, or `null` if missing or expired.
113
+ */
114
+ getJson(key: string): string | null;
115
+ /** Removes a key. Returns `true` if it existed. */
90
116
  delete(key: string): boolean;
91
- /** Indica se a chave existe e está válida (não expirada). */
117
+ /**
118
+ * Whether the key exists and is still valid (not expired). Does not count
119
+ * as a use for LRU eviction.
120
+ */
92
121
  exists(key: string): boolean;
93
- /** Esvazia o cache (não zera os contadores históricos de `stats`). */
122
+ /** Empties the cache (does not reset the historical `stats` counters). */
94
123
  clear(): void;
95
- /** Número de chaves armazenadas no momento. */
124
+ /** Number of keys currently stored. */
96
125
  size(): number;
97
126
  /**
98
- * Varre e remove todas as entradas expiradas de uma só vez.
127
+ * Sweeps and removes every expired entry at once.
99
128
  *
100
- * @returns quantas entradas foram removidas.
129
+ * @returns how many entries were removed.
101
130
  */
102
131
  cleanupExpired(): number;
103
- /** Retorna as estatísticas acumuladas + o tamanho atual. */
132
+ /** Returns the accumulated statistics + the current size. */
104
133
  stats(): CacheStats;
105
134
  }
106
135
 
package/dist/index.d.ts CHANGED
@@ -1,63 +1,66 @@
1
1
  /**
2
- * rust-node-cache — API pública (TypeScript).
2
+ * rust-node-cache — public API (TypeScript).
3
3
  *
4
- * "Ultra-fast in-memory cache powered by Rust."
4
+ * "In-memory cache for Node.js that keeps your data off the V8 heap."
5
5
  *
6
- * Esta camada é um wrapper fino e tipado sobre a classe `Cache` nativa gerada
7
- * pelo Rust/napi-rs. Por que envolver em vez de re-exportar direto?
8
- * - permite **genéricos** ergonômicos: `cache.get<User>("user:1")`;
9
- * - normaliza `undefined`/`null` para sempre devolver `null` num miss;
10
- * - dá um único lugar para JSDoc e para evoluir a API sem quebrar o core Rust.
11
- *
12
- * O custo é uma chamada de função extra por operação — desprezível frente ao
13
- * trabalho real (serialização + acesso ao DashMap) que acontece no Rust.
6
+ * This layer is a typed wrapper over the native `Cache` class generated by
7
+ * Rust/napi-rs. It does the JSON encoding on the JavaScript side, where V8's
8
+ * `JSON.stringify`/`JSON.parse` are fastest, and hands Rust plain strings:
9
+ * - `set` validates the value (see `./encode`) and stores its JSON text;
10
+ * - `get` parses the stored text back, so every read returns a fresh copy;
11
+ * - it allows ergonomic **generics**: `cache.get<User>("user:1")`.
14
12
  */
15
- /** Estratégia de evicção quando `maxSize` é atingido. */
13
+ /** Eviction strategy when `maxSize` is reached. */
16
14
  type EvictionPolicy = "reject" | "lru";
17
- /** Opções do construtor do cache. */
15
+ /** Cache constructor options. */
18
16
  interface CacheOptions {
19
17
  /**
20
- * Limite máximo de chaves. O que acontece ao atingir depende de
21
- * `evictionPolicy`. Sem valor => cache ilimitado.
18
+ * Maximum number of keys (a positive integer). What happens when it is
19
+ * reached depends on `evictionPolicy`. No value => unlimited cache.
22
20
  */
23
21
  maxSize?: number;
24
22
  /**
25
- * Política ao atingir `maxSize` numa chave **nova**:
26
- * - `"reject"` (padrão): `set` retorna `false` (sobrescrever continua ok);
27
- * - `"lru"`: remove a entrada menos recentemente usada e prossegue.
23
+ * Policy when `maxSize` is reached on a **new** key:
24
+ * - `"reject"` (default): `set` returns `false` (overwriting still works);
25
+ * - `"lru"`: removes the least recently used entry and goes on.
28
26
  */
29
27
  evictionPolicy?: EvictionPolicy;
30
28
  /**
31
- * Se definido (> 0), liga uma thread que varre entradas expiradas a cada
32
- * N segundos (expiração em background, além da preguiçosa por acesso).
29
+ * When set (> 0), starts a thread that sweeps expired entries every N
30
+ * seconds (background expiration, on top of the lazy one on access).
31
+ * Fractions are allowed; `0` or no value leaves it off.
33
32
  */
34
33
  cleanupIntervalSeconds?: number;
35
34
  }
36
- /** Opções por operação de escrita. */
35
+ /** Per-write options. */
37
36
  interface SetOptions {
38
- /** Tempo de vida em segundos. Ausente => a entrada não expira por tempo. */
37
+ /**
38
+ * Time to live in seconds; fractions are allowed (`0.5` = 500 ms, minimum
39
+ * 1 ms). Absent => the entry does not expire by time.
40
+ */
39
41
  ttlSeconds?: number;
40
42
  }
41
- /** Estatísticas acumuladas do cache (mais o tamanho atual). */
43
+ /** Accumulated cache statistics (plus the current size). */
42
44
  interface CacheStats {
43
- /** Leituras que encontraram uma entrada válida. */
45
+ /** Reads that found a valid entry. */
44
46
  hits: number;
45
- /** Leituras sem chave válida (ausente ou expirada). */
47
+ /** Reads without a valid key (missing or expired). */
46
48
  misses: number;
47
- /** Total de escritas (`set`). */
49
+ /** Total writes (`set`). */
48
50
  sets: number;
49
- /** Total de remoções explícitas (`delete`). */
51
+ /** Total explicit removals (`delete`). */
50
52
  deletes: number;
51
- /** Total de entradas removidas por expiração. */
53
+ /** Total entries removed by expiration. */
52
54
  expired: number;
53
- /** Total de entradas removidas por evicção (política LRU ao atingir `maxSize`). */
55
+ /** Total entries removed by eviction (LRU policy when `maxSize` is reached). */
54
56
  evicted: number;
55
- /** Número de chaves armazenadas agora. */
57
+ /** Number of keys stored right now. */
56
58
  size: number;
57
59
  }
58
60
  /**
59
- * Cache em memória ultrarrápido com núcleo em Rust (DashMap + contadores
60
- * atômicos). Thread-safe e com suporte a TTL por entrada.
61
+ * In-memory cache whose entries live in Rust memory, outside the V8 heap.
62
+ * Exact LRU eviction (O(1)), an optional TTL per entry and an optional
63
+ * background sweeper.
61
64
  *
62
65
  * @example
63
66
  * ```ts
@@ -69,38 +72,64 @@ interface CacheStats {
69
72
  * ```
70
73
  */
71
74
  declare class Cache {
72
- /** Instância da classe nativa (Rust). */
75
+ /** Instance of the native (Rust) class. */
73
76
  private readonly native;
77
+ /** @throws if `maxSize`, `evictionPolicy` or `cleanupIntervalSeconds` is invalid. */
74
78
  constructor(options?: CacheOptions);
75
- /** A política de evicção ativa: `"reject"` ou `"lru"`. */
79
+ /** The active eviction policy: `"reject"` or `"lru"`. */
76
80
  get evictionPolicy(): EvictionPolicy;
77
81
  /**
78
- * Armazena (ou sobrescreve) um valor. O valor é serializado como JSON no core.
82
+ * Stores (or overwrites) a value, kept as JSON. Accepts plain objects,
83
+ * arrays, strings, finite numbers, booleans and `null`; object properties
84
+ * set to `undefined` are left out, as in JSON.
79
85
  *
80
- * @returns `true` em sucesso; `false` se `maxSize` foi atingido e a chave é nova.
86
+ * @returns `true` on success; `false` if `maxSize` was reached and the key is new.
87
+ * @throws TypeError naming the offending path if the value holds something
88
+ * JSON cannot represent (a `Date`, `Map`, `Set`, `BigInt`, class instance,
89
+ * function, `NaN`, `Infinity`, an `undefined` array item, or `undefined`
90
+ * itself) — instead of storing a silently different value.
81
91
  */
82
92
  set<T>(key: string, value: T, options?: SetOptions): boolean;
83
93
  /**
84
- * Lê um valor. Aplica expiração preguiçosa: chaves vencidas contam como miss.
94
+ * Reads a value. Applies lazy expiration: expired keys count as a miss.
95
+ * Each call returns a new copy, so mutating it does not change the cache.
85
96
  *
86
- * @returns o valor desserializado, ou `null` se ausente/expirado.
97
+ * @returns the stored value, or `null` if missing or expired.
87
98
  */
88
99
  get<T = unknown>(key: string): T | null;
89
- /** Remove uma chave. Retorna `true` se ela existia. */
100
+ /**
101
+ * Stores text that is **already JSON**, as is — no validation, no
102
+ * re-encoding. Meant for payloads serialized elsewhere, such as an HTTP
103
+ * response body. Storing text that is not valid JSON makes `get()` throw.
104
+ *
105
+ * @returns `true` on success; `false` if `maxSize` was reached and the key is new.
106
+ */
107
+ setJson(key: string, json: string, options?: SetOptions): boolean;
108
+ /**
109
+ * Reads a value as its JSON text, without parsing it — e.g. to send it as
110
+ * an HTTP response body directly. Same expiration and stats as `get()`.
111
+ *
112
+ * @returns the JSON text, or `null` if missing or expired.
113
+ */
114
+ getJson(key: string): string | null;
115
+ /** Removes a key. Returns `true` if it existed. */
90
116
  delete(key: string): boolean;
91
- /** Indica se a chave existe e está válida (não expirada). */
117
+ /**
118
+ * Whether the key exists and is still valid (not expired). Does not count
119
+ * as a use for LRU eviction.
120
+ */
92
121
  exists(key: string): boolean;
93
- /** Esvazia o cache (não zera os contadores históricos de `stats`). */
122
+ /** Empties the cache (does not reset the historical `stats` counters). */
94
123
  clear(): void;
95
- /** Número de chaves armazenadas no momento. */
124
+ /** Number of keys currently stored. */
96
125
  size(): number;
97
126
  /**
98
- * Varre e remove todas as entradas expiradas de uma só vez.
127
+ * Sweeps and removes every expired entry at once.
99
128
  *
100
- * @returns quantas entradas foram removidas.
129
+ * @returns how many entries were removed.
101
130
  */
102
131
  cleanupExpired(): number;
103
- /** Retorna as estatísticas acumuladas + o tamanho atual. */
132
+ /** Returns the accumulated statistics + the current size. */
104
133
  stats(): CacheStats;
105
134
  }
106
135