@c9up/echo 0.1.5 → 0.1.7
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/CacheManager.d.ts +63 -28
- package/dist/CacheManager.d.ts.map +1 -1
- package/dist/CacheManager.js +348 -70
- package/dist/CacheManager.js.map +1 -1
- package/dist/EchoProvider.d.ts +15 -9
- package/dist/EchoProvider.d.ts.map +1 -1
- package/dist/EchoProvider.js +47 -13
- package/dist/EchoProvider.js.map +1 -1
- package/dist/StoreManager.d.ts +61 -0
- package/dist/StoreManager.d.ts.map +1 -0
- package/dist/StoreManager.js +70 -0
- package/dist/StoreManager.js.map +1 -0
- package/dist/drivers/MemoryDriver.d.ts +13 -6
- package/dist/drivers/MemoryDriver.d.ts.map +1 -1
- package/dist/drivers/MemoryDriver.js +67 -61
- package/dist/drivers/MemoryDriver.js.map +1 -1
- package/dist/drivers/RedisDriver.d.ts +17 -17
- package/dist/drivers/RedisDriver.d.ts.map +1 -1
- package/dist/drivers/RedisDriver.js +103 -55
- package/dist/drivers/RedisDriver.js.map +1 -1
- package/dist/drivers/TieredDriver.d.ts +41 -0
- package/dist/drivers/TieredDriver.d.ts.map +1 -0
- package/dist/drivers/TieredDriver.js +132 -0
- package/dist/drivers/TieredDriver.js.map +1 -0
- package/dist/duration.d.ts +32 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +75 -0
- package/dist/duration.js.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +31 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +15 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/testing/main.d.ts +41 -0
- package/dist/testing/main.d.ts.map +1 -0
- package/dist/testing/main.js +41 -0
- package/dist/testing/main.js.map +1 -0
- package/dist/types.d.ts +146 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +6 -1
- package/src/CacheManager.ts +497 -99
- package/src/EchoProvider.ts +58 -15
- package/src/StoreManager.ts +104 -0
- package/src/drivers/MemoryDriver.ts +97 -66
- package/src/drivers/RedisDriver.ts +135 -58
- package/src/drivers/TieredDriver.ts +186 -0
- package/src/duration.ts +86 -0
- package/src/errors.ts +33 -0
- package/src/index.ts +43 -3
- package/src/testing/main.ts +69 -0
- package/src/types.ts +156 -0
package/dist/EchoProvider.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type CacheConfig } from "./CacheManager.js";
|
|
|
6
6
|
*/
|
|
7
7
|
interface EchoContainer {
|
|
8
8
|
singleton(token: unknown, factory: () => unknown): void;
|
|
9
|
-
resolve<T = unknown>(token: unknown): T
|
|
9
|
+
resolve<T = unknown>(token: unknown): Promise<T>;
|
|
10
10
|
}
|
|
11
11
|
interface EchoConfigStore {
|
|
12
12
|
get<T = unknown>(key: string): T | undefined;
|
|
@@ -20,28 +20,34 @@ export interface EchoProviderConfig extends CacheConfig {
|
|
|
20
20
|
* Driver to bind by default. Only `"memory"` is created
|
|
21
21
|
* automatically — other drivers (Redis etc.) need custom client
|
|
22
22
|
* wiring, so apps build the `CacheManager` themselves and call
|
|
23
|
-
* `setCache(...)` from `@c9up/echo/services/main
|
|
23
|
+
* `setCache(...)` from `@c9up/echo/services/main`, or use the
|
|
24
|
+
* multi-store `{ default, stores }` config with `drivers.*`.
|
|
24
25
|
*
|
|
25
26
|
* Default `"memory"`.
|
|
26
27
|
*/
|
|
27
28
|
driver?: "memory";
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
|
-
* EchoProvider — registers a
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
* EchoProvider — registers a `CacheManager` (single-store default, or the
|
|
32
|
+
* default store of a `{ default, stores }` config) so apps can
|
|
33
|
+
* `import cache from '@c9up/echo/services/main'` and use it straight away. If
|
|
34
|
+
* the host container exposes an `emitter`, cache events (`cache:hit` / `miss` /
|
|
35
|
+
* `written` / `deleted` / `cleared`) are wired through it.
|
|
33
36
|
*
|
|
34
37
|
* // reamrc.ts
|
|
35
38
|
* providers: [() => import('@c9up/echo/provider')]
|
|
36
39
|
*
|
|
37
|
-
* // config/cache.ts
|
|
40
|
+
* // config/cache.ts (single store)
|
|
38
41
|
* export default { driver: 'memory', prefix: 'myapp', ttl: 300 }
|
|
39
42
|
*
|
|
40
|
-
* //
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
+
* // config/cache.ts (multi-store)
|
|
44
|
+
* export default defineConfig({
|
|
45
|
+
* default: 'memory',
|
|
46
|
+
* stores: { memory: { driver: drivers.memory() } },
|
|
47
|
+
* })
|
|
43
48
|
*/
|
|
44
49
|
export default class EchoProvider {
|
|
50
|
+
#private;
|
|
45
51
|
protected app: EchoAppContext;
|
|
46
52
|
constructor(app: EchoAppContext);
|
|
47
53
|
register(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EchoProvider.d.ts","sourceRoot":"","sources":["../src/EchoProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"EchoProvider.d.ts","sourceRoot":"","sources":["../src/EchoProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,mBAAmB,CAAC;AAMnE;;;;GAIG;AACH,UAAU,aAAa;IACtB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjD;AACD,UAAU,eAAe;IACxB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AACD,MAAM,WAAW,cAAc;IAC9B,SAAS,EAAE,aAAa,CAAC;IACzB,MAAM,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACtD;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CAClB;AAmBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,OAAO,OAAO,YAAY;;IACpB,SAAS,CAAC,GAAG,EAAE,cAAc;gBAAnB,GAAG,EAAE,cAAc;IAYzC,QAAQ,IAAI,IAAI;IA0BV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAC/B"}
|
package/dist/EchoProvider.js
CHANGED
|
@@ -1,40 +1,74 @@
|
|
|
1
1
|
import { CacheManager } from "./CacheManager.js";
|
|
2
2
|
import { MemoryDriver } from "./drivers/MemoryDriver.js";
|
|
3
|
+
import { CacheStoreManager } from "./StoreManager.js";
|
|
3
4
|
import { setCache } from "./services/main.js";
|
|
5
|
+
function isEmitter(value) {
|
|
6
|
+
return (typeof value === "object" &&
|
|
7
|
+
value !== null &&
|
|
8
|
+
typeof Reflect.get(value, "emit") === "function");
|
|
9
|
+
}
|
|
10
|
+
function isMultiStoreConfig(value) {
|
|
11
|
+
return (typeof value === "object" &&
|
|
12
|
+
value !== null &&
|
|
13
|
+
"stores" in value &&
|
|
14
|
+
"default" in value);
|
|
15
|
+
}
|
|
4
16
|
/**
|
|
5
|
-
* EchoProvider — registers a
|
|
6
|
-
*
|
|
7
|
-
*
|
|
17
|
+
* EchoProvider — registers a `CacheManager` (single-store default, or the
|
|
18
|
+
* default store of a `{ default, stores }` config) so apps can
|
|
19
|
+
* `import cache from '@c9up/echo/services/main'` and use it straight away. If
|
|
20
|
+
* the host container exposes an `emitter`, cache events (`cache:hit` / `miss` /
|
|
21
|
+
* `written` / `deleted` / `cleared`) are wired through it.
|
|
8
22
|
*
|
|
9
23
|
* // reamrc.ts
|
|
10
24
|
* providers: [() => import('@c9up/echo/provider')]
|
|
11
25
|
*
|
|
12
|
-
* // config/cache.ts
|
|
26
|
+
* // config/cache.ts (single store)
|
|
13
27
|
* export default { driver: 'memory', prefix: 'myapp', ttl: 300 }
|
|
14
28
|
*
|
|
15
|
-
* //
|
|
16
|
-
*
|
|
17
|
-
*
|
|
29
|
+
* // config/cache.ts (multi-store)
|
|
30
|
+
* export default defineConfig({
|
|
31
|
+
* default: 'memory',
|
|
32
|
+
* stores: { memory: { driver: drivers.memory() } },
|
|
33
|
+
* })
|
|
18
34
|
*/
|
|
19
35
|
export default class EchoProvider {
|
|
20
36
|
app;
|
|
21
37
|
constructor(app) {
|
|
22
38
|
this.app = app;
|
|
23
39
|
}
|
|
40
|
+
async #resolveEmitter() {
|
|
41
|
+
try {
|
|
42
|
+
const candidate = await this.app.container.resolve("emitter");
|
|
43
|
+
if (isEmitter(candidate))
|
|
44
|
+
return candidate;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// No emitter bound — events are simply not emitted.
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
24
51
|
register() {
|
|
25
|
-
this.app.container.singleton(CacheManager, () => {
|
|
26
|
-
const
|
|
27
|
-
const
|
|
52
|
+
this.app.container.singleton(CacheManager, async () => {
|
|
53
|
+
const emitter = await this.#resolveEmitter();
|
|
54
|
+
const raw = this.app.config.get("cache");
|
|
55
|
+
if (isMultiStoreConfig(raw)) {
|
|
56
|
+
const manager = new CacheStoreManager({ ...raw, emitter });
|
|
57
|
+
return manager.use();
|
|
58
|
+
}
|
|
59
|
+
const config = (raw ?? {});
|
|
60
|
+
const driver = config.driver ?? "memory";
|
|
28
61
|
if (driver !== "memory") {
|
|
29
62
|
throw new Error(`[echo] Unsupported driver '${driver}' for default provider — ` +
|
|
30
|
-
"wire CacheManager yourself for non-memory drivers
|
|
63
|
+
"wire CacheManager yourself for non-memory drivers, or use the " +
|
|
64
|
+
"multi-store `{ default, stores }` config.");
|
|
31
65
|
}
|
|
32
|
-
return new CacheManager(new MemoryDriver(), config);
|
|
66
|
+
return new CacheManager(new MemoryDriver(), { ...config, emitter });
|
|
33
67
|
});
|
|
34
68
|
this.app.container.singleton("cache", () => this.app.container.resolve(CacheManager));
|
|
35
69
|
}
|
|
36
70
|
async boot() {
|
|
37
|
-
setCache(this.app.container.resolve(CacheManager));
|
|
71
|
+
setCache(await this.app.container.resolve(CacheManager));
|
|
38
72
|
}
|
|
39
73
|
async shutdown() { }
|
|
40
74
|
}
|
package/dist/EchoProvider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EchoProvider.js","sourceRoot":"","sources":["../src/EchoProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"EchoProvider.js","sourceRoot":"","sources":["../src/EchoProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAyB,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAiC9C,SAAS,SAAS,CAAC,KAAc;IAChC,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,UAAU,CAChD,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACzC,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,QAAQ,IAAI,KAAK;QACjB,SAAS,IAAI,KAAK,CAClB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,OAAO,OAAO,YAAY;IACV;IAAtB,YAAsB,GAAmB;QAAnB,QAAG,GAAH,GAAG,CAAgB;IAAG,CAAC;IAE7C,KAAK,CAAC,eAAe;QACpB,IAAI,CAAC;YACJ,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAU,SAAS,CAAC,CAAC;YACvE,IAAI,SAAS,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACR,oDAAoD;QACrD,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,QAAQ;QACP,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YACrD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAU,OAAO,CAAC,CAAC;YAElD,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC3D,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC;YACtB,CAAC;YAED,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,CAAuB,CAAC;YACjD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,QAAQ,CAAC;YACzC,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CACd,8BAA8B,MAAM,2BAA2B;oBAC9D,gEAAgE;oBAChE,2CAA2C,CAC5C,CAAC;YACH,CAAC;YACD,OAAO,IAAI,YAAY,CAAC,IAAI,YAAY,EAAE,EAAE,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,CAC1C,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAe,YAAY,CAAC,CACtD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACT,QAAQ,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAe,YAAY,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,QAAQ,KAAmB,CAAC;CAClC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-store cache manager — `{ default, stores }` config + `cache.use(name)`
|
|
3
|
+
* (bentocache `BentoCache` / @adonisjs/cache parity), plus the `drivers.*`
|
|
4
|
+
* factory helpers.
|
|
5
|
+
*
|
|
6
|
+
* const cache = new CacheStoreManager(defineConfig({
|
|
7
|
+
* default: "memory",
|
|
8
|
+
* stores: {
|
|
9
|
+
* memory: { driver: drivers.memory() },
|
|
10
|
+
* redis: { driver: drivers.redis({ client }) },
|
|
11
|
+
* },
|
|
12
|
+
* }))
|
|
13
|
+
*
|
|
14
|
+
* await cache.use().set({ key: "k", value: 1 }) // default store
|
|
15
|
+
* await cache.use("redis").get({ key: "k" }) // named store
|
|
16
|
+
*/
|
|
17
|
+
import { CacheManager } from "./CacheManager.js";
|
|
18
|
+
import { type RedisClient } from "./drivers/RedisDriver.js";
|
|
19
|
+
import { type CacheBus } from "./drivers/TieredDriver.js";
|
|
20
|
+
import type { Duration } from "./duration.js";
|
|
21
|
+
import type { CacheDriver, CacheEmitter } from "./types.js";
|
|
22
|
+
/** A lazily-instantiated driver (built once per store, on first `use`). */
|
|
23
|
+
export type DriverFactory = () => CacheDriver;
|
|
24
|
+
export interface StoreConfig {
|
|
25
|
+
driver: DriverFactory;
|
|
26
|
+
prefix?: string;
|
|
27
|
+
/** Default TTL in seconds. */
|
|
28
|
+
ttl?: number;
|
|
29
|
+
grace?: Duration;
|
|
30
|
+
timeout?: Duration;
|
|
31
|
+
hardTimeout?: Duration;
|
|
32
|
+
lockTimeout?: Duration;
|
|
33
|
+
}
|
|
34
|
+
export interface MultiStoreConfig {
|
|
35
|
+
default: string;
|
|
36
|
+
stores: Record<string, StoreConfig>;
|
|
37
|
+
/** Shared emitter for all stores' events. */
|
|
38
|
+
emitter?: CacheEmitter;
|
|
39
|
+
}
|
|
40
|
+
/** Driver factory helpers (bento `drivers.memory` / `drivers.redis`). */
|
|
41
|
+
export declare const drivers: {
|
|
42
|
+
memory(options?: {
|
|
43
|
+
sweepIntervalMs?: number;
|
|
44
|
+
}): DriverFactory;
|
|
45
|
+
redis(options: {
|
|
46
|
+
client: RedisClient;
|
|
47
|
+
prefix?: string;
|
|
48
|
+
}): DriverFactory;
|
|
49
|
+
tiered(options: {
|
|
50
|
+
l1: DriverFactory;
|
|
51
|
+
l2: DriverFactory;
|
|
52
|
+
bus?: CacheBus;
|
|
53
|
+
}): DriverFactory;
|
|
54
|
+
};
|
|
55
|
+
export declare class CacheStoreManager {
|
|
56
|
+
#private;
|
|
57
|
+
constructor(config: MultiStoreConfig);
|
|
58
|
+
/** Resolve a store by name (or the default). Instances are built once and cached. */
|
|
59
|
+
use(name?: string): CacheManager;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=StoreManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoreManager.d.ts","sourceRoot":"","sources":["../src/StoreManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,KAAK,WAAW,EAAe,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,2BAA2B,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,2EAA2E;AAC3E,MAAM,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,WAAW,CAAC,EAAE,QAAQ,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,6CAA6C;IAC7C,OAAO,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,yEAAyE;AACzE,eAAO,MAAM,OAAO;qBACF;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa;mBAG9C;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa;oBAGvD;QACf,EAAE,EAAE,aAAa,CAAC;QAClB,EAAE,EAAE,aAAa,CAAC;QAClB,GAAG,CAAC,EAAE,QAAQ,CAAC;KACf,GAAG,aAAa;CAQjB,CAAC;AAEF,qBAAa,iBAAiB;;gBAIjB,MAAM,EAAE,gBAAgB;IASpC,qFAAqF;IACrF,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY;CAsBhC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-store cache manager — `{ default, stores }` config + `cache.use(name)`
|
|
3
|
+
* (bentocache `BentoCache` / @adonisjs/cache parity), plus the `drivers.*`
|
|
4
|
+
* factory helpers.
|
|
5
|
+
*
|
|
6
|
+
* const cache = new CacheStoreManager(defineConfig({
|
|
7
|
+
* default: "memory",
|
|
8
|
+
* stores: {
|
|
9
|
+
* memory: { driver: drivers.memory() },
|
|
10
|
+
* redis: { driver: drivers.redis({ client }) },
|
|
11
|
+
* },
|
|
12
|
+
* }))
|
|
13
|
+
*
|
|
14
|
+
* await cache.use().set({ key: "k", value: 1 }) // default store
|
|
15
|
+
* await cache.use("redis").get({ key: "k" }) // named store
|
|
16
|
+
*/
|
|
17
|
+
import { CacheManager } from "./CacheManager.js";
|
|
18
|
+
import { MemoryDriver } from "./drivers/MemoryDriver.js";
|
|
19
|
+
import { RedisDriver } from "./drivers/RedisDriver.js";
|
|
20
|
+
import { TieredDriver } from "./drivers/TieredDriver.js";
|
|
21
|
+
/** Driver factory helpers (bento `drivers.memory` / `drivers.redis`). */
|
|
22
|
+
export const drivers = {
|
|
23
|
+
memory(options) {
|
|
24
|
+
return () => new MemoryDriver(options?.sweepIntervalMs);
|
|
25
|
+
},
|
|
26
|
+
redis(options) {
|
|
27
|
+
return () => new RedisDriver(options.client, options.prefix);
|
|
28
|
+
},
|
|
29
|
+
tiered(options) {
|
|
30
|
+
return () => new TieredDriver({
|
|
31
|
+
l1: options.l1(),
|
|
32
|
+
l2: options.l2(),
|
|
33
|
+
bus: options.bus,
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export class CacheStoreManager {
|
|
38
|
+
#config;
|
|
39
|
+
#built = new Map();
|
|
40
|
+
constructor(config) {
|
|
41
|
+
this.#config = config;
|
|
42
|
+
if (!config.stores[config.default]) {
|
|
43
|
+
throw new Error(`Echo: default store "${config.default}" is not defined in stores`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Resolve a store by name (or the default). Instances are built once and cached. */
|
|
47
|
+
use(name) {
|
|
48
|
+
const store = name ?? this.#config.default;
|
|
49
|
+
const existing = this.#built.get(store);
|
|
50
|
+
if (existing)
|
|
51
|
+
return existing;
|
|
52
|
+
const cfg = this.#config.stores[store];
|
|
53
|
+
if (!cfg) {
|
|
54
|
+
throw new Error(`Echo: unknown cache store "${store}"`);
|
|
55
|
+
}
|
|
56
|
+
const manager = new CacheManager(cfg.driver(), {
|
|
57
|
+
prefix: cfg.prefix,
|
|
58
|
+
ttl: cfg.ttl,
|
|
59
|
+
grace: cfg.grace,
|
|
60
|
+
timeout: cfg.timeout,
|
|
61
|
+
hardTimeout: cfg.hardTimeout,
|
|
62
|
+
lockTimeout: cfg.lockTimeout,
|
|
63
|
+
name: store,
|
|
64
|
+
emitter: this.#config.emitter,
|
|
65
|
+
});
|
|
66
|
+
this.#built.set(store, manager);
|
|
67
|
+
return manager;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=StoreManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoreManager.js","sourceRoot":"","sources":["../src/StoreManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAoB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAiB,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAyBxE,yEAAyE;AACzE,MAAM,CAAC,MAAM,OAAO,GAAG;IACtB,MAAM,CAAC,OAAsC;QAC5C,OAAO,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IACzD,CAAC;IACD,KAAK,CAAC,OAAiD;QACtD,OAAO,GAAG,EAAE,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC;IACD,MAAM,CAAC,OAIN;QACA,OAAO,GAAG,EAAE,CACX,IAAI,YAAY,CAAC;YAChB,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE;YAChB,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE;YAChB,GAAG,EAAE,OAAO,CAAC,GAAG;SAChB,CAAC,CAAC;IACL,CAAC;CACD,CAAC;AAEF,MAAM,OAAO,iBAAiB;IAC7B,OAAO,CAAmB;IAC1B,MAAM,GAA8B,IAAI,GAAG,EAAE,CAAC;IAE9C,YAAY,MAAwB;QACnC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACd,wBAAwB,MAAM,CAAC,OAAO,4BAA4B,CAClE,CAAC;QACH,CAAC;IACF,CAAC;IAED,qFAAqF;IACrF,GAAG,CAAC,IAAa;QAChB,MAAM,KAAK,GAAG,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,GAAG,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,GAAG,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE;YAC9C,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;SAC7B,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAChC,OAAO,OAAO,CAAC;IAChB,CAAC;CACD"}
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Memory cache driver — in-process Map with TTL
|
|
3
|
-
* Suitable for development and single-process deployments
|
|
2
|
+
* Memory cache driver — in-process Map with TTL, grace (stale-while-revalidate)
|
|
3
|
+
* and tag support. Suitable for development and single-process deployments, and
|
|
4
|
+
* as the L1 tier of {@link TieredDriver}.
|
|
4
5
|
*/
|
|
5
|
-
import type {
|
|
6
|
-
export declare class MemoryDriver implements
|
|
6
|
+
import type { CacheEntry, DriverSetOptions, TaggableDriver } from "../types.js";
|
|
7
|
+
export declare class MemoryDriver implements TaggableDriver {
|
|
7
8
|
#private;
|
|
8
9
|
constructor(sweepIntervalMs?: number);
|
|
9
10
|
destroy(): void;
|
|
10
11
|
get<T = unknown>(key: string): Promise<T | null>;
|
|
12
|
+
getEntry<T = unknown>(key: string): Promise<CacheEntry<T> | null>;
|
|
13
|
+
/** Synchronous read used by both `get` and `getEntry` (and by TieredDriver's L1 fast path). */
|
|
14
|
+
getEntrySync<T = unknown>(key: string): CacheEntry<T> | null;
|
|
11
15
|
set(key: string, value: unknown, ttlSeconds?: number): Promise<void>;
|
|
16
|
+
setEntry(key: string, value: unknown, options: DriverSetOptions): Promise<void>;
|
|
12
17
|
delete(key: string): Promise<boolean>;
|
|
13
18
|
flush(): Promise<void>;
|
|
14
19
|
has(key: string): Promise<boolean>;
|
|
15
|
-
/** Set with tags for group invalidation. */
|
|
20
|
+
/** Set with tags for group invalidation (bento parity; no grace). */
|
|
16
21
|
setWithTags(key: string, value: unknown, tags: string[], ttlSeconds?: number): Promise<void>;
|
|
17
|
-
/**
|
|
22
|
+
/** Invalidate all entries tagged with any of the given tags. */
|
|
23
|
+
deleteByTag(tags: string[]): Promise<void>;
|
|
24
|
+
/** @deprecated alias of {@link deleteByTag}. */
|
|
18
25
|
flushTags(tags: string[]): Promise<void>;
|
|
19
26
|
}
|
|
20
27
|
//# sourceMappingURL=MemoryDriver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoryDriver.d.ts","sourceRoot":"","sources":["../../src/drivers/MemoryDriver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"MemoryDriver.d.ts","sourceRoot":"","sources":["../../src/drivers/MemoryDriver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAWhF,qBAAa,YAAa,YAAW,cAAc;;gBAKtC,eAAe,SAAS;IAiBpC,OAAO,IAAI,IAAI;IAIT,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAMhD,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAIvE,+FAA+F;IAC/F,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;IAqEtD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE,QAAQ,CACb,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC;IAWV,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxC,qEAAqE;IAC/D,WAAW,CAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAIhB,gEAAgE;IAC1D,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBhD,gDAAgD;IAC1C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAG9C"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Memory cache driver — in-process Map with TTL
|
|
3
|
-
* Suitable for development and single-process deployments
|
|
2
|
+
* Memory cache driver — in-process Map with TTL, grace (stale-while-revalidate)
|
|
3
|
+
* and tag support. Suitable for development and single-process deployments, and
|
|
4
|
+
* as the L1 tier of {@link TieredDriver}.
|
|
4
5
|
*/
|
|
5
6
|
export class MemoryDriver {
|
|
6
7
|
#store = new Map();
|
|
@@ -10,7 +11,7 @@ export class MemoryDriver {
|
|
|
10
11
|
this.#sweepInterval = setInterval(() => {
|
|
11
12
|
const now = Date.now();
|
|
12
13
|
for (const [key, entry] of this.#store) {
|
|
13
|
-
if (entry.
|
|
14
|
+
if (entry.staleUntil > 0 && entry.staleUntil < now) {
|
|
14
15
|
this.#evict(key, entry);
|
|
15
16
|
}
|
|
16
17
|
}
|
|
@@ -24,40 +25,79 @@ export class MemoryDriver {
|
|
|
24
25
|
clearInterval(this.#sweepInterval);
|
|
25
26
|
}
|
|
26
27
|
async get(key) {
|
|
28
|
+
const entry = this.getEntrySync(key);
|
|
29
|
+
if (entry === null || entry.stale)
|
|
30
|
+
return null;
|
|
31
|
+
return entry.value;
|
|
32
|
+
}
|
|
33
|
+
async getEntry(key) {
|
|
34
|
+
return this.getEntrySync(key);
|
|
35
|
+
}
|
|
36
|
+
/** Synchronous read used by both `get` and `getEntry` (and by TieredDriver's L1 fast path). */
|
|
37
|
+
getEntrySync(key) {
|
|
27
38
|
const entry = this.#store.get(key);
|
|
28
39
|
if (!entry)
|
|
29
40
|
return null;
|
|
30
|
-
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
if (entry.staleUntil > 0 && entry.staleUntil < now) {
|
|
31
43
|
this.#evict(key, entry);
|
|
32
44
|
return null;
|
|
33
45
|
}
|
|
34
|
-
|
|
46
|
+
const stale = entry.expiresAt > 0 && entry.expiresAt < now;
|
|
47
|
+
return { value: entry.value, stale, expiresAt: entry.expiresAt };
|
|
35
48
|
}
|
|
36
49
|
/**
|
|
37
|
-
* Delete an entry AND scrub its tag-index refs. TTL expiry (sweep +
|
|
38
|
-
* get) must go through this — a bare `#store.delete` left dangling refs
|
|
39
|
-
* `#tagIndex`, which a later `set(key, vNew)` reusing the key would turn
|
|
40
|
-
* a wrong-purge under
|
|
50
|
+
* Delete an entry AND scrub its tag-index refs. TTL/grace expiry (sweep +
|
|
51
|
+
* lazy get) must go through this — a bare `#store.delete` left dangling refs
|
|
52
|
+
* in `#tagIndex`, which a later `set(key, vNew)` reusing the key would turn
|
|
53
|
+
* into a wrong-purge under deleteByTag (audit 2026-06-13).
|
|
41
54
|
*/
|
|
42
55
|
#evict(key, entry) {
|
|
43
56
|
for (const tag of entry.tags)
|
|
44
57
|
this.#tagIndex.get(tag)?.delete(key);
|
|
45
58
|
this.#store.delete(key);
|
|
46
59
|
}
|
|
47
|
-
|
|
60
|
+
#write(key, value, ttlSeconds, graceSeconds, tags, expiresAtOverride) {
|
|
48
61
|
if (value === null || value === undefined) {
|
|
49
62
|
throw new TypeError("Echo: caching null/undefined values is not supported");
|
|
50
63
|
}
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
//
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
const hasTtl = ttlSeconds != null && ttlSeconds > 0;
|
|
66
|
+
// An absolute `expiresAtOverride` (e.g. `expire()` marking stale-now) wins
|
|
67
|
+
// over the ttl-derived expiry; a past value makes the entry immediately stale.
|
|
68
|
+
const expiresAt = expiresAtOverride !== undefined
|
|
69
|
+
? expiresAtOverride
|
|
70
|
+
: hasTtl
|
|
71
|
+
? now + ttlSeconds * 1000
|
|
72
|
+
: 0;
|
|
73
|
+
const graceMs = graceSeconds > 0 ? graceSeconds * 1000 : 0;
|
|
74
|
+
// A never-expiring entry never goes stale either; otherwise grace extends
|
|
75
|
+
// physical retention past the logical expiry. Measure grace from the LATER
|
|
76
|
+
// of the expiry and now, so an already-past expiry still survives its grace.
|
|
77
|
+
const staleUntil = expiresAt > 0 ? Math.max(expiresAt, now) + graceMs : 0;
|
|
78
|
+
// Reconcile the tag index: overwriting a previously-tagged key must drop
|
|
79
|
+
// its old tag-index refs, or a later deleteByTag could purge this fresh
|
|
80
|
+
// value (audit 2026-05-22 F3).
|
|
55
81
|
const prev = this.#store.get(key);
|
|
56
82
|
if (prev !== undefined) {
|
|
57
83
|
for (const t of prev.tags)
|
|
58
84
|
this.#tagIndex.get(t)?.delete(key);
|
|
59
85
|
}
|
|
60
|
-
this.#store.set(key, { value, expiresAt, tags
|
|
86
|
+
this.#store.set(key, { value, expiresAt, staleUntil, tags });
|
|
87
|
+
for (const tag of tags) {
|
|
88
|
+
let set = this.#tagIndex.get(tag);
|
|
89
|
+
if (!set) {
|
|
90
|
+
set = new Set();
|
|
91
|
+
this.#tagIndex.set(tag, set);
|
|
92
|
+
}
|
|
93
|
+
set.add(key);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async set(key, value, ttlSeconds) {
|
|
97
|
+
this.#write(key, value, ttlSeconds, 0, []);
|
|
98
|
+
}
|
|
99
|
+
async setEntry(key, value, options) {
|
|
100
|
+
this.#write(key, value, options.ttlSeconds, options.graceSeconds ?? 0, options.tags ?? [], options.expiresAt);
|
|
61
101
|
}
|
|
62
102
|
async delete(key) {
|
|
63
103
|
const entry = this.#store.get(key);
|
|
@@ -73,52 +113,17 @@ export class MemoryDriver {
|
|
|
73
113
|
this.#tagIndex.clear();
|
|
74
114
|
}
|
|
75
115
|
async has(key) {
|
|
76
|
-
|
|
77
|
-
return val !== null;
|
|
116
|
+
return (await this.get(key)) !== null;
|
|
78
117
|
}
|
|
79
|
-
/** Set with tags for group invalidation. */
|
|
118
|
+
/** Set with tags for group invalidation (bento parity; no grace). */
|
|
80
119
|
async setWithTags(key, value, tags, ttlSeconds) {
|
|
81
|
-
|
|
82
|
-
// `ttlSeconds <= 0` (and undefined) as "no expiration". Previously
|
|
83
|
-
// `setWithTags` used a truthy check (`ttlSeconds ?`), which let a
|
|
84
|
-
// negative value through and produced `Date.now() + (-N * 1000)` —
|
|
85
|
-
// an already-past timestamp — so the entry was born already-expired.
|
|
86
|
-
// `set()` correctly returned the immortal-entry branch on the same
|
|
87
|
-
// input. The divergence made cache semantics depend on whether the
|
|
88
|
-
// caller used tags or not, which is the worst kind of "very hard to
|
|
89
|
-
// diagnose in prod" bug.
|
|
90
|
-
const expiresAt = ttlSeconds != null && ttlSeconds > 0 ? Date.now() + ttlSeconds * 1000 : 0;
|
|
91
|
-
// Audit 2026-05-22 F3 (overwrite leg): if `key` already exists with
|
|
92
|
-
// a different tag set, the old #tagIndex entries become dangling
|
|
93
|
-
// refs to the (now overwritten) key. Clean them up before re-tagging
|
|
94
|
-
// so subsequent flushTags doesn't iterate stale references.
|
|
95
|
-
const prev = this.#store.get(key);
|
|
96
|
-
if (prev !== undefined) {
|
|
97
|
-
for (const t of prev.tags)
|
|
98
|
-
this.#tagIndex.get(t)?.delete(key);
|
|
99
|
-
}
|
|
100
|
-
this.#store.set(key, { value, expiresAt, tags });
|
|
101
|
-
for (const tag of tags) {
|
|
102
|
-
let set = this.#tagIndex.get(tag);
|
|
103
|
-
if (!set) {
|
|
104
|
-
set = new Set();
|
|
105
|
-
this.#tagIndex.set(tag, set);
|
|
106
|
-
}
|
|
107
|
-
set.add(key);
|
|
108
|
-
}
|
|
120
|
+
this.#write(key, value, ttlSeconds, 0, tags);
|
|
109
121
|
}
|
|
110
|
-
/**
|
|
111
|
-
async
|
|
112
|
-
// Audit 2026-05-22 F3:
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
// reference. A later flushTags(`fr`) would iterate over the
|
|
116
|
-
// stale entry, attempt `#store.delete(key)` (no-op), and the entry
|
|
117
|
-
// would silently linger in #tagIndex forever. Worse — if a new
|
|
118
|
-
// entry was later written under the same key with different tags,
|
|
119
|
-
// flushTags(`fr`) would WRONGLY purge it because of the residue.
|
|
120
|
-
// Collect keys first, then scrub each one from EVERY tag set it
|
|
121
|
-
// belonged to (including the tags we're not flushing this round).
|
|
122
|
+
/** Invalidate all entries tagged with any of the given tags. */
|
|
123
|
+
async deleteByTag(tags) {
|
|
124
|
+
// Audit 2026-05-22 F3: scrub each key from EVERY tag set it belongs to
|
|
125
|
+
// (including tags not being flushed this round), else a multi-tagged key
|
|
126
|
+
// leaves a dangling ref that later wrong-purges a reused key.
|
|
122
127
|
const toDelete = new Set();
|
|
123
128
|
for (const tag of tags) {
|
|
124
129
|
const keys = this.#tagIndex.get(tag);
|
|
@@ -136,11 +141,12 @@ export class MemoryDriver {
|
|
|
136
141
|
}
|
|
137
142
|
this.#store.delete(key);
|
|
138
143
|
}
|
|
139
|
-
// Now drop the flushed tag sets themselves (any other keys still
|
|
140
|
-
// referenced by them have been processed in the toDelete loop and
|
|
141
|
-
// already removed via the inner scrub).
|
|
142
144
|
for (const tag of tags)
|
|
143
145
|
this.#tagIndex.delete(tag);
|
|
144
146
|
}
|
|
147
|
+
/** @deprecated alias of {@link deleteByTag}. */
|
|
148
|
+
async flushTags(tags) {
|
|
149
|
+
return this.deleteByTag(tags);
|
|
150
|
+
}
|
|
145
151
|
}
|
|
146
152
|
//# sourceMappingURL=MemoryDriver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoryDriver.js","sourceRoot":"","sources":["../../src/drivers/MemoryDriver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"MemoryDriver.js","sourceRoot":"","sources":["../../src/drivers/MemoryDriver.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Redis cache driver — production-grade cache with TTL
|
|
2
|
+
* Redis cache driver — production-grade cache with TTL, grace
|
|
3
|
+
* (stale-while-revalidate) and tags. Suitable as the L2 tier of
|
|
4
|
+
* {@link TieredDriver}.
|
|
3
5
|
*
|
|
4
6
|
* Requires a Redis client instance implementing the minimal interface below.
|
|
5
7
|
* Compatible with ioredis and redis (node-redis) clients.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
9
|
+
* STORAGE FORMAT (breaking vs echo <=0.1.5): values are stored as a JSON
|
|
10
|
+
* envelope `{ "v": <value>, "e": <logicalExpiryEpochMs> }`. The physical Redis
|
|
11
|
+
* TTL (`EX`) covers `ttl + grace` so a logically-expired value survives for the
|
|
12
|
+
* grace window; `e` records the logical expiry so reads can flag it stale.
|
|
8
13
|
*/
|
|
9
|
-
import type {
|
|
14
|
+
import type { CacheEntry, DriverSetOptions, TaggableDriver } from "../types.js";
|
|
10
15
|
/** Minimal Redis client interface — compatible with ioredis and node-redis. */
|
|
11
16
|
export interface RedisClient {
|
|
12
17
|
get(key: string): Promise<string | null>;
|
|
@@ -21,30 +26,25 @@ export interface RedisClient {
|
|
|
21
26
|
ttl(key: string): Promise<number>;
|
|
22
27
|
scan?(cursor: string, matchOption: "MATCH", pattern: string, countOption: "COUNT", count: number): Promise<[string, string[]]>;
|
|
23
28
|
}
|
|
24
|
-
export declare class RedisDriver implements
|
|
29
|
+
export declare class RedisDriver implements TaggableDriver {
|
|
25
30
|
#private;
|
|
26
31
|
constructor(client: RedisClient, prefix?: string);
|
|
27
32
|
delete(key: string): Promise<boolean>;
|
|
28
33
|
get<T = unknown>(key: string): Promise<T | null>;
|
|
34
|
+
getEntry<T = unknown>(key: string): Promise<CacheEntry<T> | null>;
|
|
29
35
|
set(key: string, value: unknown, ttlSeconds?: number): Promise<void>;
|
|
36
|
+
setEntry(key: string, value: unknown, options: DriverSetOptions): Promise<void>;
|
|
30
37
|
flush(): Promise<void>;
|
|
31
38
|
has(key: string): Promise<boolean>;
|
|
32
|
-
/**
|
|
33
|
-
* Set a value with tag memberships for group invalidation.
|
|
34
|
-
*
|
|
35
|
-
* Re-tagging an existing key (e.g. `['news']` → `['homepage']`) cleans
|
|
36
|
-
* the stale memberships via the per-key reverse-index — without this,
|
|
37
|
-
* a later `flushTags(['news'])` would silently wipe the still-current
|
|
38
|
-
* value because the abandoned `tag:news` set kept pointing at it.
|
|
39
|
-
*/
|
|
39
|
+
/** Set a value with tag memberships for group invalidation (no grace). */
|
|
40
40
|
setWithTags(key: string, value: unknown, tags: string[], ttlSeconds?: number): Promise<void>;
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
* per-key reverse-index AND cross-tag memberships so a multi-tag key
|
|
44
|
-
*
|
|
45
|
-
* the `homepage` tag-set — otherwise the `homepage` set ends up with a
|
|
46
|
-
* dangling reference to a now-deleted value.
|
|
42
|
+
* Invalidate all entries tagged with any of the given tags. Cleans the
|
|
43
|
+
* per-key reverse-index AND cross-tag memberships so a multi-tag key flushed
|
|
44
|
+
* via one tag is also removed from the others.
|
|
47
45
|
*/
|
|
46
|
+
deleteByTag(tags: string[]): Promise<void>;
|
|
47
|
+
/** @deprecated alias of {@link deleteByTag}. */
|
|
48
48
|
flushTags(tags: string[]): Promise<void>;
|
|
49
49
|
}
|
|
50
50
|
//# sourceMappingURL=RedisDriver.d.ts.map
|