@nexusts/cache 0.7.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 +41 -0
- package/dist/index.js +576 -0
- package/dist/index.js.map +15 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @nexusts/cache
|
|
2
|
+
|
|
3
|
+
> **NexusTS** — Bun-native fullstack framework
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Application cache (memory / Drizzle backends).
|
|
8
|
+
|
|
9
|
+
Tagged caching with LRU eviction (memory) or shared Drizzle storage. Group keys by tag and invalidate them together.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
This module is part of the NexusTS monorepo. Each module is published as its own npm package under the `@nexusts/` scope.
|
|
14
|
+
|
|
15
|
+
Most apps start with just the core:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun add @nexusts/core reflect-metadata zod hono
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then add this module only if you need it:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bun add @nexusts/cache
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Peer dependencies
|
|
28
|
+
|
|
29
|
+
None. This module is fully self-contained.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { /* public API */ } from "@nexusts/cache";
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
See the [user guide](../../docs/user-guide/cache.md) and the [example app](../../examples/) for a working demo.
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT — see the root [LICENSE](../../LICENSE).
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __legacyDecorateClassTS = function(decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
5
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
6
|
+
else
|
|
7
|
+
for (var i = decorators.length - 1;i >= 0; i--)
|
|
8
|
+
if (d = decorators[i])
|
|
9
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
10
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
11
|
+
};
|
|
12
|
+
var __legacyDecorateParamTS = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
13
|
+
var __legacyMetadataTS = (k, v) => {
|
|
14
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
|
|
15
|
+
return Reflect.metadata(k, v);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// packages/cache/src/types.ts
|
|
19
|
+
import"reflect-metadata";
|
|
20
|
+
import { METADATA_KEY } from "@nexusts/core/constants.js";
|
|
21
|
+
var CACHEABLE_META = "nexus:cache:cacheable";
|
|
22
|
+
var CACHE_INVALIDATE_META = "nexus:cache:invalidate";
|
|
23
|
+
function Cacheable(prefix, keyFn, ttlSeconds = 60) {
|
|
24
|
+
return (target, propertyKey, descriptor) => {
|
|
25
|
+
const existing = Reflect.getMetadata(CACHEABLE_META, target.constructor) ?? [];
|
|
26
|
+
existing.push({
|
|
27
|
+
prefix,
|
|
28
|
+
keyFn,
|
|
29
|
+
ttl: ttlSeconds,
|
|
30
|
+
propertyKey,
|
|
31
|
+
original: descriptor.value
|
|
32
|
+
});
|
|
33
|
+
Reflect.defineMetadata(CACHEABLE_META, existing, target.constructor);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function CacheInvalidate(prefix, keyFn) {
|
|
37
|
+
return (target, propertyKey, descriptor) => {
|
|
38
|
+
const existing = Reflect.getMetadata(CACHE_INVALIDATE_META, target.constructor) ?? [];
|
|
39
|
+
existing.push({ prefix, keyFn, propertyKey, original: descriptor.value });
|
|
40
|
+
Reflect.defineMetadata(CACHE_INVALIDATE_META, existing, target.constructor);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function getCacheableSpecs(target) {
|
|
44
|
+
return Reflect.getMetadata(CACHEABLE_META, target) ?? [];
|
|
45
|
+
}
|
|
46
|
+
function getCacheInvalidateSpecs(target) {
|
|
47
|
+
return Reflect.getMetadata(CACHE_INVALIDATE_META, target) ?? [];
|
|
48
|
+
}
|
|
49
|
+
// packages/cache/src/stores/memory.ts
|
|
50
|
+
class MemoryStore {
|
|
51
|
+
kind = "memory";
|
|
52
|
+
data = new Map;
|
|
53
|
+
tagIndex = new Map;
|
|
54
|
+
max;
|
|
55
|
+
sweepTimer = null;
|
|
56
|
+
constructor(opts = {}) {
|
|
57
|
+
this.max = opts.max ?? 1e4;
|
|
58
|
+
const sweepMs = opts.sweepIntervalMs ?? 30000;
|
|
59
|
+
if (sweepMs > 0) {
|
|
60
|
+
this.sweepTimer = setInterval(() => this.sweepExpired(), sweepMs);
|
|
61
|
+
if (typeof this.sweepTimer === "object" && this.sweepTimer !== null) {
|
|
62
|
+
this.sweepTimer.unref?.();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async get(key) {
|
|
67
|
+
const e = this.data.get(key);
|
|
68
|
+
if (!e)
|
|
69
|
+
return;
|
|
70
|
+
if (e.expiresAt > 0 && e.expiresAt <= Date.now()) {
|
|
71
|
+
this.data.delete(key);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.data.delete(key);
|
|
75
|
+
this.data.set(key, e);
|
|
76
|
+
return e.value;
|
|
77
|
+
}
|
|
78
|
+
async set(key, value, opts = {}) {
|
|
79
|
+
const ttl = opts.ttl ?? 0;
|
|
80
|
+
const entry = {
|
|
81
|
+
value,
|
|
82
|
+
expiresAt: ttl > 0 ? Date.now() + ttl * 1000 : 0,
|
|
83
|
+
tags: opts.tags
|
|
84
|
+
};
|
|
85
|
+
if (!this.data.has(key) && this.data.size >= this.max) {
|
|
86
|
+
const oldest = this.data.keys().next().value;
|
|
87
|
+
if (oldest !== undefined) {
|
|
88
|
+
this.data.delete(oldest);
|
|
89
|
+
for (const keys of this.tagIndex.values())
|
|
90
|
+
keys.delete(oldest);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
this.data.set(key, entry);
|
|
94
|
+
if (opts.tags) {
|
|
95
|
+
for (const [, keys] of this.tagIndex.entries())
|
|
96
|
+
keys.delete(key);
|
|
97
|
+
for (const tag of opts.tags) {
|
|
98
|
+
let set = this.tagIndex.get(tag);
|
|
99
|
+
if (!set) {
|
|
100
|
+
set = new Set;
|
|
101
|
+
this.tagIndex.set(tag, set);
|
|
102
|
+
}
|
|
103
|
+
set.add(key);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async delete(key) {
|
|
108
|
+
const deleted = this.data.delete(key);
|
|
109
|
+
for (const keys of this.tagIndex.values())
|
|
110
|
+
keys.delete(key);
|
|
111
|
+
return deleted;
|
|
112
|
+
}
|
|
113
|
+
async clear(pattern) {
|
|
114
|
+
if (!pattern) {
|
|
115
|
+
const n2 = this.data.size;
|
|
116
|
+
this.data.clear();
|
|
117
|
+
this.tagIndex.clear();
|
|
118
|
+
return n2;
|
|
119
|
+
}
|
|
120
|
+
const rx = globToRegExp(pattern);
|
|
121
|
+
let n = 0;
|
|
122
|
+
for (const k of [...this.data.keys()]) {
|
|
123
|
+
if (rx.test(k)) {
|
|
124
|
+
this.data.delete(k);
|
|
125
|
+
for (const keys of this.tagIndex.values())
|
|
126
|
+
keys.delete(k);
|
|
127
|
+
n++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return n;
|
|
131
|
+
}
|
|
132
|
+
async wrap(key, fn, ttl) {
|
|
133
|
+
const v = await this.get(key);
|
|
134
|
+
if (v !== undefined)
|
|
135
|
+
return v;
|
|
136
|
+
const result = await fn();
|
|
137
|
+
await this.set(key, result, { ttl });
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
async invalidateByTag(tag) {
|
|
141
|
+
const keys = this.tagIndex.get(tag);
|
|
142
|
+
if (!keys)
|
|
143
|
+
return 0;
|
|
144
|
+
let n = 0;
|
|
145
|
+
for (const k of keys) {
|
|
146
|
+
if (this.data.delete(k))
|
|
147
|
+
n++;
|
|
148
|
+
}
|
|
149
|
+
this.tagIndex.delete(tag);
|
|
150
|
+
return n;
|
|
151
|
+
}
|
|
152
|
+
async close() {
|
|
153
|
+
if (this.sweepTimer)
|
|
154
|
+
clearInterval(this.sweepTimer);
|
|
155
|
+
this.sweepTimer = null;
|
|
156
|
+
this.data.clear();
|
|
157
|
+
this.tagIndex.clear();
|
|
158
|
+
}
|
|
159
|
+
sweepExpired() {
|
|
160
|
+
const now = Date.now();
|
|
161
|
+
for (const [k, e] of this.data.entries()) {
|
|
162
|
+
if (e.expiresAt > 0 && e.expiresAt <= now)
|
|
163
|
+
this.data.delete(k);
|
|
164
|
+
}
|
|
165
|
+
for (const [tag, keys] of this.tagIndex.entries()) {
|
|
166
|
+
for (const k of keys) {
|
|
167
|
+
if (!this.data.has(k))
|
|
168
|
+
keys.delete(k);
|
|
169
|
+
}
|
|
170
|
+
if (keys.size === 0)
|
|
171
|
+
this.tagIndex.delete(tag);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function globToRegExp(pattern) {
|
|
176
|
+
return new RegExp("^" + pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "::DBL::").replace(/\*/g, "[^:]+").replace(/::DBL::/g, ".*") + "$");
|
|
177
|
+
}
|
|
178
|
+
// packages/cache/src/stores/drizzle.ts
|
|
179
|
+
class DrizzleCacheStore {
|
|
180
|
+
kind = "drizzle";
|
|
181
|
+
db;
|
|
182
|
+
t;
|
|
183
|
+
tagsT;
|
|
184
|
+
c;
|
|
185
|
+
constructor(db, options = {}) {
|
|
186
|
+
this.db = db;
|
|
187
|
+
this.t = options.tableName ?? "nexus_cache";
|
|
188
|
+
this.tagsT = options.tagsTableName ?? "nexus_cache_tags";
|
|
189
|
+
this.c = {
|
|
190
|
+
key: options.columns?.key ?? "key",
|
|
191
|
+
value: options.columns?.value ?? "value",
|
|
192
|
+
expiresAt: options.columns?.expiresAt ?? "expires_at",
|
|
193
|
+
createdAt: options.columns?.createdAt ?? "created_at",
|
|
194
|
+
updatedAt: options.columns?.updatedAt ?? "updated_at",
|
|
195
|
+
tag: options.columns?.tag ?? "tag"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
async get(key) {
|
|
199
|
+
const rows = await this.db.rawQuery(`SELECT * FROM ${this.t} WHERE ${this.c.key} = ? LIMIT 1`, [key]);
|
|
200
|
+
const row = rows[0];
|
|
201
|
+
if (!row)
|
|
202
|
+
return;
|
|
203
|
+
const expiresAt = row[this.c.expiresAt];
|
|
204
|
+
if (expiresAt && new Date(expiresAt).getTime() <= Date.now()) {
|
|
205
|
+
await this.delete(key);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const raw = row[this.c.value];
|
|
209
|
+
if (raw === null || raw === undefined)
|
|
210
|
+
return;
|
|
211
|
+
try {
|
|
212
|
+
return JSON.parse(String(raw));
|
|
213
|
+
} catch {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async set(key, value, opts = {}) {
|
|
218
|
+
const now = new Date().toISOString();
|
|
219
|
+
const expiresAt = opts.ttl && opts.ttl > 0 ? new Date(Date.now() + opts.ttl * 1000).toISOString() : null;
|
|
220
|
+
const serialized = JSON.stringify(value);
|
|
221
|
+
await this.db.rawQuery(`INSERT INTO ${this.t} (${this.c.key}, ${this.c.value}, ${this.c.expiresAt}, ${this.c.createdAt}, ${this.c.updatedAt})
|
|
222
|
+
VALUES (?, ?, ?, ?, ?)
|
|
223
|
+
ON CONFLICT (${this.c.key}) DO UPDATE SET
|
|
224
|
+
${this.c.value} = excluded.${this.c.value},
|
|
225
|
+
${this.c.expiresAt} = excluded.${this.c.expiresAt},
|
|
226
|
+
${this.c.updatedAt} = excluded.${this.c.updatedAt}`, [key, serialized, expiresAt, now, now]);
|
|
227
|
+
if (opts.tags && opts.tags.length > 0) {
|
|
228
|
+
await this.db.rawQuery(`DELETE FROM ${this.tagsT} WHERE ${this.c.key} = ?`, [key]);
|
|
229
|
+
for (const tag of opts.tags) {
|
|
230
|
+
await this.db.rawQuery(`INSERT OR IGNORE INTO ${this.tagsT} (${this.c.tag}, ${this.c.key}) VALUES (?, ?)`, [tag, key]);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async delete(key) {
|
|
235
|
+
const probe = await this.db.rawQuery(`SELECT ${this.c.key} FROM ${this.t} WHERE ${this.c.key} = ? LIMIT 1`, [key]);
|
|
236
|
+
if (probe.length === 0)
|
|
237
|
+
return false;
|
|
238
|
+
await this.db.rawQuery(`DELETE FROM ${this.t} WHERE ${this.c.key} = ?`, [key]);
|
|
239
|
+
await this.db.rawQuery(`DELETE FROM ${this.tagsT} WHERE ${this.c.key} = ?`, [key]);
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
async clear(pattern) {
|
|
243
|
+
if (!pattern) {
|
|
244
|
+
await this.db.rawQuery(`DELETE FROM ${this.tagsT}`);
|
|
245
|
+
const before2 = await this.db.rawQuery(`SELECT COUNT(*) as n FROM ${this.t}`);
|
|
246
|
+
await this.db.rawQuery(`DELETE FROM ${this.t}`);
|
|
247
|
+
const n = Number(before2[0]?.n ?? 0);
|
|
248
|
+
return n;
|
|
249
|
+
}
|
|
250
|
+
const likePattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "%").replace(/\*/g, "%");
|
|
251
|
+
const before = await this.db.rawQuery(`SELECT COUNT(*) as n FROM ${this.t} WHERE ${this.c.key} LIKE ? ESCAPE '\\'`, [likePattern]);
|
|
252
|
+
await this.db.rawQuery(`DELETE FROM ${this.t} WHERE ${this.c.key} LIKE ? ESCAPE '\\'`, [likePattern]);
|
|
253
|
+
return Number(before[0]?.n ?? 0);
|
|
254
|
+
}
|
|
255
|
+
async wrap(key, fn, ttl) {
|
|
256
|
+
const hit = await this.get(key);
|
|
257
|
+
if (hit !== undefined)
|
|
258
|
+
return hit;
|
|
259
|
+
const v = await fn();
|
|
260
|
+
await this.set(key, v, { ttl });
|
|
261
|
+
return v;
|
|
262
|
+
}
|
|
263
|
+
async invalidateByTag(tag) {
|
|
264
|
+
const tagRows = await this.db.rawQuery(`SELECT ${this.c.key} FROM ${this.tagsT} WHERE ${this.c.tag} = ?`, [tag]);
|
|
265
|
+
const keys = tagRows.map((r) => r[this.c.key]);
|
|
266
|
+
if (keys.length === 0)
|
|
267
|
+
return 0;
|
|
268
|
+
const chunkSize = 100;
|
|
269
|
+
let removed = 0;
|
|
270
|
+
for (let i = 0;i < keys.length; i += chunkSize) {
|
|
271
|
+
const chunk = keys.slice(i, i + chunkSize);
|
|
272
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
273
|
+
const r1 = await this.db.rawQuery(`DELETE FROM ${this.t} WHERE ${this.c.key} IN (${placeholders})`, chunk);
|
|
274
|
+
const r2 = await this.db.rawQuery(`DELETE FROM ${this.tagsT} WHERE ${this.c.key} IN (${placeholders})`, chunk);
|
|
275
|
+
removed += Math.max(r1.length, r2.length, chunk.length);
|
|
276
|
+
}
|
|
277
|
+
return removed;
|
|
278
|
+
}
|
|
279
|
+
async gc() {
|
|
280
|
+
const now = new Date().toISOString();
|
|
281
|
+
const before = await this.db.rawQuery(`SELECT COUNT(*) as n FROM ${this.t} WHERE ${this.c.expiresAt} IS NOT NULL AND ${this.c.expiresAt} <= ?`, [now]);
|
|
282
|
+
await this.db.rawQuery(`DELETE FROM ${this.t} WHERE ${this.c.expiresAt} IS NOT NULL AND ${this.c.expiresAt} <= ?`, [now]);
|
|
283
|
+
await this.db.rawQuery(`DELETE FROM ${this.tagsT} WHERE ${this.c.key} NOT IN (SELECT ${this.c.key} FROM ${this.t})`);
|
|
284
|
+
return Number(before[0]?.n ?? 0);
|
|
285
|
+
}
|
|
286
|
+
async close() {}
|
|
287
|
+
}
|
|
288
|
+
// packages/cache/src/stores/redis.ts
|
|
289
|
+
var DEFAULT_KEY_PREFIX = "cache:";
|
|
290
|
+
|
|
291
|
+
class RedisCacheStore {
|
|
292
|
+
kind = "redis";
|
|
293
|
+
#client;
|
|
294
|
+
#keyPrefix;
|
|
295
|
+
#tagKeyPrefix;
|
|
296
|
+
constructor(client, options = {}) {
|
|
297
|
+
this.#client = client;
|
|
298
|
+
this.#keyPrefix = options.keyPrefix ?? DEFAULT_KEY_PREFIX;
|
|
299
|
+
this.#tagKeyPrefix = `${this.#keyPrefix}tag:`;
|
|
300
|
+
}
|
|
301
|
+
#key(key) {
|
|
302
|
+
return `${this.#keyPrefix}${key}`;
|
|
303
|
+
}
|
|
304
|
+
#tagKey(tag) {
|
|
305
|
+
return `${this.#tagKeyPrefix}${crc32(tag)}`;
|
|
306
|
+
}
|
|
307
|
+
async get(key) {
|
|
308
|
+
const raw = await this.#client.get(this.#key(key));
|
|
309
|
+
if (raw === null)
|
|
310
|
+
return;
|
|
311
|
+
try {
|
|
312
|
+
const entry = JSON.parse(raw);
|
|
313
|
+
if (entry.expiresAt && entry.expiresAt <= Date.now()) {
|
|
314
|
+
await this.#client.del(this.#key(key));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
return entry.value;
|
|
318
|
+
} catch {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async set(key, value, opts) {
|
|
323
|
+
const ttl = opts?.ttl ?? 0;
|
|
324
|
+
const tags = opts?.tags ?? [];
|
|
325
|
+
const entry = {
|
|
326
|
+
value,
|
|
327
|
+
expiresAt: ttl > 0 ? Date.now() + ttl * 1000 : 0,
|
|
328
|
+
...tags.length > 0 ? { tags } : {}
|
|
329
|
+
};
|
|
330
|
+
const fullKey = this.#key(key);
|
|
331
|
+
const ex = ttl > 0 ? ttl : undefined;
|
|
332
|
+
await this.#client.set(fullKey, JSON.stringify(entry), ex ? { ex } : undefined);
|
|
333
|
+
for (const tag of tags) {
|
|
334
|
+
await this.#addToTagIndex(tag, key);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async delete(key) {
|
|
338
|
+
const fullKey = this.#key(key);
|
|
339
|
+
const existed = await this.#client.get(fullKey) !== null;
|
|
340
|
+
await this.#client.del(fullKey);
|
|
341
|
+
return existed;
|
|
342
|
+
}
|
|
343
|
+
async has(key) {
|
|
344
|
+
return this.#client.exists(this.#key(key));
|
|
345
|
+
}
|
|
346
|
+
async clear() {
|
|
347
|
+
let n = 0;
|
|
348
|
+
let cursor = "0";
|
|
349
|
+
do {
|
|
350
|
+
const res = await this.#client.scan({
|
|
351
|
+
match: `${this.#keyPrefix}*`,
|
|
352
|
+
cursor,
|
|
353
|
+
count: 100
|
|
354
|
+
});
|
|
355
|
+
for (const k of res.keys) {
|
|
356
|
+
await this.#client.del(this.#keyPrefix + k);
|
|
357
|
+
n++;
|
|
358
|
+
}
|
|
359
|
+
cursor = res.cursor;
|
|
360
|
+
} while (cursor !== "0" && cursor !== 0);
|
|
361
|
+
return n;
|
|
362
|
+
}
|
|
363
|
+
async gc() {
|
|
364
|
+
let removed = 0;
|
|
365
|
+
let cursor = "0";
|
|
366
|
+
do {
|
|
367
|
+
const res = await this.#client.scan({
|
|
368
|
+
match: `${this.#tagKeyPrefix}*`,
|
|
369
|
+
cursor,
|
|
370
|
+
count: 100
|
|
371
|
+
});
|
|
372
|
+
for (const k of res.keys) {
|
|
373
|
+
const raw = await this.#client.get(this.#keyPrefix + k);
|
|
374
|
+
const ids = raw ? safeParseStringArray(raw) : [];
|
|
375
|
+
const live = [];
|
|
376
|
+
for (const id of ids) {
|
|
377
|
+
if (await this.#client.exists(this.#key(id)))
|
|
378
|
+
live.push(id);
|
|
379
|
+
}
|
|
380
|
+
if (live.length === 0) {
|
|
381
|
+
await this.#client.del(this.#keyPrefix + k);
|
|
382
|
+
removed++;
|
|
383
|
+
} else if (live.length !== ids.length) {
|
|
384
|
+
await this.#client.set(this.#keyPrefix + k, JSON.stringify(live));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
cursor = res.cursor;
|
|
388
|
+
} while (cursor !== "0" && cursor !== 0);
|
|
389
|
+
return removed;
|
|
390
|
+
}
|
|
391
|
+
async invalidateByTag(tag) {
|
|
392
|
+
const tagK = this.#tagKey(tag);
|
|
393
|
+
const raw = await this.#client.get(tagK);
|
|
394
|
+
if (!raw)
|
|
395
|
+
return 0;
|
|
396
|
+
const keys = safeParseStringArray(raw);
|
|
397
|
+
let n = 0;
|
|
398
|
+
for (const k of keys) {
|
|
399
|
+
await this.#client.del(this.#key(k));
|
|
400
|
+
n++;
|
|
401
|
+
}
|
|
402
|
+
await this.#client.del(tagK);
|
|
403
|
+
return n;
|
|
404
|
+
}
|
|
405
|
+
async invalidateByTags(tags) {
|
|
406
|
+
let n = 0;
|
|
407
|
+
for (const t of tags)
|
|
408
|
+
n += await this.invalidateByTag(t);
|
|
409
|
+
return n;
|
|
410
|
+
}
|
|
411
|
+
async wrap(key, fn, ttl) {
|
|
412
|
+
const hit = await this.get(key);
|
|
413
|
+
if (hit !== undefined)
|
|
414
|
+
return hit;
|
|
415
|
+
const value = await fn();
|
|
416
|
+
await this.set(key, value, ttl !== undefined ? { ttl } : undefined);
|
|
417
|
+
return value;
|
|
418
|
+
}
|
|
419
|
+
async close() {
|
|
420
|
+
await this.#client.close();
|
|
421
|
+
}
|
|
422
|
+
async#addToTagIndex(tag, key) {
|
|
423
|
+
const k = this.#tagKey(tag);
|
|
424
|
+
const raw = await this.#client.get(k);
|
|
425
|
+
const ids = raw ? safeParseStringArray(raw) : [];
|
|
426
|
+
if (!ids.includes(key))
|
|
427
|
+
ids.push(key);
|
|
428
|
+
await this.#client.set(k, JSON.stringify(ids));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function safeParseStringArray(raw) {
|
|
432
|
+
try {
|
|
433
|
+
const v = JSON.parse(raw);
|
|
434
|
+
return Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
435
|
+
} catch {
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function crc32(s) {
|
|
440
|
+
let c = 4294967295;
|
|
441
|
+
for (let i = 0;i < s.length; i++) {
|
|
442
|
+
c ^= s.charCodeAt(i);
|
|
443
|
+
for (let k = 0;k < 8; k++) {
|
|
444
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return (c ^ 4294967295).toString(16);
|
|
448
|
+
}
|
|
449
|
+
// packages/cache/src/cache.service.ts
|
|
450
|
+
import { Inject, Injectable } from "@nexusts/core/decorators/index.js";
|
|
451
|
+
class CacheService {
|
|
452
|
+
static TOKEN = Symbol.for("nexus:CacheService");
|
|
453
|
+
store;
|
|
454
|
+
defaultTtl;
|
|
455
|
+
prefix;
|
|
456
|
+
constructor(config = {}) {
|
|
457
|
+
this.store = config.store ?? new MemoryStore;
|
|
458
|
+
this.defaultTtl = config.defaultTtl ?? 60;
|
|
459
|
+
this.prefix = config.prefix ?? "nexusjs";
|
|
460
|
+
}
|
|
461
|
+
key(k) {
|
|
462
|
+
return `${this.prefix}:${k}`;
|
|
463
|
+
}
|
|
464
|
+
async get(k) {
|
|
465
|
+
return this.store.get(this.key(k));
|
|
466
|
+
}
|
|
467
|
+
async set(k, value, optsOrTtl = this.defaultTtl) {
|
|
468
|
+
const opts = typeof optsOrTtl === "number" ? { ttl: optsOrTtl } : { ttl: optsOrTtl.ttl ?? this.defaultTtl, tags: optsOrTtl.tags };
|
|
469
|
+
await this.store.set(this.key(k), value, opts);
|
|
470
|
+
}
|
|
471
|
+
async delete(k) {
|
|
472
|
+
return this.store.delete(this.key(k));
|
|
473
|
+
}
|
|
474
|
+
async clear(pattern) {
|
|
475
|
+
return this.store.clear(pattern ? `${this.prefix}:${pattern}` : undefined);
|
|
476
|
+
}
|
|
477
|
+
async wrap(k, fn, ttl) {
|
|
478
|
+
return this.store.wrap(this.key(k), fn, ttl ?? this.defaultTtl);
|
|
479
|
+
}
|
|
480
|
+
async invalidateByTag(tag) {
|
|
481
|
+
if (typeof this.store.invalidateByTag === "function") {
|
|
482
|
+
return await this.store.invalidateByTag(this.prefixedTag(tag));
|
|
483
|
+
}
|
|
484
|
+
return 0;
|
|
485
|
+
}
|
|
486
|
+
async gc() {
|
|
487
|
+
if (typeof this.store.gc === "function") {
|
|
488
|
+
return await this.store.gc();
|
|
489
|
+
}
|
|
490
|
+
return 0;
|
|
491
|
+
}
|
|
492
|
+
prefixedTag(tag) {
|
|
493
|
+
return `${this.prefix}:${tag}`;
|
|
494
|
+
}
|
|
495
|
+
applyDecorators(target) {
|
|
496
|
+
const ctor = target.constructor;
|
|
497
|
+
const cacheables = getCacheableSpecs(ctor);
|
|
498
|
+
for (const spec of cacheables) {
|
|
499
|
+
const original = spec.original;
|
|
500
|
+
target[spec.propertyKey] = async (...args) => {
|
|
501
|
+
const subKey = spec.keyFn(...args);
|
|
502
|
+
return this.wrap(`${spec.prefix}:${subKey}`, () => original.apply(target, args));
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
const invalidates = getCacheInvalidateSpecs(ctor);
|
|
506
|
+
for (const spec of invalidates) {
|
|
507
|
+
const original = spec.original;
|
|
508
|
+
target[spec.propertyKey] = async (...args) => {
|
|
509
|
+
const result = await original.apply(target, args);
|
|
510
|
+
const subKey = spec.keyFn(...args);
|
|
511
|
+
await this.clear(`${spec.prefix}:${subKey}*`);
|
|
512
|
+
return result;
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
CacheService = __legacyDecorateClassTS([
|
|
518
|
+
Injectable(),
|
|
519
|
+
__legacyDecorateParamTS(0, Inject("CACHE_CONFIG")),
|
|
520
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
521
|
+
typeof CacheConfig === "undefined" ? Object : CacheConfig
|
|
522
|
+
])
|
|
523
|
+
], CacheService);
|
|
524
|
+
// packages/cache/src/cache.module.ts
|
|
525
|
+
import"reflect-metadata";
|
|
526
|
+
import { Module } from "@nexusts/core/decorators/module.js";
|
|
527
|
+
class CacheModule {
|
|
528
|
+
static forRoot(config = {}) {
|
|
529
|
+
const cfg = {
|
|
530
|
+
store: new MemoryStore,
|
|
531
|
+
...config
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
class ConfiguredCacheModule {
|
|
535
|
+
}
|
|
536
|
+
ConfiguredCacheModule = __legacyDecorateClassTS([
|
|
537
|
+
Module({
|
|
538
|
+
providers: [
|
|
539
|
+
CacheService,
|
|
540
|
+
{ provide: CacheService.TOKEN, useExisting: CacheService },
|
|
541
|
+
{ provide: "CACHE_CONFIG", useValue: cfg }
|
|
542
|
+
],
|
|
543
|
+
exports: [CacheService, CacheService.TOKEN]
|
|
544
|
+
})
|
|
545
|
+
], ConfiguredCacheModule);
|
|
546
|
+
Object.defineProperty(ConfiguredCacheModule, "name", {
|
|
547
|
+
value: "ConfiguredCacheModule"
|
|
548
|
+
});
|
|
549
|
+
return ConfiguredCacheModule;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
CacheModule = __legacyDecorateClassTS([
|
|
553
|
+
Module({
|
|
554
|
+
providers: [
|
|
555
|
+
CacheService,
|
|
556
|
+
{ provide: CacheService.TOKEN, useExisting: CacheService }
|
|
557
|
+
],
|
|
558
|
+
exports: [CacheService, CacheService.TOKEN]
|
|
559
|
+
})
|
|
560
|
+
], CacheModule);
|
|
561
|
+
export {
|
|
562
|
+
getCacheableSpecs,
|
|
563
|
+
getCacheInvalidateSpecs,
|
|
564
|
+
MemoryStore,
|
|
565
|
+
METADATA_KEY,
|
|
566
|
+
DrizzleCacheStore,
|
|
567
|
+
Cacheable,
|
|
568
|
+
CacheService,
|
|
569
|
+
CacheModule,
|
|
570
|
+
CacheInvalidate,
|
|
571
|
+
CACHE_INVALIDATE_META,
|
|
572
|
+
CACHEABLE_META
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
//# debugId=6BDBD5106C96A0F964756E2164756E21
|
|
576
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/types.ts", "../src/stores/memory.ts", "../src/stores/drizzle.ts", "../src/stores/redis.ts", "../src/cache.service.ts", "../src/cache.module.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * `nexusjs/cache` — application-level caching.\n *\n * Two backends ship out of the box:\n * - `MemoryStore` — single-process LRU with TTL eviction\n * - `RedisStore` — multi-pod via ioredis (peer dep, optional)\n *\n * const cache = new CacheService({ store: new MemoryStore({ max: 10_000 }) });\n * await cache.set('user:42', user, 60); // 60-second TTL\n * const u = await cache.get<User>('user:42');\n *\n * Decorators are also provided for service methods:\n *\n * @Cacheable('user', (id: string) => id, 60)\n * async findById(id: string) { ... }\n */\n\nimport \"reflect-metadata\";\nimport { METADATA_KEY } from \"@nexusts/core/constants.js\";\n\n/** A single cache entry. */\nexport interface CacheEntry<T = unknown> {\n\tvalue: T;\n\t/** Unix-ms timestamp when this entry expires. 0 = never. */\n\texpiresAt: number;\n\t/** Stash tags for invalidation. */\n\ttags?: string[];\n}\n\n/** Storage backend for cache entries. */\nexport interface CacheStore {\n\treadonly kind: string;\n\t/** Get a value. Returns `undefined` if missing or expired. */\n\tget<T = unknown>(key: string): Promise<T | undefined>;\n\t/** Set a value with optional TTL (seconds) and tags. */\n\tset<T = unknown>(\n\t\tkey: string,\n\t\tvalue: T,\n\t\topts?: CacheSetOptions,\n\t): Promise<void>;\n\t/** Delete a single key. */\n\tdelete(key: string): Promise<boolean>;\n\t/** Delete every key matching `pattern` (glob: `*`, `**`). */\n\tclear(pattern?: string): Promise<number>;\n\t/** Wrap a function with cache-or-compute semantics. */\n\twrap<T>(key: string, fn: () => Promise<T>, ttl?: number): Promise<T>;\n\t/**\n\t * Remove every entry tagged with `tag`. Backends without a tag\n\t * index (e.g. MemoryStore) return 0.\n\t */\n\tinvalidateByTag?(tag: string): Promise<number>;\n\t/** Sweep expired entries. Backends without a sweep loop return 0. */\n\tgc?(): Promise<number>;\n\t/** Optional: free resources. */\n\tclose?(): Promise<void>;\n}\n\n/** Options for `set()`. */\nexport interface CacheSetOptions {\n\t/** Time-to-live in seconds. 0 = forever. */\n\tttl?: number;\n\t/** Tags for grouped invalidation. */\n\ttags?: string[];\n}\n\nexport interface CacheConfig {\n\t/** Storage backend. Default: in-memory LRU. */\n\tstore?: CacheStore;\n\t/** Default TTL in seconds when none is provided. Default: 60. */\n\tdefaultTtl?: number;\n\t/** Prefix prepended to all keys. Default: 'nexusjs'. */\n\tprefix?: string;\n}\n\n/** Internal metadata key. */\nexport const CACHEABLE_META = \"nexus:cache:cacheable\";\nexport const CACHE_INVALIDATE_META = \"nexus:cache:invalidate\";\n\n/** @Cacheable decorator. Caches the result of a method. */\nexport function Cacheable(\n\tprefix: string,\n\tkeyFn: (...args: any[]) => string,\n\tttlSeconds = 60,\n): MethodDecorator {\n\treturn (\n\t\ttarget: any,\n\t\tpropertyKey: string | symbol,\n\t\tdescriptor: PropertyDescriptor,\n\t) => {\n\t\tconst existing: CacheableSpec[] =\n\t\t\tReflect.getMetadata(CACHEABLE_META, target.constructor) ?? [];\n\t\texisting.push({\n\t\t\tprefix,\n\t\t\tkeyFn,\n\t\t\tttl: ttlSeconds,\n\t\t\tpropertyKey,\n\t\t\toriginal: descriptor.value,\n\t\t});\n\t\tReflect.defineMetadata(CACHEABLE_META, existing, target.constructor);\n\t};\n}\n\n/** @CacheInvalidate decorator. Removes matching keys after a method runs. */\nexport function CacheInvalidate(\n\tprefix: string,\n\tkeyFn: (...args: any[]) => string,\n): MethodDecorator {\n\treturn (\n\t\ttarget: any,\n\t\tpropertyKey: string | symbol,\n\t\tdescriptor: PropertyDescriptor,\n\t) => {\n\t\tconst existing: CacheInvalidateSpec[] =\n\t\t\tReflect.getMetadata(CACHE_INVALIDATE_META, target.constructor) ?? [];\n\t\texisting.push({ prefix, keyFn, propertyKey, original: descriptor.value });\n\t\tReflect.defineMetadata(CACHE_INVALIDATE_META, existing, target.constructor);\n\t};\n}\n\nexport interface CacheableSpec {\n\tprefix: string;\n\tkeyFn: (...args: any[]) => string;\n\tttl: number;\n\tpropertyKey: string | symbol;\n\toriginal: (...args: any[]) => any;\n}\n\nexport interface CacheInvalidateSpec {\n\tprefix: string;\n\tkeyFn: (...args: any[]) => string;\n\tpropertyKey: string | symbol;\n\toriginal: (...args: any[]) => any;\n}\n\nexport function getCacheableSpecs(target: any): CacheableSpec[] {\n\treturn Reflect.getMetadata(CACHEABLE_META, target) ?? [];\n}\nexport function getCacheInvalidateSpecs(target: any): CacheInvalidateSpec[] {\n\treturn Reflect.getMetadata(CACHE_INVALIDATE_META, target) ?? [];\n}\n\nexport { METADATA_KEY };\n",
|
|
6
|
+
"/**\n * In-memory LRU cache store with TTL eviction.\n *\n * Simple and fast; not cluster-safe. Use `RedisStore` for shared state\n * across multiple Bun processes.\n */\nimport type { CacheEntry, CacheSetOptions, CacheStore } from \"../types.js\";\n\nexport interface MemoryStoreOptions {\n\t/** Maximum number of entries. Default: 10_000. */\n\tmax?: number;\n\t/** Sweep interval for expired entries. Default: 30_000 ms. 0 = no sweep. */\n\tsweepIntervalMs?: number;\n}\n\nexport class MemoryStore implements CacheStore {\n\treadonly kind = \"memory\";\n\tprivate data = new Map<string, CacheEntry>();\n\tprivate tagIndex = new Map<string, Set<string>>();\n\tprivate readonly max: number;\n\tprivate sweepTimer: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor(opts: MemoryStoreOptions = {}) {\n\t\tthis.max = opts.max ?? 10_000;\n\t\tconst sweepMs = opts.sweepIntervalMs ?? 30_000;\n\t\tif (sweepMs > 0) {\n\t\t\tthis.sweepTimer = setInterval(() => this.sweepExpired(), sweepMs);\n\t\t\t// Don't keep the event loop alive for the sweep timer.\n\t\t\tif (typeof this.sweepTimer === \"object\" && this.sweepTimer !== null) {\n\t\t\t\t(this.sweepTimer as any).unref?.();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync get<T = unknown>(key: string): Promise<T | undefined> {\n\t\tconst e = this.data.get(key);\n\t\tif (!e) return undefined;\n\t\tif (e.expiresAt > 0 && e.expiresAt <= Date.now()) {\n\t\t\tthis.data.delete(key);\n\t\t\treturn undefined;\n\t\t}\n\t\t// Touch for LRU.\n\t\tthis.data.delete(key);\n\t\tthis.data.set(key, e);\n\t\treturn e.value as T;\n\t}\n\n\tasync set<T = unknown>(\n\t\tkey: string,\n\t\tvalue: T,\n\t\topts: CacheSetOptions = {},\n\t): Promise<void> {\n\t\tconst ttl = opts.ttl ?? 0;\n\t\tconst entry: CacheEntry<T> = {\n\t\t\tvalue,\n\t\t\texpiresAt: ttl > 0 ? Date.now() + ttl * 1000 : 0,\n\t\t\ttags: opts.tags,\n\t\t};\n\t\t// Insert (and possibly evict).\n\t\tif (!this.data.has(key) && this.data.size >= this.max) {\n\t\t\t// Evict oldest (first key in iteration order).\n\t\t\tconst oldest = this.data.keys().next().value;\n\t\t\tif (oldest !== undefined) {\n\t\t\t\tthis.data.delete(oldest);\n\t\t\t\tfor (const keys of this.tagIndex.values()) keys.delete(oldest);\n\t\t\t}\n\t\t}\n\t\tthis.data.set(key, entry);\n\t\t// Refresh tag index: remove old tag associations for this key, then add new ones.\n\t\tif (opts.tags) {\n\t\t\tfor (const [, keys] of this.tagIndex.entries()) keys.delete(key);\n\t\t\tfor (const tag of opts.tags) {\n\t\t\t\tlet set = this.tagIndex.get(tag);\n\t\t\t\tif (!set) {\n\t\t\t\t\tset = new Set();\n\t\t\t\t\tthis.tagIndex.set(tag, set);\n\t\t\t\t}\n\t\t\t\tset.add(key);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync delete(key: string): Promise<boolean> {\n\t\tconst deleted = this.data.delete(key);\n\t\t// Remove this key from every tag index.\n\t\tfor (const keys of this.tagIndex.values()) keys.delete(key);\n\t\treturn deleted;\n\t}\n\n\tasync clear(pattern?: string): Promise<number> {\n\t\tif (!pattern) {\n\t\t\tconst n = this.data.size;\n\t\t\tthis.data.clear();\n\t\t\tthis.tagIndex.clear();\n\t\t\treturn n;\n\t\t}\n\t\tconst rx = globToRegExp(pattern);\n\t\tlet n = 0;\n\t\tfor (const k of [...this.data.keys()]) {\n\t\t\tif (rx.test(k)) {\n\t\t\t\tthis.data.delete(k);\n\t\t\t\tfor (const keys of this.tagIndex.values()) keys.delete(k);\n\t\t\t\tn++;\n\t\t\t}\n\t\t}\n\t\treturn n;\n\t}\n\n\tasync wrap<T>(key: string, fn: () => Promise<T>, ttl?: number): Promise<T> {\n\t\tconst v = await this.get<T>(key);\n\t\tif (v !== undefined) return v;\n\t\tconst result = await fn();\n\t\tawait this.set(key, result, { ttl });\n\t\treturn result;\n\t}\n\n\t/**\n\t * Tag-based invalidation. Maintains a `tag -> Set<key>` index in\n\t * addition to the main map. Returns the number of keys removed.\n\t */\n\tasync invalidateByTag(tag: string): Promise<number> {\n\t\tconst keys = this.tagIndex.get(tag);\n\t\tif (!keys) return 0;\n\t\tlet n = 0;\n\t\tfor (const k of keys) {\n\t\t\tif (this.data.delete(k)) n++;\n\t\t}\n\t\tthis.tagIndex.delete(tag);\n\t\treturn n;\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.sweepTimer) clearInterval(this.sweepTimer);\n\t\tthis.sweepTimer = null;\n\t\tthis.data.clear();\n\t\tthis.tagIndex.clear();\n\t}\n\n\tprivate sweepExpired(): void {\n\t\tconst now = Date.now();\n\t\tfor (const [k, e] of this.data.entries()) {\n\t\t\tif (e.expiresAt > 0 && e.expiresAt <= now) this.data.delete(k);\n\t\t}\n\t\t// Drop tags whose keys are all gone.\n\t\tfor (const [tag, keys] of this.tagIndex.entries()) {\n\t\t\tfor (const k of keys) {\n\t\t\t\tif (!this.data.has(k)) keys.delete(k);\n\t\t\t}\n\t\t\tif (keys.size === 0) this.tagIndex.delete(tag);\n\t\t}\n\t}\n}\n\nfunction globToRegExp(pattern: string): RegExp {\n\treturn new RegExp(\n\t\t\"^\" +\n\t\t\tpattern\n\t\t\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t\t\t.replace(/\\*\\*/g, \"::DBL::\")\n\t\t\t\t.replace(/\\*/g, \"[^:]+\")\n\t\t\t\t.replace(/::DBL::/g, \".*\") +\n\t\t\t\"$\",\n\t);\n}\n",
|
|
7
|
+
"/**\n * `DrizzleCacheStore` — cache entries backed by any Drizzle database.\n *\n * import { DrizzleService } from 'nexusjs/drizzle';\n * import { DrizzleCacheStore } from 'nexusjs/cache';\n *\n * const db = new DrizzleService({ dialect: 'postgres', connection: {...} });\n * await db.open();\n *\n * const store = new DrizzleCacheStore(db, {\n * tableName: 'nexus_cache',\n * });\n *\n * CacheModule.forRoot({ store, defaultTtl: 300 });\n *\n * Schema (managed by your migration):\n *\n * CREATE TABLE nexus_cache (\n * key TEXT PRIMARY KEY,\n * value TEXT NOT NULL, -- JSON-encoded\n * expires_at TEXT, -- ISO timestamp, null = never\n * created_at TEXT NOT NULL,\n * updated_at TEXT NOT NULL\n * );\n *\n * CREATE TABLE nexus_cache_tags ( -- tag → key index\n * tag TEXT NOT NULL,\n * key TEXT NOT NULL,\n * PRIMARY KEY (tag, key)\n * );\n * CREATE INDEX nexus_cache_tags_key_idx ON nexus_cache_tags(key);\n *\n * Why a tag table? It enables true `invalidateByTag('users')` that\n * removes every entry tagged 'users' in a single statement, regardless\n * of how many keys share the tag.\n */\nimport type { DrizzleService } from \"../../drizzle/drizzle.service.js\";\nimport type { CacheSetOptions, CacheStore } from \"../types.js\";\n\nexport interface DrizzleCacheOptions {\n\tdb: DrizzleService;\n\t/** Cache row table. Default: 'nexus_cache'. */\n\ttableName?: string;\n\t/** Tag index table. Default: 'nexus_cache_tags'. */\n\ttagsTableName?: string;\n\t/** Column names — override to match your schema. */\n\tcolumns?: {\n\t\tkey?: string;\n\t\tvalue?: string;\n\t\texpiresAt?: string;\n\t\tcreatedAt?: string;\n\t\tupdatedAt?: string;\n\t\ttag?: string;\n\t};\n}\n\ntype CacheRow = Record<string, string | null>;\ntype TagRow = Record<string, string>;\n\nexport class DrizzleCacheStore implements CacheStore {\n\treadonly kind = \"drizzle\" as const;\n\n\tprivate db: DrizzleService;\n\tprivate t: string;\n\tprivate tagsT: string;\n\tprivate c: {\n\t\tkey: string;\n\t\tvalue: string;\n\t\texpiresAt: string;\n\t\tcreatedAt: string;\n\t\tupdatedAt: string;\n\t\ttag: string;\n\t};\n\n\tconstructor(db: DrizzleService, options: Omit<DrizzleCacheOptions, \"db\"> = {}) {\n\t\tthis.db = db;\n\t\tthis.t = options.tableName ?? \"nexus_cache\";\n\t\tthis.tagsT = options.tagsTableName ?? \"nexus_cache_tags\";\n\t\tthis.c = {\n\t\t\tkey: options.columns?.key ?? \"key\",\n\t\t\tvalue: options.columns?.value ?? \"value\",\n\t\t\texpiresAt: options.columns?.expiresAt ?? \"expires_at\",\n\t\t\tcreatedAt: options.columns?.createdAt ?? \"created_at\",\n\t\t\tupdatedAt: options.columns?.updatedAt ?? \"updated_at\",\n\t\t\ttag: options.columns?.tag ?? \"tag\",\n\t\t};\n\t}\n\n\tasync get<T = unknown>(key: string): Promise<T | undefined> {\n\t\tconst rows = await this.db.rawQuery<CacheRow>(\n\t\t\t`SELECT * FROM ${this.t} WHERE ${this.c.key} = ? LIMIT 1`,\n\t\t\t[key],\n\t\t);\n\t\tconst row = rows[0];\n\t\tif (!row) return undefined;\n\t\tconst expiresAt = row[this.c.expiresAt];\n\t\tif (expiresAt && new Date(expiresAt).getTime() <= Date.now()) {\n\t\t\t// Expired — clean up lazily.\n\t\t\tawait this.delete(key);\n\t\t\treturn undefined;\n\t\t}\n\t\tconst raw = row[this.c.value];\n\t\tif (raw === null || raw === undefined) return undefined;\n\t\ttry {\n\t\t\treturn JSON.parse(String(raw)) as T;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tasync set<T = unknown>(\n\t\tkey: string,\n\t\tvalue: T,\n\t\topts: CacheSetOptions = {},\n\t): Promise<void> {\n\t\tconst now = new Date().toISOString();\n\t\tconst expiresAt =\n\t\t\topts.ttl && opts.ttl > 0\n\t\t\t\t? new Date(Date.now() + opts.ttl * 1000).toISOString()\n\t\t\t\t: null;\n\t\tconst serialized = JSON.stringify(value);\n\n\t\t// Upsert the cache row. Different dialects use different syntax;\n\t\t// we use the simple INSERT ... ON CONFLICT pattern (works in\n\t\t// sqlite, postgres, mysql 8+).\n\t\tawait this.db.rawQuery(\n\t\t\t`INSERT INTO ${this.t} (${this.c.key}, ${this.c.value}, ${this.c.expiresAt}, ${this.c.createdAt}, ${this.c.updatedAt})\n\t\t\t VALUES (?, ?, ?, ?, ?)\n\t\t\t ON CONFLICT (${this.c.key}) DO UPDATE SET\n\t\t\t ${this.c.value} = excluded.${this.c.value},\n\t\t\t ${this.c.expiresAt} = excluded.${this.c.expiresAt},\n\t\t\t ${this.c.updatedAt} = excluded.${this.c.updatedAt}`,\n\t\t\t[key, serialized, expiresAt, now, now],\n\t\t);\n\n\t\t// Refresh tag index: delete old tags, then insert new ones.\n\t\tif (opts.tags && opts.tags.length > 0) {\n\t\t\tawait this.db.rawQuery(\n\t\t\t\t`DELETE FROM ${this.tagsT} WHERE ${this.c.key} = ?`,\n\t\t\t\t[key],\n\t\t\t);\n\t\t\tfor (const tag of opts.tags) {\n\t\t\t\tawait this.db.rawQuery(\n\t\t\t\t\t`INSERT OR IGNORE INTO ${this.tagsT} (${this.c.tag}, ${this.c.key}) VALUES (?, ?)`,\n\t\t\t\t\t[tag, key],\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync delete(key: string): Promise<boolean> {\n\t\t// Cheap probe: does the row exist at all (even expired)?\n\t\tconst probe = await this.db.rawQuery<CacheRow>(\n\t\t\t`SELECT ${this.c.key} FROM ${this.t} WHERE ${this.c.key} = ? LIMIT 1`,\n\t\t\t[key],\n\t\t);\n\t\tif (probe.length === 0) return false;\n\t\tawait this.db.rawQuery(`DELETE FROM ${this.t} WHERE ${this.c.key} = ?`, [key]);\n\t\tawait this.db.rawQuery(\n\t\t\t`DELETE FROM ${this.tagsT} WHERE ${this.c.key} = ?`,\n\t\t\t[key],\n\t\t);\n\t\treturn true;\n\t}\n\n\tasync clear(pattern?: string): Promise<number> {\n\t\tif (!pattern) {\n\t\t\tawait this.db.rawQuery(`DELETE FROM ${this.tagsT}`);\n\t\t\tconst before = await this.db.rawQuery<unknown>(\n\t\t\t\t`SELECT COUNT(*) as n FROM ${this.t}`,\n\t\t\t);\n\t\t\tawait this.db.rawQuery(`DELETE FROM ${this.t}`);\n\t\t\tconst n = Number((before[0] as { n?: number } | undefined)?.n ?? 0);\n\t\t\treturn n;\n\t\t}\n\t\t// Translate glob to SQL LIKE. `*` -> `%`.\n\t\tconst likePattern = pattern\n\t\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t\t.replace(/\\*\\*/g, \"%\")\n\t\t\t.replace(/\\*/g, \"%\");\n\t\tconst before = await this.db.rawQuery<unknown>(\n\t\t\t`SELECT COUNT(*) as n FROM ${this.t} WHERE ${this.c.key} LIKE ? ESCAPE '\\\\'`,\n\t\t\t[likePattern],\n\t\t);\n\t\tawait this.db.rawQuery(\n\t\t\t`DELETE FROM ${this.t} WHERE ${this.c.key} LIKE ? ESCAPE '\\\\'`,\n\t\t\t[likePattern],\n\t\t);\n\t\treturn Number((before[0] as { n?: number } | undefined)?.n ?? 0);\n\t}\n\n\tasync wrap<T>(key: string, fn: () => Promise<T>, ttl?: number): Promise<T> {\n\t\tconst hit = await this.get<T>(key);\n\t\tif (hit !== undefined) return hit;\n\t\tconst v = await fn();\n\t\tawait this.set(key, v, { ttl });\n\t\treturn v;\n\t}\n\n\t/**\n\t * Remove every cache entry that has been tagged with `tag`.\n\t * Returns the number of keys removed.\n\t */\n\tasync invalidateByTag(tag: string): Promise<number> {\n\t\tconst tagRows = await this.db.rawQuery<TagRow>(\n\t\t\t`SELECT ${this.c.key} FROM ${this.tagsT} WHERE ${this.c.tag} = ?`,\n\t\t\t[tag],\n\t\t);\n\t\tconst keys = tagRows.map((r) => r[this.c.key]);\n\t\tif (keys.length === 0) return 0;\n\t\t// Use a parameter list to safely delete. We chunk in case there\n\t\t// are many keys (some DBs cap the parameter count).\n\t\tconst chunkSize = 100;\n\t\tlet removed = 0;\n\t\tfor (let i = 0; i < keys.length; i += chunkSize) {\n\t\t\tconst chunk = keys.slice(i, i + chunkSize);\n\t\t\tconst placeholders = chunk.map(() => \"?\").join(\", \");\n\t\t\tconst r1 = await this.db.rawQuery<unknown>(\n\t\t\t\t`DELETE FROM ${this.t} WHERE ${this.c.key} IN (${placeholders})`,\n\t\t\t\tchunk,\n\t\t\t);\n\t\t\tconst r2 = await this.db.rawQuery<unknown>(\n\t\t\t\t`DELETE FROM ${this.tagsT} WHERE ${this.c.key} IN (${placeholders})`,\n\t\t\t\tchunk,\n\t\t\t);\n\t\t\tremoved += Math.max(r1.length, r2.length, chunk.length);\n\t\t}\n\t\treturn removed;\n\t}\n\n\t/** Clean up expired entries. */\n\tasync gc(): Promise<number> {\n\t\tconst now = new Date().toISOString();\n\t\tconst before = await this.db.rawQuery<unknown>(\n\t\t\t`SELECT COUNT(*) as n FROM ${this.t} WHERE ${this.c.expiresAt} IS NOT NULL AND ${this.c.expiresAt} <= ?`,\n\t\t\t[now],\n\t\t);\n\t\tawait this.db.rawQuery(\n\t\t\t`DELETE FROM ${this.t} WHERE ${this.c.expiresAt} IS NOT NULL AND ${this.c.expiresAt} <= ?`,\n\t\t\t[now],\n\t\t);\n\t\t// Drop dangling tag rows.\n\t\tawait this.db.rawQuery(\n\t\t\t`DELETE FROM ${this.tagsT} WHERE ${this.c.key} NOT IN (SELECT ${this.c.key} FROM ${this.t})`,\n\t\t);\n\t\treturn Number((before[0] as { n?: number } | undefined)?.n ?? 0);\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// No resources to release — the underlying db is owned by the user.\n\t}\n}\n",
|
|
8
|
+
"/**\n * `RedisCacheStore` — a `CacheStore` backed by `nexusjs/redis`.\n *\n * Works on **Bun** (`Bun.redis`), **Node** (`ioredis`), and\n * **Cloudflare Workers KV** (via `CloudflareKVAdapter`). The\n * same adapter selection applies as for sessions.\n *\n * import { CacheService } from 'nexusjs/cache';\n * import { RedisCacheStore, createRedisClient } from 'nexusjs/redis';\n *\n * const cache = new CacheService({\n * store: new RedisCacheStore(createRedisClient({ url: 'redis://localhost:6379' }), {\n * keyPrefix: 'cache:',\n * }),\n * });\n *\n * Values are JSON-serialized. The store uses the KV backend's\n * own TTL (Redis `EX` / KV `expirationTtl`).\n *\n * Tag-based invalidation is supported on the Redis and Node\n * adapters (a per-tag Set is maintained as a separate KV key).\n * On Cloudflare KV, tag invalidation degrades to a SCAN — slower\n * but correct.\n */\n\nimport type { RedisClient } from \"@nexusts/redis/src/types.js\";\nimport type { CacheEntry, CacheSetOptions, CacheStore } from \"../types.js\";\n\nconst DEFAULT_KEY_PREFIX = \"cache:\";\n\nexport interface RedisCacheStoreOptions {\n\t/** Key prefix. Default: \"cache:\". */\n\tkeyPrefix?: string;\n}\n\nexport class RedisCacheStore implements CacheStore {\n\treadonly kind = \"redis\";\n\t#client: RedisClient;\n\t#keyPrefix: string;\n\t#tagKeyPrefix: string;\n\n\tconstructor(client: RedisClient, options: RedisCacheStoreOptions = {}) {\n\t\tthis.#client = client;\n\t\tthis.#keyPrefix = options.keyPrefix ?? DEFAULT_KEY_PREFIX;\n\t\tthis.#tagKeyPrefix = `${this.#keyPrefix}tag:`;\n\t}\n\n\t#key(key: string): string {\n\t\treturn `${this.#keyPrefix}${key}`;\n\t}\n\n\t#tagKey(tag: string): string {\n\t\treturn `${this.#tagKeyPrefix}${crc32(tag)}`;\n\t}\n\n\tasync get<T = unknown>(key: string): Promise<T | undefined> {\n\t\tconst raw = await this.#client.get(this.#key(key));\n\t\tif (raw === null) return undefined;\n\t\ttry {\n\t\t\tconst entry = JSON.parse(raw) as CacheEntry<T>;\n\t\t\tif (entry.expiresAt && entry.expiresAt <= Date.now()) {\n\t\t\t\tawait this.#client.del(this.#key(key));\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn entry.value;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tasync set<T = unknown>(\n\t\tkey: string,\n\t\tvalue: T,\n\t\topts?: CacheSetOptions,\n\t): Promise<void> {\n\t\tconst ttl = opts?.ttl ?? 0;\n\t\tconst tags = opts?.tags ?? [];\n\t\tconst entry: CacheEntry<T> = {\n\t\t\tvalue,\n\t\t\texpiresAt: ttl > 0 ? Date.now() + ttl * 1000 : 0,\n\t\t\t...(tags.length > 0 ? { tags } : {}),\n\t\t};\n\t\tconst fullKey = this.#key(key);\n\t\tconst ex = ttl > 0 ? ttl : undefined;\n\t\tawait this.#client.set(fullKey, JSON.stringify(entry), ex ? { ex } : undefined);\n\t\t// Update per-tag indexes.\n\t\tfor (const tag of tags) {\n\t\t\tawait this.#addToTagIndex(tag, key);\n\t\t}\n\t}\n\n\tasync delete(key: string): Promise<boolean> {\n\t\tconst fullKey = this.#key(key);\n\t\tconst existed = (await this.#client.get(fullKey)) !== null;\n\t\tawait this.#client.del(fullKey);\n\t\t// The tag indexes are best-effort — leave them; gc() will\n\t\t// eventually prune orphans. (A full implementation would\n\t\t// need to read the entry to find its tags before deleting,\n\t\t// which is an extra round-trip.)\n\t\treturn existed;\n\t}\n\n\tasync has(key: string): Promise<boolean> {\n\t\treturn this.#client.exists(this.#key(key));\n\t}\n\n\tasync clear(): Promise<number> {\n\t\tlet n = 0;\n\t\tlet cursor: string | number = \"0\";\n\t\tdo {\n\t\t\tconst res = await this.#client.scan({\n\t\t\t\tmatch: `${this.#keyPrefix}*`,\n\t\t\t\tcursor,\n\t\t\t\tcount: 100,\n\t\t\t});\n\t\t\tfor (const k of res.keys) {\n\t\t\t\tawait this.#client.del(this.#keyPrefix + k);\n\t\t\t\tn++;\n\t\t\t}\n\t\t\tcursor = res.cursor;\n\t\t} while (cursor !== \"0\" && cursor !== 0);\n\t\treturn n;\n\t}\n\n\tasync gc(): Promise<number> {\n\t\t// The KV store evicts on TTL. Here we clean up orphan tag\n\t\t// indexes — keys in the tag set that no longer exist in\n\t\t// the value store.\n\t\tlet removed = 0;\n\t\tlet cursor: string | number = \"0\";\n\t\tdo {\n\t\t\tconst res = await this.#client.scan({\n\t\t\t\tmatch: `${this.#tagKeyPrefix}*`,\n\t\t\t\tcursor,\n\t\t\t\tcount: 100,\n\t\t\t});\n\t\t\tfor (const k of res.keys) {\n\t\t\t\tconst raw = await this.#client.get(this.#keyPrefix + k);\n\t\t\t\tconst ids = raw ? safeParseStringArray(raw) : [];\n\t\t\t\tconst live: string[] = [];\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tif (await this.#client.exists(this.#key(id))) live.push(id);\n\t\t\t\t}\n\t\t\t\tif (live.length === 0) {\n\t\t\t\t\tawait this.#client.del(this.#keyPrefix + k);\n\t\t\t\t\tremoved++;\n\t\t\t\t} else if (live.length !== ids.length) {\n\t\t\t\t\tawait this.#client.set(\n\t\t\t\t\t\tthis.#keyPrefix + k,\n\t\t\t\t\t\tJSON.stringify(live),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor = res.cursor;\n\t\t} while (cursor !== \"0\" && cursor !== 0);\n\t\treturn removed;\n\t}\n\n\tasync invalidateByTag(tag: string): Promise<number> {\n\t\tconst tagK = this.#tagKey(tag);\n\t\tconst raw = await this.#client.get(tagK);\n\t\tif (!raw) return 0;\n\t\tconst keys = safeParseStringArray(raw);\n\t\tlet n = 0;\n\t\tfor (const k of keys) {\n\t\t\tawait this.#client.del(this.#key(k));\n\t\t\tn++;\n\t\t}\n\t\tawait this.#client.del(tagK);\n\t\treturn n;\n\t}\n\n\tasync invalidateByTags(tags: string[]): Promise<number> {\n\t\tlet n = 0;\n\t\tfor (const t of tags) n += await this.invalidateByTag(t);\n\t\treturn n;\n\t}\n\n\tasync wrap<T>(key: string, fn: () => Promise<T>, ttl?: number): Promise<T> {\n\t\tconst hit = await this.get<T>(key);\n\t\tif (hit !== undefined) return hit;\n\t\tconst value = await fn();\n\t\tawait this.set(key, value, ttl !== undefined ? { ttl } : undefined);\n\t\treturn value;\n\t}\n\n\tasync close(): Promise<void> {\n\t\tawait this.#client.close();\n\t}\n\n\tasync #addToTagIndex(tag: string, key: string): Promise<void> {\n\t\tconst k = this.#tagKey(tag);\n\t\tconst raw = await this.#client.get(k);\n\t\tconst ids = raw ? safeParseStringArray(raw) : [];\n\t\tif (!ids.includes(key)) ids.push(key);\n\t\tawait this.#client.set(k, JSON.stringify(ids));\n\t}\n}\n\nfunction safeParseStringArray(raw: string): string[] {\n\ttry {\n\t\tconst v = JSON.parse(raw);\n\t\treturn Array.isArray(v) ? v.filter((x) => typeof x === \"string\") : [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction crc32(s: string): string {\n\tlet c = 0xffffffff;\n\tfor (let i = 0; i < s.length; i++) {\n\t\tc ^= s.charCodeAt(i);\n\t\tfor (let k = 0; k < 8; k++) {\n\t\t\tc = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n\t\t}\n\t}\n\treturn (c ^ 0xffffffff).toString(16);\n}\n",
|
|
9
|
+
"/**\n * `CacheService` — main entry point for caching.\n *\n * const cache = new CacheService({ store: new MemoryStore() });\n * await cache.set('user:42', user, { ttl: 60 });\n * const u = await cache.get<User>('user:42');\n *\n * Provides:\n * - `get`, `set`, `delete`, `clear` — direct key operations\n * - `wrap` — cache-or-compute\n * - `getOrSet` — alias for wrap with a default TTL\n * - `invalidateByTag`, `invalidateByPrefix` — bulk removal\n * - `applyDecorators(target)` — wires @Cacheable / @CacheInvalidate\n * onto an existing service instance.\n */\nimport { Inject, Injectable } from \"@nexusts/core/decorators/index.js\";\nimport { MemoryStore } from \"./stores/memory.js\";\nimport type { CacheConfig, CacheStore } from \"./types.js\";\nimport {\n\tgetCacheableSpecs,\n\tgetCacheInvalidateSpecs,\n} from \"./types.js\";\n\n@Injectable()\nexport class CacheService {\n\t/** DI token. */\n\tstatic readonly TOKEN = Symbol.for(\"nexus:CacheService\");\n\n\tstore: CacheStore;\n\tdefaultTtl: number;\n\tprefix: string;\n\n\tconstructor(@Inject(\"CACHE_CONFIG\") config: CacheConfig = {}) {\n\t\tthis.store = config.store ?? new MemoryStore();\n\t\tthis.defaultTtl = config.defaultTtl ?? 60;\n\t\tthis.prefix = config.prefix ?? \"nexusjs\";\n\t}\n\n\tprivate key(k: string): string {\n\t\treturn `${this.prefix}:${k}`;\n\t}\n\n\tasync get<T = unknown>(k: string): Promise<T | undefined> {\n\t\treturn this.store.get<T>(this.key(k));\n\t}\n\n\tasync set<T = unknown>(k: string, value: T, ttl?: number): Promise<void>;\n\tasync set<T = unknown>(k: string, value: T, opts: { ttl?: number; tags?: string[] }): Promise<void>;\n\tasync set<T = unknown>(\n\t\tk: string,\n\t\tvalue: T,\n\t\toptsOrTtl: number | { ttl?: number; tags?: string[] } = this.defaultTtl,\n\t): Promise<void> {\n\t\tconst opts =\n\t\t\ttypeof optsOrTtl === \"number\"\n\t\t\t\t? { ttl: optsOrTtl }\n\t\t\t\t: { ttl: optsOrTtl.ttl ?? this.defaultTtl, tags: optsOrTtl.tags };\n\t\tawait this.store.set<T>(this.key(k), value, opts);\n\t}\n\n\tasync delete(k: string): Promise<boolean> {\n\t\treturn this.store.delete(this.key(k));\n\t}\n\n\tasync clear(pattern?: string): Promise<number> {\n\t\treturn this.store.clear(pattern ? `${this.prefix}:${pattern}` : undefined);\n\t}\n\n\t/** Get or compute-and-store. */\n\tasync wrap<T>(k: string, fn: () => Promise<T>, ttl?: number): Promise<T> {\n\t\treturn this.store.wrap<T>(this.key(k), fn, ttl ?? this.defaultTtl);\n\t}\n\n\t/**\n\t * Tag-based invalidation. Delegates to the underlying store.\n\t * Stores without a tag index (the default `MemoryStore`) return 0.\n\t * Use `DrizzleCacheStore` (or implement `invalidateByTag` on a\n\t * custom store) for true tag-based removal.\n\t */\n\tasync invalidateByTag(tag: string): Promise<number> {\n\t\tif (typeof (this.store as any).invalidateByTag === \"function\") {\n\t\t\treturn await (this.store as any).invalidateByTag(this.prefixedTag(tag));\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/** Sweep expired entries. No-op on stores that don't implement `gc()`. */\n\tasync gc(): Promise<number> {\n\t\tif (typeof this.store.gc === \"function\") {\n\t\t\treturn await this.store.gc();\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/** Apply the configured prefix to a tag name. */\n\tprivate prefixedTag(tag: string): string {\n\t\treturn `${this.prefix}:${tag}`;\n\t}\n\n\t/**\n\t * Apply @Cacheable / @CacheInvalidate decorators to an existing service\n\t * instance. The framework's DI container does this automatically.\n\t */\n\tapplyDecorators(target: any): void {\n\t\tconst ctor = target.constructor;\n\t\tconst cacheables = getCacheableSpecs(ctor);\n\t\tfor (const spec of cacheables) {\n\t\t\tconst original = spec.original;\n\t\t\t(target as any)[spec.propertyKey] = async (...args: any[]) => {\n\t\t\t\tconst subKey = spec.keyFn(...args);\n\t\t\t\treturn this.wrap(`${spec.prefix}:${subKey}`, () =>\n\t\t\t\t\toriginal.apply(target, args),\n\t\t\t\t);\n\t\t\t};\n\t\t}\n\t\tconst invalidates = getCacheInvalidateSpecs(ctor);\n\t\tfor (const spec of invalidates) {\n\t\t\tconst original = spec.original;\n\t\t\t(target as any)[spec.propertyKey] = async (...args: any[]) => {\n\t\t\t\tconst result = await original.apply(target, args);\n\t\t\t\tconst subKey = spec.keyFn(...args);\n\t\t\t\tawait this.clear(`${spec.prefix}:${subKey}*`);\n\t\t\t\treturn result;\n\t\t\t};\n\t\t}\n\t}\n}\n",
|
|
10
|
+
"/**\n * `CacheModule` — drop-in caching.\n *\n * @Module({\n * imports: [\n * CacheModule.forRoot({\n * store: new MemoryStore({ max: 50_000 }),\n * defaultTtl: 300, // 5 min\n * prefix: 'myapp',\n * }),\n * ],\n * })\n * export class AppModule {}\n */\nimport \"reflect-metadata\";\nimport { Module } from \"@nexusts/core/decorators/module.js\";\nimport { CacheService } from \"./cache.service.js\";\nimport { MemoryStore } from \"./stores/memory.js\";\nimport type { CacheConfig } from \"./types.js\";\n\n@Module({\n\tproviders: [\n\t\tCacheService,\n\t\t{ provide: CacheService.TOKEN, useExisting: CacheService },\n\t],\n\texports: [CacheService, CacheService.TOKEN],\n})\nexport class CacheModule {\n\tstatic forRoot(config: CacheConfig = {}) {\n\t\tconst cfg: CacheConfig = {\n\t\t\tstore: new MemoryStore(),\n\t\t\t...config,\n\t\t};\n\t\t@Module({\n\t\t\tproviders: [\n\t\t\t\tCacheService,\n\t\t\t\t{ provide: CacheService.TOKEN, useExisting: CacheService },\n\t\t\t\t{ provide: \"CACHE_CONFIG\", useValue: cfg },\n\t\t\t],\n\t\t\texports: [CacheService, CacheService.TOKEN],\n\t\t})\n\t\tclass ConfiguredCacheModule {}\n\t\tObject.defineProperty(ConfiguredCacheModule, \"name\", {\n\t\t\tvalue: \"ConfiguredCacheModule\",\n\t\t});\n\t\treturn ConfiguredCacheModule;\n\t}\n}\n"
|
|
11
|
+
],
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "6BDBD5106C96A0F964756E2164756E21",
|
|
14
|
+
"names": []
|
|
15
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nexusts/cache",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Application cache (memory / Drizzle backends)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "bun run ../../build.ts"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"nexusts",
|
|
24
|
+
"framework",
|
|
25
|
+
"bun"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@nexusts/core": "^0.7.0"
|
|
30
|
+
}
|
|
31
|
+
}
|