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.
- package/README.md +145 -48
- package/binding.d.ts +35 -27
- package/dist/express.d.mts +14 -10
- package/dist/express.d.ts +14 -10
- package/dist/express.js +13 -3
- package/dist/express.js.map +1 -1
- package/dist/express.mjs +13 -3
- package/dist/express.mjs.map +1 -1
- package/dist/fastify.d.mts +9 -9
- package/dist/fastify.d.ts +9 -9
- package/dist/fastify.js +5 -11
- package/dist/fastify.js.map +1 -1
- package/dist/fastify.mjs +5 -11
- package/dist/fastify.mjs.map +1 -1
- package/dist/index.d.mts +73 -44
- package/dist/index.d.ts +73 -44
- package/dist/index.js +128 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +128 -15
- package/dist/index.mjs.map +1 -1
- package/dist/nestjs.d.mts +11 -11
- package/dist/nestjs.d.ts +11 -11
- package/dist/nestjs.js +11 -3
- package/dist/nestjs.js.map +1 -1
- package/dist/nestjs.mjs +11 -3
- package/dist/nestjs.mjs.map +1 -1
- package/package.json +7 -4
- package/rust-node-cache.darwin-arm64.node +0 -0
- package/rust-node-cache.darwin-x64.node +0 -0
- package/rust-node-cache.linux-arm64-gnu.node +0 -0
- package/rust-node-cache.linux-arm64-musl.node +0 -0
- package/rust-node-cache.linux-x64-gnu.node +0 -0
- package/rust-node-cache.linux-x64-musl.node +0 -0
- package/rust-node-cache.win32-x64-msvc.node +0 -0
package/dist/express.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../js/express.ts"],"sourcesContent":["/**\n *
|
|
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":[]}
|
package/dist/fastify.d.mts
CHANGED
|
@@ -2,31 +2,31 @@ import { FastifyRequest, FastifyInstance } from 'fastify';
|
|
|
2
2
|
import { Cache } from './index.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Fastify integration: caching of JSON responses through a plugin.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
15
|
-
* (
|
|
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
|
-
/**
|
|
19
|
+
/** Cache instance to use. */
|
|
20
20
|
cache: Cache;
|
|
21
|
-
/** TTL
|
|
21
|
+
/** TTL applied to stored responses (in seconds). */
|
|
22
22
|
ttlSeconds?: number;
|
|
23
|
-
/**
|
|
23
|
+
/** HTTP methods eligible for caching. Default: `["GET"]`. */
|
|
24
24
|
methods?: string[];
|
|
25
|
-
/**
|
|
25
|
+
/** Builds the cache key from the request. Default: `METHOD:url`. */
|
|
26
26
|
keyGenerator?: (req: FastifyRequest) => string;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
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
|
-
*
|
|
5
|
+
* Fastify integration: caching of JSON responses through a plugin.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
15
|
-
* (
|
|
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
|
-
/**
|
|
19
|
+
/** Cache instance to use. */
|
|
20
20
|
cache: Cache;
|
|
21
|
-
/** TTL
|
|
21
|
+
/** TTL applied to stored responses (in seconds). */
|
|
22
22
|
ttlSeconds?: number;
|
|
23
|
-
/**
|
|
23
|
+
/** HTTP methods eligible for caching. Default: `["GET"]`. */
|
|
24
24
|
methods?: string[];
|
|
25
|
-
/**
|
|
25
|
+
/** Builds the cache key from the request. Default: `METHOD:url`. */
|
|
26
26
|
keyGenerator?: (req: FastifyRequest) => string;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
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.
|
|
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
|
-
|
|
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
|
package/dist/fastify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n *
|
|
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.
|
|
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
|
-
|
|
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
|
};
|
package/dist/fastify.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n *
|
|
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
|
|
2
|
+
* rust-node-cache — public API (TypeScript).
|
|
3
3
|
*
|
|
4
|
-
* "
|
|
4
|
+
* "In-memory cache for Node.js that keeps your data off the V8 heap."
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
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
|
-
/**
|
|
13
|
+
/** Eviction strategy when `maxSize` is reached. */
|
|
16
14
|
type EvictionPolicy = "reject" | "lru";
|
|
17
|
-
/**
|
|
15
|
+
/** Cache constructor options. */
|
|
18
16
|
interface CacheOptions {
|
|
19
17
|
/**
|
|
20
|
-
*
|
|
21
|
-
* `evictionPolicy`.
|
|
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
|
-
*
|
|
26
|
-
* - `"reject"` (
|
|
27
|
-
* - `"lru"`:
|
|
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
|
-
*
|
|
32
|
-
*
|
|
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
|
-
/**
|
|
35
|
+
/** Per-write options. */
|
|
37
36
|
interface SetOptions {
|
|
38
|
-
/**
|
|
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
|
-
/**
|
|
43
|
+
/** Accumulated cache statistics (plus the current size). */
|
|
42
44
|
interface CacheStats {
|
|
43
|
-
/**
|
|
45
|
+
/** Reads that found a valid entry. */
|
|
44
46
|
hits: number;
|
|
45
|
-
/**
|
|
47
|
+
/** Reads without a valid key (missing or expired). */
|
|
46
48
|
misses: number;
|
|
47
|
-
/** Total
|
|
49
|
+
/** Total writes (`set`). */
|
|
48
50
|
sets: number;
|
|
49
|
-
/** Total
|
|
51
|
+
/** Total explicit removals (`delete`). */
|
|
50
52
|
deletes: number;
|
|
51
|
-
/** Total
|
|
53
|
+
/** Total entries removed by expiration. */
|
|
52
54
|
expired: number;
|
|
53
|
-
/** Total
|
|
55
|
+
/** Total entries removed by eviction (LRU policy when `maxSize` is reached). */
|
|
54
56
|
evicted: number;
|
|
55
|
-
/**
|
|
57
|
+
/** Number of keys stored right now. */
|
|
56
58
|
size: number;
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
79
|
+
/** The active eviction policy: `"reject"` or `"lru"`. */
|
|
76
80
|
get evictionPolicy(): EvictionPolicy;
|
|
77
81
|
/**
|
|
78
|
-
*
|
|
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`
|
|
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
|
-
*
|
|
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
|
|
97
|
+
* @returns the stored value, or `null` if missing or expired.
|
|
87
98
|
*/
|
|
88
99
|
get<T = unknown>(key: string): T | null;
|
|
89
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
122
|
+
/** Empties the cache (does not reset the historical `stats` counters). */
|
|
94
123
|
clear(): void;
|
|
95
|
-
/**
|
|
124
|
+
/** Number of keys currently stored. */
|
|
96
125
|
size(): number;
|
|
97
126
|
/**
|
|
98
|
-
*
|
|
127
|
+
* Sweeps and removes every expired entry at once.
|
|
99
128
|
*
|
|
100
|
-
* @returns
|
|
129
|
+
* @returns how many entries were removed.
|
|
101
130
|
*/
|
|
102
131
|
cleanupExpired(): number;
|
|
103
|
-
/**
|
|
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
|
|
2
|
+
* rust-node-cache — public API (TypeScript).
|
|
3
3
|
*
|
|
4
|
-
* "
|
|
4
|
+
* "In-memory cache for Node.js that keeps your data off the V8 heap."
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
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
|
-
/**
|
|
13
|
+
/** Eviction strategy when `maxSize` is reached. */
|
|
16
14
|
type EvictionPolicy = "reject" | "lru";
|
|
17
|
-
/**
|
|
15
|
+
/** Cache constructor options. */
|
|
18
16
|
interface CacheOptions {
|
|
19
17
|
/**
|
|
20
|
-
*
|
|
21
|
-
* `evictionPolicy`.
|
|
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
|
-
*
|
|
26
|
-
* - `"reject"` (
|
|
27
|
-
* - `"lru"`:
|
|
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
|
-
*
|
|
32
|
-
*
|
|
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
|
-
/**
|
|
35
|
+
/** Per-write options. */
|
|
37
36
|
interface SetOptions {
|
|
38
|
-
/**
|
|
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
|
-
/**
|
|
43
|
+
/** Accumulated cache statistics (plus the current size). */
|
|
42
44
|
interface CacheStats {
|
|
43
|
-
/**
|
|
45
|
+
/** Reads that found a valid entry. */
|
|
44
46
|
hits: number;
|
|
45
|
-
/**
|
|
47
|
+
/** Reads without a valid key (missing or expired). */
|
|
46
48
|
misses: number;
|
|
47
|
-
/** Total
|
|
49
|
+
/** Total writes (`set`). */
|
|
48
50
|
sets: number;
|
|
49
|
-
/** Total
|
|
51
|
+
/** Total explicit removals (`delete`). */
|
|
50
52
|
deletes: number;
|
|
51
|
-
/** Total
|
|
53
|
+
/** Total entries removed by expiration. */
|
|
52
54
|
expired: number;
|
|
53
|
-
/** Total
|
|
55
|
+
/** Total entries removed by eviction (LRU policy when `maxSize` is reached). */
|
|
54
56
|
evicted: number;
|
|
55
|
-
/**
|
|
57
|
+
/** Number of keys stored right now. */
|
|
56
58
|
size: number;
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
79
|
+
/** The active eviction policy: `"reject"` or `"lru"`. */
|
|
76
80
|
get evictionPolicy(): EvictionPolicy;
|
|
77
81
|
/**
|
|
78
|
-
*
|
|
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`
|
|
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
|
-
*
|
|
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
|
|
97
|
+
* @returns the stored value, or `null` if missing or expired.
|
|
87
98
|
*/
|
|
88
99
|
get<T = unknown>(key: string): T | null;
|
|
89
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
122
|
+
/** Empties the cache (does not reset the historical `stats` counters). */
|
|
94
123
|
clear(): void;
|
|
95
|
-
/**
|
|
124
|
+
/** Number of keys currently stored. */
|
|
96
125
|
size(): number;
|
|
97
126
|
/**
|
|
98
|
-
*
|
|
127
|
+
* Sweeps and removes every expired entry at once.
|
|
99
128
|
*
|
|
100
|
-
* @returns
|
|
129
|
+
* @returns how many entries were removed.
|
|
101
130
|
*/
|
|
102
131
|
cleanupExpired(): number;
|
|
103
|
-
/**
|
|
132
|
+
/** Returns the accumulated statistics + the current size. */
|
|
104
133
|
stats(): CacheStats;
|
|
105
134
|
}
|
|
106
135
|
|