rust-node-cache 0.2.1 → 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 +126 -48
- package/binding.d.ts +17 -9
- package/dist/express.d.mts +4 -0
- package/dist/express.d.ts +4 -0
- 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.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 +46 -17
- package/dist/index.d.ts +46 -17
- package/dist/index.js +119 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +119 -6
- package/dist/index.mjs.map +1 -1
- 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 +4 -3
- 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/index.d.mts
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
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
|
-
* This layer is a
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
* The cost is one extra function call per operation — negligible next to the
|
|
13
|
-
* real work (serialization + DashMap access) done in 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
|
-
* Maximum number of keys. What happens when it is
|
|
21
|
-
* `evictionPolicy`. No value => unlimited cache.
|
|
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
|
/**
|
|
@@ -30,12 +28,16 @@ interface CacheOptions {
|
|
|
30
28
|
/**
|
|
31
29
|
* When set (> 0), starts a thread that sweeps expired entries every N
|
|
32
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). */
|
|
@@ -56,8 +58,9 @@ interface CacheStats {
|
|
|
56
58
|
size: number;
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
|
-
* In-memory cache
|
|
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
|
|
@@ -71,24 +74,50 @@ interface CacheStats {
|
|
|
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
|
-
* Stores (or overwrites) a value
|
|
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
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 the
|
|
97
|
+
* @returns the stored value, or `null` if missing or expired.
|
|
87
98
|
*/
|
|
88
99
|
get<T = unknown>(key: string): T | null;
|
|
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;
|
|
89
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;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
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
|
-
* This layer is a
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
* The cost is one extra function call per operation — negligible next to the
|
|
13
|
-
* real work (serialization + DashMap access) done in 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
|
-
* Maximum number of keys. What happens when it is
|
|
21
|
-
* `evictionPolicy`. No value => unlimited cache.
|
|
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
|
/**
|
|
@@ -30,12 +28,16 @@ interface CacheOptions {
|
|
|
30
28
|
/**
|
|
31
29
|
* When set (> 0), starts a thread that sweeps expired entries every N
|
|
32
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). */
|
|
@@ -56,8 +58,9 @@ interface CacheStats {
|
|
|
56
58
|
size: number;
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
|
-
* In-memory cache
|
|
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
|
|
@@ -71,24 +74,50 @@ interface CacheStats {
|
|
|
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
|
-
* Stores (or overwrites) a value
|
|
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
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 the
|
|
97
|
+
* @returns the stored value, or `null` if missing or expired.
|
|
87
98
|
*/
|
|
88
99
|
get<T = unknown>(key: string): T | null;
|
|
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;
|
|
89
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;
|
package/dist/index.js
CHANGED
|
@@ -34,7 +34,91 @@ __export(index_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
var native = __toESM(require("../binding.js"));
|
|
37
|
+
|
|
38
|
+
// js/encode.ts
|
|
39
|
+
var MAX_DEPTH = 1e3;
|
|
40
|
+
var HINTS = {
|
|
41
|
+
Date: " Store date.toISOString() or date.getTime() instead.",
|
|
42
|
+
Map: " Store Object.fromEntries(map) instead.",
|
|
43
|
+
Set: " Store [...set] instead.",
|
|
44
|
+
BigInt: " Store String(n) instead."
|
|
45
|
+
};
|
|
46
|
+
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
47
|
+
function encode(value) {
|
|
48
|
+
const problem2 = check(value, 0);
|
|
49
|
+
if (problem2 !== void 0) throw toError(problem2);
|
|
50
|
+
return JSON.stringify(value);
|
|
51
|
+
}
|
|
52
|
+
function check(value, depth) {
|
|
53
|
+
switch (typeof value) {
|
|
54
|
+
case "string":
|
|
55
|
+
case "boolean":
|
|
56
|
+
return void 0;
|
|
57
|
+
case "number":
|
|
58
|
+
return Number.isFinite(value) ? void 0 : problem(String(value));
|
|
59
|
+
case "object": {
|
|
60
|
+
if (value === null) return void 0;
|
|
61
|
+
if (depth >= MAX_DEPTH) return tooDeep(value);
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
for (let i = 0; i < value.length; i++) {
|
|
64
|
+
const item = value[i];
|
|
65
|
+
const p = item === void 0 ? problem("undefined") : check(item, depth + 1);
|
|
66
|
+
if (p !== void 0) {
|
|
67
|
+
p.path.unshift(i);
|
|
68
|
+
return p;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
const proto = Object.getPrototypeOf(value);
|
|
74
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
75
|
+
const name = value.constructor?.name;
|
|
76
|
+
return problem(
|
|
77
|
+
name ? `a ${name}` : "an object with a custom prototype",
|
|
78
|
+
name && HINTS[name] || ""
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
for (const key in value) {
|
|
82
|
+
const item = value[key];
|
|
83
|
+
if (item === void 0) continue;
|
|
84
|
+
const p = check(item, depth + 1);
|
|
85
|
+
if (p !== void 0) {
|
|
86
|
+
p.path.unshift(key);
|
|
87
|
+
return p;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
case "bigint":
|
|
93
|
+
return problem("a BigInt", HINTS.BigInt);
|
|
94
|
+
case "undefined":
|
|
95
|
+
return problem("undefined");
|
|
96
|
+
default:
|
|
97
|
+
return problem(`a ${typeof value}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function problem(what, hint = "") {
|
|
101
|
+
return { path: [], what, hint };
|
|
102
|
+
}
|
|
103
|
+
function toError({ path, what, hint }) {
|
|
104
|
+
let where = "value";
|
|
105
|
+
for (const part of path) {
|
|
106
|
+
if (typeof part === "number") where += `[${part}]`;
|
|
107
|
+
else if (IDENTIFIER.test(part)) where += `.${part}`;
|
|
108
|
+
else where += `[${JSON.stringify(part)}]`;
|
|
109
|
+
}
|
|
110
|
+
return new TypeError(
|
|
111
|
+
`rust-node-cache: cannot store ${where} (${what}). Values are kept as JSON, so only plain objects, arrays, strings, finite numbers, booleans and null are accepted.${hint}`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
function tooDeep(value) {
|
|
115
|
+
JSON.stringify(value);
|
|
116
|
+
throw new TypeError(`rust-node-cache: cannot store a value nested more than ${MAX_DEPTH} levels deep.`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// js/index.ts
|
|
37
120
|
var Cache2 = class {
|
|
121
|
+
/** @throws if `maxSize`, `evictionPolicy` or `cleanupIntervalSeconds` is invalid. */
|
|
38
122
|
constructor(options = {}) {
|
|
39
123
|
const hasOptions = options.maxSize !== void 0 || options.evictionPolicy !== void 0 || options.cleanupIntervalSeconds !== void 0;
|
|
40
124
|
this.native = hasOptions ? new native.Cache({
|
|
@@ -48,27 +132,56 @@ var Cache2 = class {
|
|
|
48
132
|
return this.native.evictionPolicy;
|
|
49
133
|
}
|
|
50
134
|
/**
|
|
51
|
-
* Stores (or overwrites) a value
|
|
135
|
+
* Stores (or overwrites) a value, kept as JSON. Accepts plain objects,
|
|
136
|
+
* arrays, strings, finite numbers, booleans and `null`; object properties
|
|
137
|
+
* set to `undefined` are left out, as in JSON.
|
|
52
138
|
*
|
|
53
139
|
* @returns `true` on success; `false` if `maxSize` was reached and the key is new.
|
|
140
|
+
* @throws TypeError naming the offending path if the value holds something
|
|
141
|
+
* JSON cannot represent (a `Date`, `Map`, `Set`, `BigInt`, class instance,
|
|
142
|
+
* function, `NaN`, `Infinity`, an `undefined` array item, or `undefined`
|
|
143
|
+
* itself) — instead of storing a silently different value.
|
|
54
144
|
*/
|
|
55
145
|
set(key, value, options) {
|
|
56
|
-
return this.native.set(key, value, options);
|
|
146
|
+
return this.native.set(key, encode(value), options);
|
|
57
147
|
}
|
|
58
148
|
/**
|
|
59
149
|
* Reads a value. Applies lazy expiration: expired keys count as a miss.
|
|
150
|
+
* Each call returns a new copy, so mutating it does not change the cache.
|
|
60
151
|
*
|
|
61
|
-
* @returns the
|
|
152
|
+
* @returns the stored value, or `null` if missing or expired.
|
|
62
153
|
*/
|
|
63
154
|
get(key) {
|
|
64
|
-
const
|
|
65
|
-
return
|
|
155
|
+
const json = this.native.get(key);
|
|
156
|
+
return json === null ? null : JSON.parse(json);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Stores text that is **already JSON**, as is — no validation, no
|
|
160
|
+
* re-encoding. Meant for payloads serialized elsewhere, such as an HTTP
|
|
161
|
+
* response body. Storing text that is not valid JSON makes `get()` throw.
|
|
162
|
+
*
|
|
163
|
+
* @returns `true` on success; `false` if `maxSize` was reached and the key is new.
|
|
164
|
+
*/
|
|
165
|
+
setJson(key, json, options) {
|
|
166
|
+
return this.native.set(key, json, options);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Reads a value as its JSON text, without parsing it — e.g. to send it as
|
|
170
|
+
* an HTTP response body directly. Same expiration and stats as `get()`.
|
|
171
|
+
*
|
|
172
|
+
* @returns the JSON text, or `null` if missing or expired.
|
|
173
|
+
*/
|
|
174
|
+
getJson(key) {
|
|
175
|
+
return this.native.get(key);
|
|
66
176
|
}
|
|
67
177
|
/** Removes a key. Returns `true` if it existed. */
|
|
68
178
|
delete(key) {
|
|
69
179
|
return this.native.delete(key);
|
|
70
180
|
}
|
|
71
|
-
/**
|
|
181
|
+
/**
|
|
182
|
+
* Whether the key exists and is still valid (not expired). Does not count
|
|
183
|
+
* as a use for LRU eviction.
|
|
184
|
+
*/
|
|
72
185
|
exists(key) {
|
|
73
186
|
return this.native.exists(key);
|
|
74
187
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../js/index.ts"],"sourcesContent":["/**\n * rust-node-cache — public API (TypeScript).\n *\n * \"Ultra-fast in-memory cache powered by Rust.\"\n *\n * This layer is a thin, typed wrapper over the native `Cache` class generated\n * by Rust/napi-rs. Why wrap it instead of re-exporting it directly?\n * - it allows ergonomic **generics**: `cache.get<User>(\"user:1\")`;\n * - it normalizes `undefined`/`null` so a miss always returns `null`;\n * - it gives one place for the JSDoc and for evolving the API without breaking the Rust core.\n *\n * The cost is one extra function call per operation — negligible next to the\n * real work (serialization + DashMap access) done in Rust.\n */\n\n// Namespace import: works the same in CommonJS and ESM when consuming the native\n// addon (a CJS module generated by napi-rs).\nimport * as native from \"../binding.js\";\n\n/** Eviction strategy when `maxSize` is reached. */\nexport type EvictionPolicy = \"reject\" | \"lru\";\n\n/** Cache constructor options. */\nexport interface CacheOptions {\n /**\n * Maximum number of keys. What happens when it is reached depends on\n * `evictionPolicy`. No value => unlimited cache.\n */\n maxSize?: number;\n /**\n * Policy when `maxSize` is reached on a **new** key:\n * - `\"reject\"` (default): `set` returns `false` (overwriting still works);\n * - `\"lru\"`: removes the least recently used entry and goes on.\n */\n evictionPolicy?: EvictionPolicy;\n /**\n * When set (> 0), starts a thread that sweeps expired entries every N\n * seconds (background expiration, on top of the lazy one on access).\n */\n cleanupIntervalSeconds?: number;\n}\n\n/** Per-write options. */\nexport interface SetOptions {\n /** Time to live in seconds. Absent => the entry does not expire by time. */\n ttlSeconds?: number;\n}\n\n/** Accumulated cache statistics (plus the current size). */\nexport interface CacheStats {\n /** Reads that found a valid entry. */\n hits: number;\n /** Reads without a valid key (missing or expired). */\n misses: number;\n /** Total writes (`set`). */\n sets: number;\n /** Total explicit removals (`delete`). */\n deletes: number;\n /** Total entries removed by expiration. */\n expired: number;\n /** Total entries removed by eviction (LRU policy when `maxSize` is reached). */\n evicted: number;\n /** Number of keys stored right now. */\n size: number;\n}\n\n/**\n * In-memory cache with a Rust core (DashMap + atomic counters). Thread-safe,\n * with an optional TTL per entry.\n *\n * @example\n * ```ts\n * import { Cache } from \"rust-node-cache\";\n *\n * const cache = new Cache();\n * cache.set(\"user:1\", { id: 1, name: \"Roberto\" });\n * cache.get<{ id: number; name: string }>(\"user:1\"); // { id: 1, name: \"Roberto\" }\n * ```\n */\nexport class Cache {\n /** Instance of the native (Rust) class. */\n private readonly native: native.Cache;\n\n constructor(options: CacheOptions = {}) {\n // An options object is passed to Rust only when it carries something.\n const hasOptions =\n options.maxSize !== undefined ||\n options.evictionPolicy !== undefined ||\n options.cleanupIntervalSeconds !== undefined;\n this.native = hasOptions\n ? new native.Cache({\n maxSize: options.maxSize,\n evictionPolicy: options.evictionPolicy,\n cleanupIntervalSeconds: options.cleanupIntervalSeconds,\n })\n : new native.Cache();\n }\n\n /** The active eviction policy: `\"reject\"` or `\"lru\"`. */\n get evictionPolicy(): EvictionPolicy {\n return this.native.evictionPolicy as EvictionPolicy;\n }\n\n /**\n * Stores (or overwrites) a value. The value is serialized as JSON in the core.\n *\n * @returns `true` on success; `false` if `maxSize` was reached and the key is new.\n */\n set<T>(key: string, value: T, options?: SetOptions): boolean {\n return this.native.set(key, value as unknown as object, options);\n }\n\n /**\n * Reads a value. Applies lazy expiration: expired keys count as a miss.\n *\n * @returns the deserialized value, or `null` if missing or expired.\n */\n get<T = unknown>(key: string): T | null {\n const value = this.native.get(key);\n return value === null || value === undefined ? null : (value as T);\n }\n\n /** Removes a key. Returns `true` if it existed. */\n delete(key: string): boolean {\n return this.native.delete(key);\n }\n\n /** Whether the key exists and is still valid (not expired). */\n exists(key: string): boolean {\n return this.native.exists(key);\n }\n\n /** Empties the cache (does not reset the historical `stats` counters). */\n clear(): void {\n this.native.clear();\n }\n\n /** Number of keys currently stored. */\n size(): number {\n return this.native.size();\n }\n\n /**\n * Sweeps and removes every expired entry at once.\n *\n * @returns how many entries were removed.\n */\n cleanupExpired(): number {\n return this.native.cleanupExpired();\n }\n\n /** Returns the accumulated statistics + the current size. */\n stats(): CacheStats {\n return this.native.stats();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA,eAAAA;AAAA;AAAA;AAiBA,aAAwB;AA8DjB,IAAMA,SAAN,MAAY;AAAA,EAIjB,YAAY,UAAwB,CAAC,GAAG;AAEtC,UAAM,aACJ,QAAQ,YAAY,UACpB,QAAQ,mBAAmB,UAC3B,QAAQ,2BAA2B;AACrC,SAAK,SAAS,aACV,IAAW,aAAM;AAAA,MACf,SAAS,QAAQ;AAAA,MACjB,gBAAgB,QAAQ;AAAA,MACxB,wBAAwB,QAAQ;AAAA,IAClC,CAAC,IACD,IAAW,aAAM;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,iBAAiC;AACnC,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAO,KAAa,OAAU,SAA+B;AAC3D,WAAO,KAAK,OAAO,IAAI,KAAK,OAA4B,OAAO;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAiB,KAAuB;AACtC,UAAM,QAAQ,KAAK,OAAO,IAAI,GAAG;AACjC,WAAO,UAAU,QAAQ,UAAU,SAAY,OAAQ;AAAA,EACzD;AAAA;AAAA,EAGA,OAAO,KAAsB;AAC3B,WAAO,KAAK,OAAO,OAAO,GAAG;AAAA,EAC/B;AAAA;AAAA,EAGA,OAAO,KAAsB;AAC3B,WAAO,KAAK,OAAO,OAAO,GAAG;AAAA,EAC/B;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,OAAO,MAAM;AAAA,EACpB;AAAA;AAAA,EAGA,OAAe;AACb,WAAO,KAAK,OAAO,KAAK;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAyB;AACvB,WAAO,KAAK,OAAO,eAAe;AAAA,EACpC;AAAA;AAAA,EAGA,QAAoB;AAClB,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AACF;","names":["Cache"]}
|
|
1
|
+
{"version":3,"sources":["../js/index.ts","../js/encode.ts"],"sourcesContent":["/**\n * rust-node-cache — public API (TypeScript).\n *\n * \"In-memory cache for Node.js that keeps your data off the V8 heap.\"\n *\n * This layer is a typed wrapper over the native `Cache` class generated by\n * Rust/napi-rs. It does the JSON encoding on the JavaScript side, where V8's\n * `JSON.stringify`/`JSON.parse` are fastest, and hands Rust plain strings:\n * - `set` validates the value (see `./encode`) and stores its JSON text;\n * - `get` parses the stored text back, so every read returns a fresh copy;\n * - it allows ergonomic **generics**: `cache.get<User>(\"user:1\")`.\n */\n\n// Namespace import: works the same in CommonJS and ESM when consuming the native\n// addon (a CJS module generated by napi-rs).\nimport * as native from \"../binding.js\";\nimport { encode } from \"./encode\";\n\n/** Eviction strategy when `maxSize` is reached. */\nexport type EvictionPolicy = \"reject\" | \"lru\";\n\n/** Cache constructor options. */\nexport interface CacheOptions {\n /**\n * Maximum number of keys (a positive integer). What happens when it is\n * reached depends on `evictionPolicy`. No value => unlimited cache.\n */\n maxSize?: number;\n /**\n * Policy when `maxSize` is reached on a **new** key:\n * - `\"reject\"` (default): `set` returns `false` (overwriting still works);\n * - `\"lru\"`: removes the least recently used entry and goes on.\n */\n evictionPolicy?: EvictionPolicy;\n /**\n * When set (> 0), starts a thread that sweeps expired entries every N\n * seconds (background expiration, on top of the lazy one on access).\n * Fractions are allowed; `0` or no value leaves it off.\n */\n cleanupIntervalSeconds?: number;\n}\n\n/** Per-write options. */\nexport interface SetOptions {\n /**\n * Time to live in seconds; fractions are allowed (`0.5` = 500 ms, minimum\n * 1 ms). Absent => the entry does not expire by time.\n */\n ttlSeconds?: number;\n}\n\n/** Accumulated cache statistics (plus the current size). */\nexport interface CacheStats {\n /** Reads that found a valid entry. */\n hits: number;\n /** Reads without a valid key (missing or expired). */\n misses: number;\n /** Total writes (`set`). */\n sets: number;\n /** Total explicit removals (`delete`). */\n deletes: number;\n /** Total entries removed by expiration. */\n expired: number;\n /** Total entries removed by eviction (LRU policy when `maxSize` is reached). */\n evicted: number;\n /** Number of keys stored right now. */\n size: number;\n}\n\n/**\n * In-memory cache whose entries live in Rust memory, outside the V8 heap.\n * Exact LRU eviction (O(1)), an optional TTL per entry and an optional\n * background sweeper.\n *\n * @example\n * ```ts\n * import { Cache } from \"rust-node-cache\";\n *\n * const cache = new Cache();\n * cache.set(\"user:1\", { id: 1, name: \"Roberto\" });\n * cache.get<{ id: number; name: string }>(\"user:1\"); // { id: 1, name: \"Roberto\" }\n * ```\n */\nexport class Cache {\n /** Instance of the native (Rust) class. */\n private readonly native: native.Cache;\n\n /** @throws if `maxSize`, `evictionPolicy` or `cleanupIntervalSeconds` is invalid. */\n constructor(options: CacheOptions = {}) {\n // An options object is passed to Rust only when it carries something.\n const hasOptions =\n options.maxSize !== undefined ||\n options.evictionPolicy !== undefined ||\n options.cleanupIntervalSeconds !== undefined;\n this.native = hasOptions\n ? new native.Cache({\n maxSize: options.maxSize,\n evictionPolicy: options.evictionPolicy,\n cleanupIntervalSeconds: options.cleanupIntervalSeconds,\n })\n : new native.Cache();\n }\n\n /** The active eviction policy: `\"reject\"` or `\"lru\"`. */\n get evictionPolicy(): EvictionPolicy {\n return this.native.evictionPolicy as EvictionPolicy;\n }\n\n /**\n * Stores (or overwrites) a value, kept as JSON. Accepts plain objects,\n * arrays, strings, finite numbers, booleans and `null`; object properties\n * set to `undefined` are left out, as in JSON.\n *\n * @returns `true` on success; `false` if `maxSize` was reached and the key is new.\n * @throws TypeError naming the offending path if the value holds something\n * JSON cannot represent (a `Date`, `Map`, `Set`, `BigInt`, class instance,\n * function, `NaN`, `Infinity`, an `undefined` array item, or `undefined`\n * itself) — instead of storing a silently different value.\n */\n set<T>(key: string, value: T, options?: SetOptions): boolean {\n return this.native.set(key, encode(value), options);\n }\n\n /**\n * Reads a value. Applies lazy expiration: expired keys count as a miss.\n * Each call returns a new copy, so mutating it does not change the cache.\n *\n * @returns the stored value, or `null` if missing or expired.\n */\n get<T = unknown>(key: string): T | null {\n const json = this.native.get(key);\n return json === null ? null : (JSON.parse(json) as T);\n }\n\n /**\n * Stores text that is **already JSON**, as is — no validation, no\n * re-encoding. Meant for payloads serialized elsewhere, such as an HTTP\n * response body. Storing text that is not valid JSON makes `get()` throw.\n *\n * @returns `true` on success; `false` if `maxSize` was reached and the key is new.\n */\n setJson(key: string, json: string, options?: SetOptions): boolean {\n return this.native.set(key, json, options);\n }\n\n /**\n * Reads a value as its JSON text, without parsing it — e.g. to send it as\n * an HTTP response body directly. Same expiration and stats as `get()`.\n *\n * @returns the JSON text, or `null` if missing or expired.\n */\n getJson(key: string): string | null {\n return this.native.get(key);\n }\n\n /** Removes a key. Returns `true` if it existed. */\n delete(key: string): boolean {\n return this.native.delete(key);\n }\n\n /**\n * Whether the key exists and is still valid (not expired). Does not count\n * as a use for LRU eviction.\n */\n exists(key: string): boolean {\n return this.native.exists(key);\n }\n\n /** Empties the cache (does not reset the historical `stats` counters). */\n clear(): void {\n this.native.clear();\n }\n\n /** Number of keys currently stored. */\n size(): number {\n return this.native.size();\n }\n\n /**\n * Sweeps and removes every expired entry at once.\n *\n * @returns how many entries were removed.\n */\n cleanupExpired(): number {\n return this.native.cleanupExpired();\n }\n\n /** Returns the accumulated statistics + the current size. */\n stats(): CacheStats {\n return this.native.stats();\n }\n}\n","/**\n * Value encoding for the cache: validate, then `JSON.stringify`.\n *\n * Values are kept as JSON text in Rust memory. JSON cannot represent a `Date`,\n * `Map`, `Set`, `BigInt`, class instance, `NaN`/`Infinity` or an `undefined`\n * array slot — `JSON.stringify` would silently turn them into `{}`, a string or\n * `null`, and `get()` would return something else than what was stored. So\n * those are rejected with a `TypeError` naming where they are, instead.\n */\n\n/** Nesting deeper than this is reported instead of overflowing the stack. */\nconst MAX_DEPTH = 1000;\n\nconst HINTS: Record<string, string> = {\n Date: \" Store date.toISOString() or date.getTime() instead.\",\n Map: \" Store Object.fromEntries(map) instead.\",\n Set: \" Store [...set] instead.\",\n BigInt: \" Store String(n) instead.\",\n};\n\nconst IDENTIFIER = /^[A-Za-z_$][\\w$]*$/;\n\n/** Validates `value` and returns its JSON text. Throws `TypeError` otherwise. */\nexport function encode(value: unknown): string {\n const problem = check(value, 0);\n if (problem !== undefined) throw toError(problem);\n return JSON.stringify(value);\n}\n\n/** Why a value was rejected. `path` is filled in while unwinding, root first. */\ninterface Problem {\n path: (string | number)[];\n what: string;\n hint: string;\n}\n\n/**\n * Returns `undefined` when `value` round-trips through JSON unchanged, or the\n * first problem found. The path is only built on failure, so the common case\n * stays a bare walk.\n */\nfunction check(value: unknown, depth: number): Problem | undefined {\n switch (typeof value) {\n case \"string\":\n case \"boolean\":\n return undefined;\n case \"number\":\n return Number.isFinite(value) ? undefined : problem(String(value));\n case \"object\": {\n if (value === null) return undefined;\n if (depth >= MAX_DEPTH) return tooDeep(value);\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n const item = value[i];\n const p = item === undefined ? problem(\"undefined\") : check(item, depth + 1);\n if (p !== undefined) {\n p.path.unshift(i);\n return p;\n }\n }\n return undefined;\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) {\n const name = (value as object).constructor?.name;\n return problem(\n name ? `a ${name}` : \"an object with a custom prototype\",\n (name && HINTS[name]) || \"\",\n );\n }\n for (const key in value) {\n const item = (value as Record<string, unknown>)[key];\n // JSON omits undefined properties, and reading one back yields undefined\n // anyway, so they are accepted.\n if (item === undefined) continue;\n const p = check(item, depth + 1);\n if (p !== undefined) {\n p.path.unshift(key);\n return p;\n }\n }\n return undefined;\n }\n case \"bigint\":\n return problem(\"a BigInt\", HINTS.BigInt);\n case \"undefined\":\n return problem(\"undefined\");\n default:\n // function, symbol\n return problem(`a ${typeof value}`);\n }\n}\n\nfunction problem(what: string, hint = \"\"): Problem {\n return { path: [], what, hint };\n}\n\nfunction toError({ path, what, hint }: Problem): TypeError {\n let where = \"value\";\n for (const part of path) {\n if (typeof part === \"number\") where += `[${part}]`;\n else if (IDENTIFIER.test(part)) where += `.${part}`;\n else where += `[${JSON.stringify(part)}]`;\n }\n return new TypeError(\n `rust-node-cache: cannot store ${where} (${what}). Values are kept as JSON, so only ` +\n `plain objects, arrays, strings, finite numbers, booleans and null are accepted.${hint}`,\n );\n}\n\nfunction tooDeep(value: unknown): never {\n // A cycle also ends up here; let JSON.stringify name it if that is the case.\n JSON.stringify(value);\n throw new TypeError(`rust-node-cache: cannot store a value nested more than ${MAX_DEPTH} levels deep.`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA,eAAAA;AAAA;AAAA;AAeA,aAAwB;;;ACJxB,IAAM,YAAY;AAElB,IAAM,QAAgC;AAAA,EACpC,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,QAAQ;AACV;AAEA,IAAM,aAAa;AAGZ,SAAS,OAAO,OAAwB;AAC7C,QAAMC,WAAU,MAAM,OAAO,CAAC;AAC9B,MAAIA,aAAY,OAAW,OAAM,QAAQA,QAAO;AAChD,SAAO,KAAK,UAAU,KAAK;AAC7B;AAcA,SAAS,MAAM,OAAgB,OAAoC;AACjE,UAAQ,OAAO,OAAO;AAAA,IACpB,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,OAAO,SAAS,KAAK,IAAI,SAAY,QAAQ,OAAO,KAAK,CAAC;AAAA,IACnE,KAAK,UAAU;AACb,UAAI,UAAU,KAAM,QAAO;AAC3B,UAAI,SAAS,UAAW,QAAO,QAAQ,KAAK;AAC5C,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAM,OAAO,MAAM,CAAC;AACpB,gBAAM,IAAI,SAAS,SAAY,QAAQ,WAAW,IAAI,MAAM,MAAM,QAAQ,CAAC;AAC3E,cAAI,MAAM,QAAW;AACnB,cAAE,KAAK,QAAQ,CAAC;AAChB,mBAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO;AAAA,MACT;AACA,YAAM,QAAQ,OAAO,eAAe,KAAK;AACzC,UAAI,UAAU,OAAO,aAAa,UAAU,MAAM;AAChD,cAAM,OAAQ,MAAiB,aAAa;AAC5C,eAAO;AAAA,UACL,OAAO,KAAK,IAAI,KAAK;AAAA,UACpB,QAAQ,MAAM,IAAI,KAAM;AAAA,QAC3B;AAAA,MACF;AACA,iBAAW,OAAO,OAAO;AACvB,cAAM,OAAQ,MAAkC,GAAG;AAGnD,YAAI,SAAS,OAAW;AACxB,cAAM,IAAI,MAAM,MAAM,QAAQ,CAAC;AAC/B,YAAI,MAAM,QAAW;AACnB,YAAE,KAAK,QAAQ,GAAG;AAClB,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IACA,KAAK;AACH,aAAO,QAAQ,YAAY,MAAM,MAAM;AAAA,IACzC,KAAK;AACH,aAAO,QAAQ,WAAW;AAAA,IAC5B;AAEE,aAAO,QAAQ,KAAK,OAAO,KAAK,EAAE;AAAA,EACtC;AACF;AAEA,SAAS,QAAQ,MAAc,OAAO,IAAa;AACjD,SAAO,EAAE,MAAM,CAAC,GAAG,MAAM,KAAK;AAChC;AAEA,SAAS,QAAQ,EAAE,MAAM,MAAM,KAAK,GAAuB;AACzD,MAAI,QAAQ;AACZ,aAAW,QAAQ,MAAM;AACvB,QAAI,OAAO,SAAS,SAAU,UAAS,IAAI,IAAI;AAAA,aACtC,WAAW,KAAK,IAAI,EAAG,UAAS,IAAI,IAAI;AAAA,QAC5C,UAAS,IAAI,KAAK,UAAU,IAAI,CAAC;AAAA,EACxC;AACA,SAAO,IAAI;AAAA,IACT,iCAAiC,KAAK,KAAK,IAAI,sHACqC,IAAI;AAAA,EAC1F;AACF;AAEA,SAAS,QAAQ,OAAuB;AAEtC,OAAK,UAAU,KAAK;AACpB,QAAM,IAAI,UAAU,0DAA0D,SAAS,eAAe;AACxG;;;AD/BO,IAAMC,SAAN,MAAY;AAAA;AAAA,EAKjB,YAAY,UAAwB,CAAC,GAAG;AAEtC,UAAM,aACJ,QAAQ,YAAY,UACpB,QAAQ,mBAAmB,UAC3B,QAAQ,2BAA2B;AACrC,SAAK,SAAS,aACV,IAAW,aAAM;AAAA,MACf,SAAS,QAAQ;AAAA,MACjB,gBAAgB,QAAQ;AAAA,MACxB,wBAAwB,QAAQ;AAAA,IAClC,CAAC,IACD,IAAW,aAAM;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,iBAAiC;AACnC,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,IAAO,KAAa,OAAU,SAA+B;AAC3D,WAAO,KAAK,OAAO,IAAI,KAAK,OAAO,KAAK,GAAG,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAiB,KAAuB;AACtC,UAAM,OAAO,KAAK,OAAO,IAAI,GAAG;AAChC,WAAO,SAAS,OAAO,OAAQ,KAAK,MAAM,IAAI;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,QAAQ,KAAa,MAAc,SAA+B;AAChE,WAAO,KAAK,OAAO,IAAI,KAAK,MAAM,OAAO;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QAAQ,KAA4B;AAClC,WAAO,KAAK,OAAO,IAAI,GAAG;AAAA,EAC5B;AAAA;AAAA,EAGA,OAAO,KAAsB;AAC3B,WAAO,KAAK,OAAO,OAAO,GAAG;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,KAAsB;AAC3B,WAAO,KAAK,OAAO,OAAO,GAAG;AAAA,EAC/B;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,OAAO,MAAM;AAAA,EACpB;AAAA;AAAA,EAGA,OAAe;AACb,WAAO,KAAK,OAAO,KAAK;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAyB;AACvB,WAAO,KAAK,OAAO,eAAe;AAAA,EACpC;AAAA;AAAA,EAGA,QAAoB;AAClB,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AACF;","names":["Cache","problem","Cache"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,90 @@
|
|
|
1
1
|
// js/index.ts
|
|
2
2
|
import * as native from "../binding.js";
|
|
3
|
+
|
|
4
|
+
// js/encode.ts
|
|
5
|
+
var MAX_DEPTH = 1e3;
|
|
6
|
+
var HINTS = {
|
|
7
|
+
Date: " Store date.toISOString() or date.getTime() instead.",
|
|
8
|
+
Map: " Store Object.fromEntries(map) instead.",
|
|
9
|
+
Set: " Store [...set] instead.",
|
|
10
|
+
BigInt: " Store String(n) instead."
|
|
11
|
+
};
|
|
12
|
+
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
13
|
+
function encode(value) {
|
|
14
|
+
const problem2 = check(value, 0);
|
|
15
|
+
if (problem2 !== void 0) throw toError(problem2);
|
|
16
|
+
return JSON.stringify(value);
|
|
17
|
+
}
|
|
18
|
+
function check(value, depth) {
|
|
19
|
+
switch (typeof value) {
|
|
20
|
+
case "string":
|
|
21
|
+
case "boolean":
|
|
22
|
+
return void 0;
|
|
23
|
+
case "number":
|
|
24
|
+
return Number.isFinite(value) ? void 0 : problem(String(value));
|
|
25
|
+
case "object": {
|
|
26
|
+
if (value === null) return void 0;
|
|
27
|
+
if (depth >= MAX_DEPTH) return tooDeep(value);
|
|
28
|
+
if (Array.isArray(value)) {
|
|
29
|
+
for (let i = 0; i < value.length; i++) {
|
|
30
|
+
const item = value[i];
|
|
31
|
+
const p = item === void 0 ? problem("undefined") : check(item, depth + 1);
|
|
32
|
+
if (p !== void 0) {
|
|
33
|
+
p.path.unshift(i);
|
|
34
|
+
return p;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
const proto = Object.getPrototypeOf(value);
|
|
40
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
41
|
+
const name = value.constructor?.name;
|
|
42
|
+
return problem(
|
|
43
|
+
name ? `a ${name}` : "an object with a custom prototype",
|
|
44
|
+
name && HINTS[name] || ""
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
for (const key in value) {
|
|
48
|
+
const item = value[key];
|
|
49
|
+
if (item === void 0) continue;
|
|
50
|
+
const p = check(item, depth + 1);
|
|
51
|
+
if (p !== void 0) {
|
|
52
|
+
p.path.unshift(key);
|
|
53
|
+
return p;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
case "bigint":
|
|
59
|
+
return problem("a BigInt", HINTS.BigInt);
|
|
60
|
+
case "undefined":
|
|
61
|
+
return problem("undefined");
|
|
62
|
+
default:
|
|
63
|
+
return problem(`a ${typeof value}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function problem(what, hint = "") {
|
|
67
|
+
return { path: [], what, hint };
|
|
68
|
+
}
|
|
69
|
+
function toError({ path, what, hint }) {
|
|
70
|
+
let where = "value";
|
|
71
|
+
for (const part of path) {
|
|
72
|
+
if (typeof part === "number") where += `[${part}]`;
|
|
73
|
+
else if (IDENTIFIER.test(part)) where += `.${part}`;
|
|
74
|
+
else where += `[${JSON.stringify(part)}]`;
|
|
75
|
+
}
|
|
76
|
+
return new TypeError(
|
|
77
|
+
`rust-node-cache: cannot store ${where} (${what}). Values are kept as JSON, so only plain objects, arrays, strings, finite numbers, booleans and null are accepted.${hint}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
function tooDeep(value) {
|
|
81
|
+
JSON.stringify(value);
|
|
82
|
+
throw new TypeError(`rust-node-cache: cannot store a value nested more than ${MAX_DEPTH} levels deep.`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// js/index.ts
|
|
3
86
|
var Cache2 = class {
|
|
87
|
+
/** @throws if `maxSize`, `evictionPolicy` or `cleanupIntervalSeconds` is invalid. */
|
|
4
88
|
constructor(options = {}) {
|
|
5
89
|
const hasOptions = options.maxSize !== void 0 || options.evictionPolicy !== void 0 || options.cleanupIntervalSeconds !== void 0;
|
|
6
90
|
this.native = hasOptions ? new native.Cache({
|
|
@@ -14,27 +98,56 @@ var Cache2 = class {
|
|
|
14
98
|
return this.native.evictionPolicy;
|
|
15
99
|
}
|
|
16
100
|
/**
|
|
17
|
-
* Stores (or overwrites) a value
|
|
101
|
+
* Stores (or overwrites) a value, kept as JSON. Accepts plain objects,
|
|
102
|
+
* arrays, strings, finite numbers, booleans and `null`; object properties
|
|
103
|
+
* set to `undefined` are left out, as in JSON.
|
|
18
104
|
*
|
|
19
105
|
* @returns `true` on success; `false` if `maxSize` was reached and the key is new.
|
|
106
|
+
* @throws TypeError naming the offending path if the value holds something
|
|
107
|
+
* JSON cannot represent (a `Date`, `Map`, `Set`, `BigInt`, class instance,
|
|
108
|
+
* function, `NaN`, `Infinity`, an `undefined` array item, or `undefined`
|
|
109
|
+
* itself) — instead of storing a silently different value.
|
|
20
110
|
*/
|
|
21
111
|
set(key, value, options) {
|
|
22
|
-
return this.native.set(key, value, options);
|
|
112
|
+
return this.native.set(key, encode(value), options);
|
|
23
113
|
}
|
|
24
114
|
/**
|
|
25
115
|
* Reads a value. Applies lazy expiration: expired keys count as a miss.
|
|
116
|
+
* Each call returns a new copy, so mutating it does not change the cache.
|
|
26
117
|
*
|
|
27
|
-
* @returns the
|
|
118
|
+
* @returns the stored value, or `null` if missing or expired.
|
|
28
119
|
*/
|
|
29
120
|
get(key) {
|
|
30
|
-
const
|
|
31
|
-
return
|
|
121
|
+
const json = this.native.get(key);
|
|
122
|
+
return json === null ? null : JSON.parse(json);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Stores text that is **already JSON**, as is — no validation, no
|
|
126
|
+
* re-encoding. Meant for payloads serialized elsewhere, such as an HTTP
|
|
127
|
+
* response body. Storing text that is not valid JSON makes `get()` throw.
|
|
128
|
+
*
|
|
129
|
+
* @returns `true` on success; `false` if `maxSize` was reached and the key is new.
|
|
130
|
+
*/
|
|
131
|
+
setJson(key, json, options) {
|
|
132
|
+
return this.native.set(key, json, options);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Reads a value as its JSON text, without parsing it — e.g. to send it as
|
|
136
|
+
* an HTTP response body directly. Same expiration and stats as `get()`.
|
|
137
|
+
*
|
|
138
|
+
* @returns the JSON text, or `null` if missing or expired.
|
|
139
|
+
*/
|
|
140
|
+
getJson(key) {
|
|
141
|
+
return this.native.get(key);
|
|
32
142
|
}
|
|
33
143
|
/** Removes a key. Returns `true` if it existed. */
|
|
34
144
|
delete(key) {
|
|
35
145
|
return this.native.delete(key);
|
|
36
146
|
}
|
|
37
|
-
/**
|
|
147
|
+
/**
|
|
148
|
+
* Whether the key exists and is still valid (not expired). Does not count
|
|
149
|
+
* as a use for LRU eviction.
|
|
150
|
+
*/
|
|
38
151
|
exists(key) {
|
|
39
152
|
return this.native.exists(key);
|
|
40
153
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../js/index.ts"],"sourcesContent":["/**\n * rust-node-cache — public API (TypeScript).\n *\n * \"Ultra-fast in-memory cache powered by Rust.\"\n *\n * This layer is a thin, typed wrapper over the native `Cache` class generated\n * by Rust/napi-rs. Why wrap it instead of re-exporting it directly?\n * - it allows ergonomic **generics**: `cache.get<User>(\"user:1\")`;\n * - it normalizes `undefined`/`null` so a miss always returns `null`;\n * - it gives one place for the JSDoc and for evolving the API without breaking the Rust core.\n *\n * The cost is one extra function call per operation — negligible next to the\n * real work (serialization + DashMap access) done in Rust.\n */\n\n// Namespace import: works the same in CommonJS and ESM when consuming the native\n// addon (a CJS module generated by napi-rs).\nimport * as native from \"../binding.js\";\n\n/** Eviction strategy when `maxSize` is reached. */\nexport type EvictionPolicy = \"reject\" | \"lru\";\n\n/** Cache constructor options. */\nexport interface CacheOptions {\n /**\n * Maximum number of keys. What happens when it is reached depends on\n * `evictionPolicy`. No value => unlimited cache.\n */\n maxSize?: number;\n /**\n * Policy when `maxSize` is reached on a **new** key:\n * - `\"reject\"` (default): `set` returns `false` (overwriting still works);\n * - `\"lru\"`: removes the least recently used entry and goes on.\n */\n evictionPolicy?: EvictionPolicy;\n /**\n * When set (> 0), starts a thread that sweeps expired entries every N\n * seconds (background expiration, on top of the lazy one on access).\n */\n cleanupIntervalSeconds?: number;\n}\n\n/** Per-write options. */\nexport interface SetOptions {\n /** Time to live in seconds. Absent => the entry does not expire by time. */\n ttlSeconds?: number;\n}\n\n/** Accumulated cache statistics (plus the current size). */\nexport interface CacheStats {\n /** Reads that found a valid entry. */\n hits: number;\n /** Reads without a valid key (missing or expired). */\n misses: number;\n /** Total writes (`set`). */\n sets: number;\n /** Total explicit removals (`delete`). */\n deletes: number;\n /** Total entries removed by expiration. */\n expired: number;\n /** Total entries removed by eviction (LRU policy when `maxSize` is reached). */\n evicted: number;\n /** Number of keys stored right now. */\n size: number;\n}\n\n/**\n * In-memory cache with a Rust core (DashMap + atomic counters). Thread-safe,\n * with an optional TTL per entry.\n *\n * @example\n * ```ts\n * import { Cache } from \"rust-node-cache\";\n *\n * const cache = new Cache();\n * cache.set(\"user:1\", { id: 1, name: \"Roberto\" });\n * cache.get<{ id: number; name: string }>(\"user:1\"); // { id: 1, name: \"Roberto\" }\n * ```\n */\nexport class Cache {\n /** Instance of the native (Rust) class. */\n private readonly native: native.Cache;\n\n constructor(options: CacheOptions = {}) {\n // An options object is passed to Rust only when it carries something.\n const hasOptions =\n options.maxSize !== undefined ||\n options.evictionPolicy !== undefined ||\n options.cleanupIntervalSeconds !== undefined;\n this.native = hasOptions\n ? new native.Cache({\n maxSize: options.maxSize,\n evictionPolicy: options.evictionPolicy,\n cleanupIntervalSeconds: options.cleanupIntervalSeconds,\n })\n : new native.Cache();\n }\n\n /** The active eviction policy: `\"reject\"` or `\"lru\"`. */\n get evictionPolicy(): EvictionPolicy {\n return this.native.evictionPolicy as EvictionPolicy;\n }\n\n /**\n * Stores (or overwrites) a value. The value is serialized as JSON in the core.\n *\n * @returns `true` on success; `false` if `maxSize` was reached and the key is new.\n */\n set<T>(key: string, value: T, options?: SetOptions): boolean {\n return this.native.set(key, value as unknown as object, options);\n }\n\n /**\n * Reads a value. Applies lazy expiration: expired keys count as a miss.\n *\n * @returns the deserialized value, or `null` if missing or expired.\n */\n get<T = unknown>(key: string): T | null {\n const value = this.native.get(key);\n return value === null || value === undefined ? null : (value as T);\n }\n\n /** Removes a key. Returns `true` if it existed. */\n delete(key: string): boolean {\n return this.native.delete(key);\n }\n\n /** Whether the key exists and is still valid (not expired). */\n exists(key: string): boolean {\n return this.native.exists(key);\n }\n\n /** Empties the cache (does not reset the historical `stats` counters). */\n clear(): void {\n this.native.clear();\n }\n\n /** Number of keys currently stored. */\n size(): number {\n return this.native.size();\n }\n\n /**\n * Sweeps and removes every expired entry at once.\n *\n * @returns how many entries were removed.\n */\n cleanupExpired(): number {\n return this.native.cleanupExpired();\n }\n\n /** Returns the accumulated statistics + the current size. */\n stats(): CacheStats {\n return this.native.stats();\n }\n}\n"],"mappings":";AAiBA,YAAY,YAAY;AA8DjB,IAAMA,SAAN,MAAY;AAAA,EAIjB,YAAY,UAAwB,CAAC,GAAG;AAEtC,UAAM,aACJ,QAAQ,YAAY,UACpB,QAAQ,mBAAmB,UAC3B,QAAQ,2BAA2B;AACrC,SAAK,SAAS,aACV,IAAW,aAAM;AAAA,MACf,SAAS,QAAQ;AAAA,MACjB,gBAAgB,QAAQ;AAAA,MACxB,wBAAwB,QAAQ;AAAA,IAClC,CAAC,IACD,IAAW,aAAM;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,iBAAiC;AACnC,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAO,KAAa,OAAU,SAA+B;AAC3D,WAAO,KAAK,OAAO,IAAI,KAAK,OAA4B,OAAO;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAiB,KAAuB;AACtC,UAAM,QAAQ,KAAK,OAAO,IAAI,GAAG;AACjC,WAAO,UAAU,QAAQ,UAAU,SAAY,OAAQ;AAAA,EACzD;AAAA;AAAA,EAGA,OAAO,KAAsB;AAC3B,WAAO,KAAK,OAAO,OAAO,GAAG;AAAA,EAC/B;AAAA;AAAA,EAGA,OAAO,KAAsB;AAC3B,WAAO,KAAK,OAAO,OAAO,GAAG;AAAA,EAC/B;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,OAAO,MAAM;AAAA,EACpB;AAAA;AAAA,EAGA,OAAe;AACb,WAAO,KAAK,OAAO,KAAK;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAyB;AACvB,WAAO,KAAK,OAAO,eAAe;AAAA,EACpC;AAAA;AAAA,EAGA,QAAoB;AAClB,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AACF;","names":["Cache"]}
|
|
1
|
+
{"version":3,"sources":["../js/index.ts","../js/encode.ts"],"sourcesContent":["/**\n * rust-node-cache — public API (TypeScript).\n *\n * \"In-memory cache for Node.js that keeps your data off the V8 heap.\"\n *\n * This layer is a typed wrapper over the native `Cache` class generated by\n * Rust/napi-rs. It does the JSON encoding on the JavaScript side, where V8's\n * `JSON.stringify`/`JSON.parse` are fastest, and hands Rust plain strings:\n * - `set` validates the value (see `./encode`) and stores its JSON text;\n * - `get` parses the stored text back, so every read returns a fresh copy;\n * - it allows ergonomic **generics**: `cache.get<User>(\"user:1\")`.\n */\n\n// Namespace import: works the same in CommonJS and ESM when consuming the native\n// addon (a CJS module generated by napi-rs).\nimport * as native from \"../binding.js\";\nimport { encode } from \"./encode\";\n\n/** Eviction strategy when `maxSize` is reached. */\nexport type EvictionPolicy = \"reject\" | \"lru\";\n\n/** Cache constructor options. */\nexport interface CacheOptions {\n /**\n * Maximum number of keys (a positive integer). What happens when it is\n * reached depends on `evictionPolicy`. No value => unlimited cache.\n */\n maxSize?: number;\n /**\n * Policy when `maxSize` is reached on a **new** key:\n * - `\"reject\"` (default): `set` returns `false` (overwriting still works);\n * - `\"lru\"`: removes the least recently used entry and goes on.\n */\n evictionPolicy?: EvictionPolicy;\n /**\n * When set (> 0), starts a thread that sweeps expired entries every N\n * seconds (background expiration, on top of the lazy one on access).\n * Fractions are allowed; `0` or no value leaves it off.\n */\n cleanupIntervalSeconds?: number;\n}\n\n/** Per-write options. */\nexport interface SetOptions {\n /**\n * Time to live in seconds; fractions are allowed (`0.5` = 500 ms, minimum\n * 1 ms). Absent => the entry does not expire by time.\n */\n ttlSeconds?: number;\n}\n\n/** Accumulated cache statistics (plus the current size). */\nexport interface CacheStats {\n /** Reads that found a valid entry. */\n hits: number;\n /** Reads without a valid key (missing or expired). */\n misses: number;\n /** Total writes (`set`). */\n sets: number;\n /** Total explicit removals (`delete`). */\n deletes: number;\n /** Total entries removed by expiration. */\n expired: number;\n /** Total entries removed by eviction (LRU policy when `maxSize` is reached). */\n evicted: number;\n /** Number of keys stored right now. */\n size: number;\n}\n\n/**\n * In-memory cache whose entries live in Rust memory, outside the V8 heap.\n * Exact LRU eviction (O(1)), an optional TTL per entry and an optional\n * background sweeper.\n *\n * @example\n * ```ts\n * import { Cache } from \"rust-node-cache\";\n *\n * const cache = new Cache();\n * cache.set(\"user:1\", { id: 1, name: \"Roberto\" });\n * cache.get<{ id: number; name: string }>(\"user:1\"); // { id: 1, name: \"Roberto\" }\n * ```\n */\nexport class Cache {\n /** Instance of the native (Rust) class. */\n private readonly native: native.Cache;\n\n /** @throws if `maxSize`, `evictionPolicy` or `cleanupIntervalSeconds` is invalid. */\n constructor(options: CacheOptions = {}) {\n // An options object is passed to Rust only when it carries something.\n const hasOptions =\n options.maxSize !== undefined ||\n options.evictionPolicy !== undefined ||\n options.cleanupIntervalSeconds !== undefined;\n this.native = hasOptions\n ? new native.Cache({\n maxSize: options.maxSize,\n evictionPolicy: options.evictionPolicy,\n cleanupIntervalSeconds: options.cleanupIntervalSeconds,\n })\n : new native.Cache();\n }\n\n /** The active eviction policy: `\"reject\"` or `\"lru\"`. */\n get evictionPolicy(): EvictionPolicy {\n return this.native.evictionPolicy as EvictionPolicy;\n }\n\n /**\n * Stores (or overwrites) a value, kept as JSON. Accepts plain objects,\n * arrays, strings, finite numbers, booleans and `null`; object properties\n * set to `undefined` are left out, as in JSON.\n *\n * @returns `true` on success; `false` if `maxSize` was reached and the key is new.\n * @throws TypeError naming the offending path if the value holds something\n * JSON cannot represent (a `Date`, `Map`, `Set`, `BigInt`, class instance,\n * function, `NaN`, `Infinity`, an `undefined` array item, or `undefined`\n * itself) — instead of storing a silently different value.\n */\n set<T>(key: string, value: T, options?: SetOptions): boolean {\n return this.native.set(key, encode(value), options);\n }\n\n /**\n * Reads a value. Applies lazy expiration: expired keys count as a miss.\n * Each call returns a new copy, so mutating it does not change the cache.\n *\n * @returns the stored value, or `null` if missing or expired.\n */\n get<T = unknown>(key: string): T | null {\n const json = this.native.get(key);\n return json === null ? null : (JSON.parse(json) as T);\n }\n\n /**\n * Stores text that is **already JSON**, as is — no validation, no\n * re-encoding. Meant for payloads serialized elsewhere, such as an HTTP\n * response body. Storing text that is not valid JSON makes `get()` throw.\n *\n * @returns `true` on success; `false` if `maxSize` was reached and the key is new.\n */\n setJson(key: string, json: string, options?: SetOptions): boolean {\n return this.native.set(key, json, options);\n }\n\n /**\n * Reads a value as its JSON text, without parsing it — e.g. to send it as\n * an HTTP response body directly. Same expiration and stats as `get()`.\n *\n * @returns the JSON text, or `null` if missing or expired.\n */\n getJson(key: string): string | null {\n return this.native.get(key);\n }\n\n /** Removes a key. Returns `true` if it existed. */\n delete(key: string): boolean {\n return this.native.delete(key);\n }\n\n /**\n * Whether the key exists and is still valid (not expired). Does not count\n * as a use for LRU eviction.\n */\n exists(key: string): boolean {\n return this.native.exists(key);\n }\n\n /** Empties the cache (does not reset the historical `stats` counters). */\n clear(): void {\n this.native.clear();\n }\n\n /** Number of keys currently stored. */\n size(): number {\n return this.native.size();\n }\n\n /**\n * Sweeps and removes every expired entry at once.\n *\n * @returns how many entries were removed.\n */\n cleanupExpired(): number {\n return this.native.cleanupExpired();\n }\n\n /** Returns the accumulated statistics + the current size. */\n stats(): CacheStats {\n return this.native.stats();\n }\n}\n","/**\n * Value encoding for the cache: validate, then `JSON.stringify`.\n *\n * Values are kept as JSON text in Rust memory. JSON cannot represent a `Date`,\n * `Map`, `Set`, `BigInt`, class instance, `NaN`/`Infinity` or an `undefined`\n * array slot — `JSON.stringify` would silently turn them into `{}`, a string or\n * `null`, and `get()` would return something else than what was stored. So\n * those are rejected with a `TypeError` naming where they are, instead.\n */\n\n/** Nesting deeper than this is reported instead of overflowing the stack. */\nconst MAX_DEPTH = 1000;\n\nconst HINTS: Record<string, string> = {\n Date: \" Store date.toISOString() or date.getTime() instead.\",\n Map: \" Store Object.fromEntries(map) instead.\",\n Set: \" Store [...set] instead.\",\n BigInt: \" Store String(n) instead.\",\n};\n\nconst IDENTIFIER = /^[A-Za-z_$][\\w$]*$/;\n\n/** Validates `value` and returns its JSON text. Throws `TypeError` otherwise. */\nexport function encode(value: unknown): string {\n const problem = check(value, 0);\n if (problem !== undefined) throw toError(problem);\n return JSON.stringify(value);\n}\n\n/** Why a value was rejected. `path` is filled in while unwinding, root first. */\ninterface Problem {\n path: (string | number)[];\n what: string;\n hint: string;\n}\n\n/**\n * Returns `undefined` when `value` round-trips through JSON unchanged, or the\n * first problem found. The path is only built on failure, so the common case\n * stays a bare walk.\n */\nfunction check(value: unknown, depth: number): Problem | undefined {\n switch (typeof value) {\n case \"string\":\n case \"boolean\":\n return undefined;\n case \"number\":\n return Number.isFinite(value) ? undefined : problem(String(value));\n case \"object\": {\n if (value === null) return undefined;\n if (depth >= MAX_DEPTH) return tooDeep(value);\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n const item = value[i];\n const p = item === undefined ? problem(\"undefined\") : check(item, depth + 1);\n if (p !== undefined) {\n p.path.unshift(i);\n return p;\n }\n }\n return undefined;\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) {\n const name = (value as object).constructor?.name;\n return problem(\n name ? `a ${name}` : \"an object with a custom prototype\",\n (name && HINTS[name]) || \"\",\n );\n }\n for (const key in value) {\n const item = (value as Record<string, unknown>)[key];\n // JSON omits undefined properties, and reading one back yields undefined\n // anyway, so they are accepted.\n if (item === undefined) continue;\n const p = check(item, depth + 1);\n if (p !== undefined) {\n p.path.unshift(key);\n return p;\n }\n }\n return undefined;\n }\n case \"bigint\":\n return problem(\"a BigInt\", HINTS.BigInt);\n case \"undefined\":\n return problem(\"undefined\");\n default:\n // function, symbol\n return problem(`a ${typeof value}`);\n }\n}\n\nfunction problem(what: string, hint = \"\"): Problem {\n return { path: [], what, hint };\n}\n\nfunction toError({ path, what, hint }: Problem): TypeError {\n let where = \"value\";\n for (const part of path) {\n if (typeof part === \"number\") where += `[${part}]`;\n else if (IDENTIFIER.test(part)) where += `.${part}`;\n else where += `[${JSON.stringify(part)}]`;\n }\n return new TypeError(\n `rust-node-cache: cannot store ${where} (${what}). Values are kept as JSON, so only ` +\n `plain objects, arrays, strings, finite numbers, booleans and null are accepted.${hint}`,\n );\n}\n\nfunction tooDeep(value: unknown): never {\n // A cycle also ends up here; let JSON.stringify name it if that is the case.\n JSON.stringify(value);\n throw new TypeError(`rust-node-cache: cannot store a value nested more than ${MAX_DEPTH} levels deep.`);\n}\n"],"mappings":"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","names":["problem","Cache"]}
|