@guren/server 0.2.0-alpha.7 → 1.0.0-rc.9
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/dist/Application-DtWDHXr1.d.ts +2110 -0
- package/dist/BroadcastManager-AkIWUGJo.d.ts +466 -0
- package/dist/CacheManager-BkvHEOZX.d.ts +244 -0
- package/dist/ConsoleKernel-CqCVrdZs.d.ts +207 -0
- package/dist/EventManager-CmIoLt7r.d.ts +207 -0
- package/dist/Gate-CNkBYf8m.d.ts +268 -0
- package/dist/HealthManager-DUyMIzsZ.d.ts +141 -0
- package/dist/I18nManager-Dtgzsf5n.d.ts +270 -0
- package/dist/LogManager-7mxnkaPM.d.ts +256 -0
- package/dist/MailManager-DpMvYiP9.d.ts +292 -0
- package/dist/Scheduler-BstvSca7.d.ts +469 -0
- package/dist/StorageManager-oZTHqaza.d.ts +337 -0
- package/dist/api-token-JOif2CtG.d.ts +1792 -0
- package/dist/app-key-CsBfRC_Q.d.ts +214 -0
- package/dist/auth/index.d.ts +418 -0
- package/dist/auth/index.js +6742 -0
- package/dist/authorization/index.d.ts +129 -0
- package/dist/authorization/index.js +621 -0
- package/dist/broadcasting/index.d.ts +233 -0
- package/dist/broadcasting/index.js +907 -0
- package/dist/cache/index.d.ts +233 -0
- package/dist/cache/index.js +817 -0
- package/dist/encryption/index.d.ts +222 -0
- package/dist/encryption/index.js +602 -0
- package/dist/events/index.d.ts +155 -0
- package/dist/events/index.js +330 -0
- package/dist/health/index.d.ts +185 -0
- package/dist/health/index.js +379 -0
- package/dist/i18n/index.d.ts +101 -0
- package/dist/i18n/index.js +597 -0
- package/dist/index-9_Jzj5jo.d.ts +7 -0
- package/dist/index.d.ts +2628 -619
- package/dist/index.js +22229 -3116
- package/dist/lambda/index.d.ts +156 -0
- package/dist/lambda/index.js +91 -0
- package/dist/logging/index.d.ts +50 -0
- package/dist/logging/index.js +557 -0
- package/dist/mail/index.d.ts +288 -0
- package/dist/mail/index.js +695 -0
- package/dist/mcp/index.d.ts +139 -0
- package/dist/mcp/index.js +382 -0
- package/dist/notifications/index.d.ts +271 -0
- package/dist/notifications/index.js +741 -0
- package/dist/queue/index.d.ts +423 -0
- package/dist/queue/index.js +958 -0
- package/dist/runtime/index.d.ts +93 -0
- package/dist/runtime/index.js +834 -0
- package/dist/scheduling/index.d.ts +41 -0
- package/dist/scheduling/index.js +836 -0
- package/dist/storage/index.d.ts +196 -0
- package/dist/storage/index.js +832 -0
- package/dist/vite/index.js +203 -3
- package/package.json +93 -6
- package/dist/chunk-FK2XQSBF.js +0 -160
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
// src/cache/stores/MemoryStore.ts
|
|
2
|
+
var MemoryStore = class {
|
|
3
|
+
cache = /* @__PURE__ */ new Map();
|
|
4
|
+
maxSize;
|
|
5
|
+
checkInterval = null;
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
this.maxSize = options.maxSize ?? Infinity;
|
|
8
|
+
const checkPeriod = options.checkPeriod ?? 6e4;
|
|
9
|
+
if (checkPeriod > 0) {
|
|
10
|
+
this.checkInterval = setInterval(() => {
|
|
11
|
+
this.removeExpired();
|
|
12
|
+
}, checkPeriod);
|
|
13
|
+
if (this.checkInterval.unref) {
|
|
14
|
+
this.checkInterval.unref();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Remove expired items from the cache.
|
|
20
|
+
*/
|
|
21
|
+
removeExpired() {
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
for (const [key, item] of this.cache) {
|
|
24
|
+
if (item.expiresAt !== null && item.expiresAt <= now) {
|
|
25
|
+
this.cache.delete(key);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if an item is expired.
|
|
31
|
+
*/
|
|
32
|
+
isExpired(item) {
|
|
33
|
+
return item.expiresAt !== null && item.expiresAt <= Date.now();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Evict items if cache is full.
|
|
37
|
+
*/
|
|
38
|
+
evictIfNeeded() {
|
|
39
|
+
if (this.cache.size >= this.maxSize) {
|
|
40
|
+
const firstKey = this.cache.keys().next().value;
|
|
41
|
+
if (firstKey !== void 0) {
|
|
42
|
+
this.cache.delete(firstKey);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async get(key) {
|
|
47
|
+
const item = this.cache.get(key);
|
|
48
|
+
if (!item) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (this.isExpired(item)) {
|
|
52
|
+
this.cache.delete(key);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return item.value;
|
|
56
|
+
}
|
|
57
|
+
async set(key, value, ttl) {
|
|
58
|
+
this.evictIfNeeded();
|
|
59
|
+
const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
|
|
60
|
+
this.cache.set(key, {
|
|
61
|
+
value,
|
|
62
|
+
expiresAt
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async has(key) {
|
|
66
|
+
const item = this.cache.get(key);
|
|
67
|
+
if (!item) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
if (this.isExpired(item)) {
|
|
71
|
+
this.cache.delete(key);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
async delete(key) {
|
|
77
|
+
return this.cache.delete(key);
|
|
78
|
+
}
|
|
79
|
+
async clear() {
|
|
80
|
+
this.cache.clear();
|
|
81
|
+
}
|
|
82
|
+
async increment(key, value = 1) {
|
|
83
|
+
const current = await this.get(key);
|
|
84
|
+
const newValue = (current ?? 0) + value;
|
|
85
|
+
const item = this.cache.get(key);
|
|
86
|
+
const ttl = item?.expiresAt ? Math.max(0, Math.ceil((item.expiresAt - Date.now()) / 1e3)) : void 0;
|
|
87
|
+
await this.set(key, newValue, ttl);
|
|
88
|
+
return newValue;
|
|
89
|
+
}
|
|
90
|
+
async decrement(key, value = 1) {
|
|
91
|
+
return this.increment(key, -value);
|
|
92
|
+
}
|
|
93
|
+
async remember(key, ttl, callback) {
|
|
94
|
+
const cached = await this.get(key);
|
|
95
|
+
if (cached !== null) {
|
|
96
|
+
return cached;
|
|
97
|
+
}
|
|
98
|
+
const value = await callback();
|
|
99
|
+
await this.set(key, value, ttl);
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
async rememberForever(key, callback) {
|
|
103
|
+
const cached = await this.get(key);
|
|
104
|
+
if (cached !== null) {
|
|
105
|
+
return cached;
|
|
106
|
+
}
|
|
107
|
+
const value = await callback();
|
|
108
|
+
await this.set(key, value);
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
async getMany(keys) {
|
|
112
|
+
const result = /* @__PURE__ */ new Map();
|
|
113
|
+
for (const key of keys) {
|
|
114
|
+
result.set(key, await this.get(key));
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
async setMany(items, ttl) {
|
|
119
|
+
for (const [key, value] of items) {
|
|
120
|
+
await this.set(key, value, ttl);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async deleteMany(keys) {
|
|
124
|
+
let deleted = 0;
|
|
125
|
+
for (const key of keys) {
|
|
126
|
+
if (await this.delete(key)) {
|
|
127
|
+
deleted++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return deleted;
|
|
131
|
+
}
|
|
132
|
+
async ttl(key) {
|
|
133
|
+
const item = this.cache.get(key);
|
|
134
|
+
if (!item) {
|
|
135
|
+
return -2;
|
|
136
|
+
}
|
|
137
|
+
if (this.isExpired(item)) {
|
|
138
|
+
this.cache.delete(key);
|
|
139
|
+
return -2;
|
|
140
|
+
}
|
|
141
|
+
if (item.expiresAt === null) {
|
|
142
|
+
return -1;
|
|
143
|
+
}
|
|
144
|
+
return Math.max(0, Math.ceil((item.expiresAt - Date.now()) / 1e3));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get the number of items in the cache.
|
|
148
|
+
*/
|
|
149
|
+
size() {
|
|
150
|
+
return this.cache.size;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Stop the expiration check interval.
|
|
154
|
+
*/
|
|
155
|
+
destroy() {
|
|
156
|
+
if (this.checkInterval) {
|
|
157
|
+
clearInterval(this.checkInterval);
|
|
158
|
+
this.checkInterval = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/cache/stores/RedisStore.ts
|
|
164
|
+
var RedisStore = class {
|
|
165
|
+
client;
|
|
166
|
+
prefix;
|
|
167
|
+
constructor(options) {
|
|
168
|
+
this.client = options.client;
|
|
169
|
+
this.prefix = options.prefix ?? "cache:";
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get the prefixed key.
|
|
173
|
+
*/
|
|
174
|
+
prefixKey(key) {
|
|
175
|
+
return `${this.prefix}${key}`;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Serialize a value for storage.
|
|
179
|
+
*/
|
|
180
|
+
serialize(value) {
|
|
181
|
+
return JSON.stringify(value);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Deserialize a stored value.
|
|
185
|
+
*/
|
|
186
|
+
deserialize(value) {
|
|
187
|
+
if (value === null) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
return JSON.parse(value);
|
|
192
|
+
} catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async get(key) {
|
|
197
|
+
const value = await this.client.get(this.prefixKey(key));
|
|
198
|
+
return this.deserialize(value);
|
|
199
|
+
}
|
|
200
|
+
async set(key, value, ttl) {
|
|
201
|
+
const serialized = this.serialize(value);
|
|
202
|
+
const prefixedKey = this.prefixKey(key);
|
|
203
|
+
if (ttl) {
|
|
204
|
+
await this.client.setex(prefixedKey, ttl, serialized);
|
|
205
|
+
} else {
|
|
206
|
+
await this.client.set(prefixedKey, serialized);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async has(key) {
|
|
210
|
+
const exists = await this.client.exists(this.prefixKey(key));
|
|
211
|
+
return exists > 0;
|
|
212
|
+
}
|
|
213
|
+
async delete(key) {
|
|
214
|
+
const deleted = await this.client.del(this.prefixKey(key));
|
|
215
|
+
return deleted > 0;
|
|
216
|
+
}
|
|
217
|
+
async clear() {
|
|
218
|
+
const keys = await this.client.keys(`${this.prefix}*`);
|
|
219
|
+
if (keys.length > 0) {
|
|
220
|
+
await this.client.del(...keys);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async increment(key, value = 1) {
|
|
224
|
+
const prefixedKey = this.prefixKey(key);
|
|
225
|
+
const exists = await this.client.exists(prefixedKey);
|
|
226
|
+
if (!exists) {
|
|
227
|
+
await this.client.set(prefixedKey, "0");
|
|
228
|
+
}
|
|
229
|
+
return this.client.incrby(prefixedKey, value);
|
|
230
|
+
}
|
|
231
|
+
async decrement(key, value = 1) {
|
|
232
|
+
const prefixedKey = this.prefixKey(key);
|
|
233
|
+
const exists = await this.client.exists(prefixedKey);
|
|
234
|
+
if (!exists) {
|
|
235
|
+
await this.client.set(prefixedKey, "0");
|
|
236
|
+
}
|
|
237
|
+
return this.client.decrby(prefixedKey, value);
|
|
238
|
+
}
|
|
239
|
+
async remember(key, ttl, callback) {
|
|
240
|
+
const cached = await this.get(key);
|
|
241
|
+
if (cached !== null) {
|
|
242
|
+
return cached;
|
|
243
|
+
}
|
|
244
|
+
const value = await callback();
|
|
245
|
+
await this.set(key, value, ttl);
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
async rememberForever(key, callback) {
|
|
249
|
+
const cached = await this.get(key);
|
|
250
|
+
if (cached !== null) {
|
|
251
|
+
return cached;
|
|
252
|
+
}
|
|
253
|
+
const value = await callback();
|
|
254
|
+
await this.set(key, value);
|
|
255
|
+
return value;
|
|
256
|
+
}
|
|
257
|
+
async getMany(keys) {
|
|
258
|
+
const prefixedKeys = keys.map((key) => this.prefixKey(key));
|
|
259
|
+
const values = await this.client.mget(...prefixedKeys);
|
|
260
|
+
const result = /* @__PURE__ */ new Map();
|
|
261
|
+
for (let i = 0; i < keys.length; i++) {
|
|
262
|
+
result.set(keys[i], this.deserialize(values[i]));
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
async setMany(items, ttl) {
|
|
267
|
+
const pipeline = this.client.pipeline();
|
|
268
|
+
for (const [key, value] of items) {
|
|
269
|
+
const serialized = this.serialize(value);
|
|
270
|
+
const prefixedKey = this.prefixKey(key);
|
|
271
|
+
if (ttl) {
|
|
272
|
+
pipeline.setex(prefixedKey, ttl, serialized);
|
|
273
|
+
} else {
|
|
274
|
+
pipeline.set(prefixedKey, serialized);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
await pipeline.exec();
|
|
278
|
+
}
|
|
279
|
+
async deleteMany(keys) {
|
|
280
|
+
if (keys.length === 0) {
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
const prefixedKeys = keys.map((key) => this.prefixKey(key));
|
|
284
|
+
return this.client.del(...prefixedKeys);
|
|
285
|
+
}
|
|
286
|
+
async ttl(key) {
|
|
287
|
+
return this.client.ttl(this.prefixKey(key));
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Get the Redis client instance.
|
|
291
|
+
*/
|
|
292
|
+
getClient() {
|
|
293
|
+
return this.client;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Get the key prefix.
|
|
297
|
+
*/
|
|
298
|
+
getPrefix() {
|
|
299
|
+
return this.prefix;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// src/cache/stores/FileStore.ts
|
|
304
|
+
import { readFile, writeFile, unlink, readdir, mkdir, rm } from "fs/promises";
|
|
305
|
+
import { existsSync } from "fs";
|
|
306
|
+
import { join, dirname } from "path";
|
|
307
|
+
import { createHash } from "crypto";
|
|
308
|
+
var FileStore = class {
|
|
309
|
+
basePath;
|
|
310
|
+
extension;
|
|
311
|
+
constructor(options) {
|
|
312
|
+
this.basePath = options.path;
|
|
313
|
+
this.extension = options.extension ?? ".cache";
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Generate a hashed filename for a cache key.
|
|
317
|
+
*/
|
|
318
|
+
getFilePath(key) {
|
|
319
|
+
const hash = createHash("sha256").update(key).digest("hex");
|
|
320
|
+
const dir = hash.slice(0, 2);
|
|
321
|
+
return join(this.basePath, dir, `${hash}${this.extension}`);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Ensure the cache directory exists.
|
|
325
|
+
*/
|
|
326
|
+
async ensureDirectory(filePath) {
|
|
327
|
+
const dir = dirname(filePath);
|
|
328
|
+
if (!existsSync(dir)) {
|
|
329
|
+
await mkdir(dir, { recursive: true });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Read a cache file.
|
|
334
|
+
*/
|
|
335
|
+
async readCacheFile(filePath) {
|
|
336
|
+
try {
|
|
337
|
+
const content = await readFile(filePath, "utf-8");
|
|
338
|
+
return JSON.parse(content);
|
|
339
|
+
} catch {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Write a cache file.
|
|
345
|
+
*/
|
|
346
|
+
async writeCacheFile(filePath, item) {
|
|
347
|
+
await this.ensureDirectory(filePath);
|
|
348
|
+
await writeFile(filePath, JSON.stringify(item), "utf-8");
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Delete a cache file.
|
|
352
|
+
*/
|
|
353
|
+
async deleteCacheFile(filePath) {
|
|
354
|
+
try {
|
|
355
|
+
await unlink(filePath);
|
|
356
|
+
return true;
|
|
357
|
+
} catch {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Check if a cached item is expired.
|
|
363
|
+
*/
|
|
364
|
+
isExpired(item) {
|
|
365
|
+
return item.expiresAt !== null && item.expiresAt <= Date.now();
|
|
366
|
+
}
|
|
367
|
+
async get(key) {
|
|
368
|
+
const filePath = this.getFilePath(key);
|
|
369
|
+
const item = await this.readCacheFile(filePath);
|
|
370
|
+
if (!item) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
if (this.isExpired(item)) {
|
|
374
|
+
await this.deleteCacheFile(filePath);
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
return item.value;
|
|
378
|
+
}
|
|
379
|
+
async set(key, value, ttl) {
|
|
380
|
+
const filePath = this.getFilePath(key);
|
|
381
|
+
const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
|
|
382
|
+
await this.writeCacheFile(filePath, {
|
|
383
|
+
value,
|
|
384
|
+
expiresAt
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
async has(key) {
|
|
388
|
+
const value = await this.get(key);
|
|
389
|
+
return value !== null;
|
|
390
|
+
}
|
|
391
|
+
async delete(key) {
|
|
392
|
+
const filePath = this.getFilePath(key);
|
|
393
|
+
return this.deleteCacheFile(filePath);
|
|
394
|
+
}
|
|
395
|
+
async clear() {
|
|
396
|
+
if (!existsSync(this.basePath)) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
await rm(this.basePath, { recursive: true, force: true });
|
|
400
|
+
await mkdir(this.basePath, { recursive: true });
|
|
401
|
+
}
|
|
402
|
+
async increment(key, value = 1) {
|
|
403
|
+
const current = await this.get(key);
|
|
404
|
+
const newValue = (current ?? 0) + value;
|
|
405
|
+
const filePath = this.getFilePath(key);
|
|
406
|
+
const item = await this.readCacheFile(filePath);
|
|
407
|
+
const ttl = item?.expiresAt ? Math.max(0, Math.ceil((item.expiresAt - Date.now()) / 1e3)) : void 0;
|
|
408
|
+
await this.set(key, newValue, ttl);
|
|
409
|
+
return newValue;
|
|
410
|
+
}
|
|
411
|
+
async decrement(key, value = 1) {
|
|
412
|
+
return this.increment(key, -value);
|
|
413
|
+
}
|
|
414
|
+
async remember(key, ttl, callback) {
|
|
415
|
+
const cached = await this.get(key);
|
|
416
|
+
if (cached !== null) {
|
|
417
|
+
return cached;
|
|
418
|
+
}
|
|
419
|
+
const value = await callback();
|
|
420
|
+
await this.set(key, value, ttl);
|
|
421
|
+
return value;
|
|
422
|
+
}
|
|
423
|
+
async rememberForever(key, callback) {
|
|
424
|
+
const cached = await this.get(key);
|
|
425
|
+
if (cached !== null) {
|
|
426
|
+
return cached;
|
|
427
|
+
}
|
|
428
|
+
const value = await callback();
|
|
429
|
+
await this.set(key, value);
|
|
430
|
+
return value;
|
|
431
|
+
}
|
|
432
|
+
async getMany(keys) {
|
|
433
|
+
const result = /* @__PURE__ */ new Map();
|
|
434
|
+
for (const key of keys) {
|
|
435
|
+
result.set(key, await this.get(key));
|
|
436
|
+
}
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
async setMany(items, ttl) {
|
|
440
|
+
const promises = [];
|
|
441
|
+
for (const [key, value] of items) {
|
|
442
|
+
promises.push(this.set(key, value, ttl));
|
|
443
|
+
}
|
|
444
|
+
await Promise.all(promises);
|
|
445
|
+
}
|
|
446
|
+
async deleteMany(keys) {
|
|
447
|
+
let deleted = 0;
|
|
448
|
+
for (const key of keys) {
|
|
449
|
+
if (await this.delete(key)) {
|
|
450
|
+
deleted++;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return deleted;
|
|
454
|
+
}
|
|
455
|
+
async ttl(key) {
|
|
456
|
+
const filePath = this.getFilePath(key);
|
|
457
|
+
const item = await this.readCacheFile(filePath);
|
|
458
|
+
if (!item) {
|
|
459
|
+
return -2;
|
|
460
|
+
}
|
|
461
|
+
if (this.isExpired(item)) {
|
|
462
|
+
await this.deleteCacheFile(filePath);
|
|
463
|
+
return -2;
|
|
464
|
+
}
|
|
465
|
+
if (item.expiresAt === null) {
|
|
466
|
+
return -1;
|
|
467
|
+
}
|
|
468
|
+
return Math.max(0, Math.ceil((item.expiresAt - Date.now()) / 1e3));
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Clean up expired cache files.
|
|
472
|
+
* This can be called periodically to free disk space.
|
|
473
|
+
*/
|
|
474
|
+
async cleanup() {
|
|
475
|
+
let cleaned = 0;
|
|
476
|
+
if (!existsSync(this.basePath)) {
|
|
477
|
+
return cleaned;
|
|
478
|
+
}
|
|
479
|
+
const subdirs = await readdir(this.basePath);
|
|
480
|
+
for (const subdir of subdirs) {
|
|
481
|
+
const subdirPath = join(this.basePath, subdir);
|
|
482
|
+
let files;
|
|
483
|
+
try {
|
|
484
|
+
files = await readdir(subdirPath);
|
|
485
|
+
} catch {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
for (const file of files) {
|
|
489
|
+
if (!file.endsWith(this.extension)) {
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
const filePath = join(subdirPath, file);
|
|
493
|
+
const item = await this.readCacheFile(filePath);
|
|
494
|
+
if (item && this.isExpired(item)) {
|
|
495
|
+
await this.deleteCacheFile(filePath);
|
|
496
|
+
cleaned++;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return cleaned;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Get the base path.
|
|
504
|
+
*/
|
|
505
|
+
getBasePath() {
|
|
506
|
+
return this.basePath;
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// src/cache/TaggedCache.ts
|
|
511
|
+
var TaggedCache = class {
|
|
512
|
+
store;
|
|
513
|
+
tags;
|
|
514
|
+
tagSetPrefix;
|
|
515
|
+
constructor(store, tags, tagSetPrefix = "tag:") {
|
|
516
|
+
this.store = store;
|
|
517
|
+
this.tags = tags;
|
|
518
|
+
this.tagSetPrefix = tagSetPrefix;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Get a unique namespace for the current tags.
|
|
522
|
+
*/
|
|
523
|
+
async getTagNamespace() {
|
|
524
|
+
const namespaces = [];
|
|
525
|
+
for (const tag of this.tags) {
|
|
526
|
+
const tagKey = `${this.tagSetPrefix}${tag}`;
|
|
527
|
+
let namespace = await this.store.get(tagKey);
|
|
528
|
+
if (!namespace) {
|
|
529
|
+
namespace = this.generateNamespace();
|
|
530
|
+
await this.store.set(tagKey, namespace);
|
|
531
|
+
}
|
|
532
|
+
namespaces.push(namespace);
|
|
533
|
+
}
|
|
534
|
+
return namespaces.join(":");
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Generate a unique namespace identifier.
|
|
538
|
+
*/
|
|
539
|
+
generateNamespace() {
|
|
540
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Get the tagged key.
|
|
544
|
+
*/
|
|
545
|
+
async taggedKey(key) {
|
|
546
|
+
const namespace = await this.getTagNamespace();
|
|
547
|
+
return `tagged:${namespace}:${key}`;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Track a key in the tag sets.
|
|
551
|
+
*/
|
|
552
|
+
async trackKey(key) {
|
|
553
|
+
const taggedKey = await this.taggedKey(key);
|
|
554
|
+
for (const tag of this.tags) {
|
|
555
|
+
const setKey = `${this.tagSetPrefix}${tag}:keys`;
|
|
556
|
+
const keys = await this.store.get(setKey) ?? [];
|
|
557
|
+
if (!keys.includes(taggedKey)) {
|
|
558
|
+
keys.push(taggedKey);
|
|
559
|
+
await this.store.set(setKey, keys);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
async get(key) {
|
|
564
|
+
const taggedKey = await this.taggedKey(key);
|
|
565
|
+
return this.store.get(taggedKey);
|
|
566
|
+
}
|
|
567
|
+
async set(key, value, ttl) {
|
|
568
|
+
const taggedKey = await this.taggedKey(key);
|
|
569
|
+
await this.store.set(taggedKey, value, ttl);
|
|
570
|
+
await this.trackKey(key);
|
|
571
|
+
}
|
|
572
|
+
async has(key) {
|
|
573
|
+
const taggedKey = await this.taggedKey(key);
|
|
574
|
+
return this.store.has(taggedKey);
|
|
575
|
+
}
|
|
576
|
+
async delete(key) {
|
|
577
|
+
const taggedKey = await this.taggedKey(key);
|
|
578
|
+
return this.store.delete(taggedKey);
|
|
579
|
+
}
|
|
580
|
+
async clear() {
|
|
581
|
+
await this.flush();
|
|
582
|
+
}
|
|
583
|
+
async increment(key, value = 1) {
|
|
584
|
+
const taggedKey = await this.taggedKey(key);
|
|
585
|
+
await this.trackKey(key);
|
|
586
|
+
return this.store.increment(taggedKey, value);
|
|
587
|
+
}
|
|
588
|
+
async decrement(key, value = 1) {
|
|
589
|
+
const taggedKey = await this.taggedKey(key);
|
|
590
|
+
await this.trackKey(key);
|
|
591
|
+
return this.store.decrement(taggedKey, value);
|
|
592
|
+
}
|
|
593
|
+
async remember(key, ttl, callback) {
|
|
594
|
+
const cached = await this.get(key);
|
|
595
|
+
if (cached !== null) {
|
|
596
|
+
return cached;
|
|
597
|
+
}
|
|
598
|
+
const value = await callback();
|
|
599
|
+
await this.set(key, value, ttl);
|
|
600
|
+
return value;
|
|
601
|
+
}
|
|
602
|
+
async rememberForever(key, callback) {
|
|
603
|
+
const cached = await this.get(key);
|
|
604
|
+
if (cached !== null) {
|
|
605
|
+
return cached;
|
|
606
|
+
}
|
|
607
|
+
const value = await callback();
|
|
608
|
+
await this.set(key, value);
|
|
609
|
+
return value;
|
|
610
|
+
}
|
|
611
|
+
async getMany(keys) {
|
|
612
|
+
const result = /* @__PURE__ */ new Map();
|
|
613
|
+
for (const key of keys) {
|
|
614
|
+
result.set(key, await this.get(key));
|
|
615
|
+
}
|
|
616
|
+
return result;
|
|
617
|
+
}
|
|
618
|
+
async setMany(items, ttl) {
|
|
619
|
+
for (const [key, value] of items) {
|
|
620
|
+
await this.set(key, value, ttl);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
async deleteMany(keys) {
|
|
624
|
+
let deleted = 0;
|
|
625
|
+
for (const key of keys) {
|
|
626
|
+
if (await this.delete(key)) {
|
|
627
|
+
deleted++;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return deleted;
|
|
631
|
+
}
|
|
632
|
+
async ttl(key) {
|
|
633
|
+
const taggedKey = await this.taggedKey(key);
|
|
634
|
+
return this.store.ttl(taggedKey);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Flush all items with the current tags.
|
|
638
|
+
* This works by resetting the tag namespaces, effectively
|
|
639
|
+
* making all previously tagged items inaccessible.
|
|
640
|
+
*/
|
|
641
|
+
async flush() {
|
|
642
|
+
for (const tag of this.tags) {
|
|
643
|
+
const tagKey = `${this.tagSetPrefix}${tag}`;
|
|
644
|
+
const setKey = `${this.tagSetPrefix}${tag}:keys`;
|
|
645
|
+
const keys = await this.store.get(setKey) ?? [];
|
|
646
|
+
for (const key of keys) {
|
|
647
|
+
await this.store.delete(key);
|
|
648
|
+
}
|
|
649
|
+
await this.store.delete(tagKey);
|
|
650
|
+
await this.store.delete(setKey);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Get the current tags.
|
|
655
|
+
*/
|
|
656
|
+
getTags() {
|
|
657
|
+
return [...this.tags];
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Get the underlying store.
|
|
661
|
+
*/
|
|
662
|
+
getStore() {
|
|
663
|
+
return this.store;
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// src/cache/CacheManager.ts
|
|
668
|
+
var TaggableCacheStoreWrapper = class {
|
|
669
|
+
constructor(store) {
|
|
670
|
+
this.store = store;
|
|
671
|
+
}
|
|
672
|
+
get(key) {
|
|
673
|
+
return this.store.get(key);
|
|
674
|
+
}
|
|
675
|
+
set(key, value, ttl) {
|
|
676
|
+
return this.store.set(key, value, ttl);
|
|
677
|
+
}
|
|
678
|
+
has(key) {
|
|
679
|
+
return this.store.has(key);
|
|
680
|
+
}
|
|
681
|
+
delete(key) {
|
|
682
|
+
return this.store.delete(key);
|
|
683
|
+
}
|
|
684
|
+
clear() {
|
|
685
|
+
return this.store.clear();
|
|
686
|
+
}
|
|
687
|
+
increment(key, value) {
|
|
688
|
+
return this.store.increment(key, value);
|
|
689
|
+
}
|
|
690
|
+
decrement(key, value) {
|
|
691
|
+
return this.store.decrement(key, value);
|
|
692
|
+
}
|
|
693
|
+
remember(key, ttl, callback) {
|
|
694
|
+
return this.store.remember(key, ttl, callback);
|
|
695
|
+
}
|
|
696
|
+
rememberForever(key, callback) {
|
|
697
|
+
return this.store.rememberForever(key, callback);
|
|
698
|
+
}
|
|
699
|
+
getMany(keys) {
|
|
700
|
+
return this.store.getMany(keys);
|
|
701
|
+
}
|
|
702
|
+
setMany(items, ttl) {
|
|
703
|
+
return this.store.setMany(items, ttl);
|
|
704
|
+
}
|
|
705
|
+
deleteMany(keys) {
|
|
706
|
+
return this.store.deleteMany(keys);
|
|
707
|
+
}
|
|
708
|
+
ttl(key) {
|
|
709
|
+
return this.store.ttl(key);
|
|
710
|
+
}
|
|
711
|
+
tags(tags) {
|
|
712
|
+
return new TaggedCache(this.store, tags);
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
var CacheManager = class {
|
|
716
|
+
defaultStoreName;
|
|
717
|
+
storeFactories = /* @__PURE__ */ new Map();
|
|
718
|
+
resolvedStores = /* @__PURE__ */ new Map();
|
|
719
|
+
constructor(config = {}) {
|
|
720
|
+
this.defaultStoreName = config.default ?? "memory";
|
|
721
|
+
this.registerBuiltinDrivers();
|
|
722
|
+
if (config.stores) {
|
|
723
|
+
for (const [name, storeConfig] of Object.entries(config.stores)) {
|
|
724
|
+
this.registerStoreFromConfig(name, storeConfig);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (!this.storeFactories.has(this.defaultStoreName) && this.defaultStoreName === "memory") {
|
|
728
|
+
this.storeFactories.set("memory", () => new MemoryStore());
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
driverFactories = /* @__PURE__ */ new Map();
|
|
732
|
+
/**
|
|
733
|
+
* Register built-in store drivers.
|
|
734
|
+
*/
|
|
735
|
+
registerBuiltinDrivers() {
|
|
736
|
+
this.driverFactories.set("memory", (options) => {
|
|
737
|
+
return new MemoryStore(options);
|
|
738
|
+
});
|
|
739
|
+
this.driverFactories.set("redis", (options) => {
|
|
740
|
+
return new RedisStore(options);
|
|
741
|
+
});
|
|
742
|
+
this.driverFactories.set("file", (options) => {
|
|
743
|
+
return new FileStore(options);
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Register a store from configuration.
|
|
748
|
+
*/
|
|
749
|
+
registerStoreFromConfig(name, config) {
|
|
750
|
+
const { driver, ...options } = config;
|
|
751
|
+
const factory = this.driverFactories.get(driver);
|
|
752
|
+
if (!factory) {
|
|
753
|
+
throw new Error(`Unknown cache driver: ${driver}`);
|
|
754
|
+
}
|
|
755
|
+
this.storeFactories.set(name, () => factory(options));
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Get a cache store by name.
|
|
759
|
+
* Returns the default store if no name is specified.
|
|
760
|
+
*/
|
|
761
|
+
store(name) {
|
|
762
|
+
const storeName = name ?? this.defaultStoreName;
|
|
763
|
+
const cached = this.resolvedStores.get(storeName);
|
|
764
|
+
if (cached) {
|
|
765
|
+
return cached;
|
|
766
|
+
}
|
|
767
|
+
const factory = this.storeFactories.get(storeName);
|
|
768
|
+
if (!factory) {
|
|
769
|
+
throw new Error(`Cache store not found: ${storeName}`);
|
|
770
|
+
}
|
|
771
|
+
const store = new TaggableCacheStoreWrapper(factory());
|
|
772
|
+
this.resolvedStores.set(storeName, store);
|
|
773
|
+
return store;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Register a custom store factory.
|
|
777
|
+
*/
|
|
778
|
+
registerStore(name, factory) {
|
|
779
|
+
this.storeFactories.set(name, factory);
|
|
780
|
+
this.resolvedStores.delete(name);
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Register a custom driver.
|
|
784
|
+
*/
|
|
785
|
+
registerDriver(name, factory) {
|
|
786
|
+
this.driverFactories.set(name, factory);
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Check if a store is registered.
|
|
790
|
+
*/
|
|
791
|
+
hasStore(name) {
|
|
792
|
+
return this.storeFactories.has(name);
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Get the default store name.
|
|
796
|
+
*/
|
|
797
|
+
getDefaultStoreName() {
|
|
798
|
+
return this.defaultStoreName;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Get all registered store names.
|
|
802
|
+
*/
|
|
803
|
+
getStoreNames() {
|
|
804
|
+
return Array.from(this.storeFactories.keys());
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
function createCacheManager(config) {
|
|
808
|
+
return new CacheManager(config);
|
|
809
|
+
}
|
|
810
|
+
export {
|
|
811
|
+
CacheManager,
|
|
812
|
+
FileStore,
|
|
813
|
+
MemoryStore,
|
|
814
|
+
RedisStore,
|
|
815
|
+
TaggedCache,
|
|
816
|
+
createCacheManager
|
|
817
|
+
};
|