@warlock.js/cache 5.19.1 → 5.21.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/CHANGELOG.md +61 -1
- package/cjs/index.cjs +1439 -331
- package/cjs/index.cjs.map +1 -1
- package/esm/cache-manager.d.mts +4 -0
- package/esm/cache-manager.d.mts.map +1 -1
- package/esm/cache-manager.mjs +23 -5
- package/esm/cache-manager.mjs.map +1 -1
- package/esm/cached/auto-key.d.mts +14 -14
- package/esm/cached/auto-key.d.mts.map +1 -1
- package/esm/cached/auto-key.mjs +50 -34
- package/esm/cached/auto-key.mjs.map +1 -1
- package/esm/cached/cached.mjs +4 -0
- package/esm/cached/cached.mjs.map +1 -1
- package/esm/drivers/base-cache-driver.d.mts +84 -8
- package/esm/drivers/base-cache-driver.d.mts.map +1 -1
- package/esm/drivers/base-cache-driver.mjs +152 -42
- package/esm/drivers/base-cache-driver.mjs.map +1 -1
- package/esm/drivers/file-cache-driver.d.mts +15 -0
- package/esm/drivers/file-cache-driver.d.mts.map +1 -1
- package/esm/drivers/file-cache-driver.mjs +68 -18
- package/esm/drivers/file-cache-driver.mjs.map +1 -1
- package/esm/drivers/in-memory-tag-index.d.mts +44 -0
- package/esm/drivers/in-memory-tag-index.d.mts.map +1 -0
- package/esm/drivers/in-memory-tag-index.mjs +73 -0
- package/esm/drivers/in-memory-tag-index.mjs.map +1 -0
- package/esm/drivers/index.d.mts +1 -1
- package/esm/drivers/index.mjs +1 -1
- package/esm/drivers/lru-memory-cache-driver.d.mts +43 -0
- package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/lru-memory-cache-driver.mjs +96 -0
- package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/memory-cache-driver.d.mts +87 -30
- package/esm/drivers/memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/memory-cache-driver.mjs +214 -96
- package/esm/drivers/memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/memory-extended-cache-driver.d.mts +9 -5
- package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -1
- package/esm/drivers/memory-extended-cache-driver.mjs +10 -23
- package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -1
- package/esm/drivers/mock-cache-driver.d.mts +41 -0
- package/esm/drivers/mock-cache-driver.d.mts.map +1 -1
- package/esm/drivers/mock-cache-driver.mjs +105 -8
- package/esm/drivers/mock-cache-driver.mjs.map +1 -1
- package/esm/drivers/null-cache-driver.d.mts +8 -2
- package/esm/drivers/null-cache-driver.d.mts.map +1 -1
- package/esm/drivers/null-cache-driver.mjs +10 -2
- package/esm/drivers/null-cache-driver.mjs.map +1 -1
- package/esm/drivers/pg-cache-driver.d.mts +77 -3
- package/esm/drivers/pg-cache-driver.d.mts.map +1 -1
- package/esm/drivers/pg-cache-driver.mjs +229 -7
- package/esm/drivers/pg-cache-driver.mjs.map +1 -1
- package/esm/drivers/redis-cache-driver.d.mts +86 -1
- package/esm/drivers/redis-cache-driver.d.mts.map +1 -1
- package/esm/drivers/redis-cache-driver.mjs +252 -16
- package/esm/drivers/redis-cache-driver.mjs.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/index.mjs +2 -2
- package/esm/list/memory-cache-list.d.mts +11 -8
- package/esm/list/memory-cache-list.d.mts.map +1 -1
- package/esm/list/memory-cache-list.mjs +39 -37
- package/esm/list/memory-cache-list.mjs.map +1 -1
- package/esm/scoped-cache.d.mts +9 -3
- package/esm/scoped-cache.d.mts.map +1 -1
- package/esm/scoped-cache.mjs +19 -7
- package/esm/scoped-cache.mjs.map +1 -1
- package/esm/tagged-cache.d.mts +20 -0
- package/esm/tagged-cache.d.mts.map +1 -1
- package/esm/tagged-cache.mjs +62 -24
- package/esm/tagged-cache.mjs.map +1 -1
- package/esm/tagged-scoped-cache.d.mts +7 -3
- package/esm/tagged-scoped-cache.d.mts.map +1 -1
- package/esm/tagged-scoped-cache.mjs +19 -7
- package/esm/tagged-scoped-cache.mjs.map +1 -1
- package/esm/types.d.mts +41 -3
- package/esm/types.d.mts.map +1 -1
- package/esm/types.mjs.map +1 -1
- package/esm/utils.d.mts +9 -3
- package/esm/utils.d.mts.map +1 -1
- package/esm/utils.mjs +16 -6
- package/esm/utils.mjs.map +1 -1
- package/llms-full.txt +1685 -1666
- package/llms.txt +18 -18
- package/package.json +4 -4
- package/skills/apply-cache-patterns/SKILL.md +98 -97
- package/skills/configure-pg-cache/SKILL.md +6 -5
- package/skills/configure-set-options/SKILL.md +102 -96
- package/skills/handle-cache-errors/SKILL.md +92 -92
- package/skills/index.md +20 -0
- package/skills/observe-cache/SKILL.md +103 -103
- package/skills/pick-cache-driver/SKILL.md +129 -123
- package/skills/test-cache-code/SKILL.md +219 -219
- package/skills/use-cache-atomic/SKILL.md +57 -65
- package/skills/use-cache-bulk/SKILL.md +57 -57
- package/skills/use-cache-list/SKILL.md +85 -85
- package/skills/use-cache-lock/SKILL.md +113 -104
- package/skills/use-cache-namespace/SKILL.md +88 -88
- package/skills/use-cache-similarity/SKILL.md +94 -94
- package/skills/use-cache-tags/SKILL.md +7 -4
- package/skills/use-cache-update-merge/SKILL.md +84 -84
- package/skills/use-cache-utils/SKILL.md +89 -89
- package/skills/use-cached-hof/SKILL.md +100 -99
- package/skills/use-swr/SKILL.md +104 -104
|
@@ -1,123 +1,129 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pick-cache-driver
|
|
3
|
-
description:
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Cache drivers — pick the right one
|
|
7
|
-
|
|
8
|
-
Seven production drivers + a mock driver ship in-box. Pick by durability, scope, and workload.
|
|
9
|
-
|
|
10
|
-
| Driver | Process scope | Persists on restart | Good for | Avoid when |
|
|
11
|
-
| --- | --- | --- | --- | --- |
|
|
12
|
-
| `null` | — | — | Disabling cache in tests; feature-flagging off | You actually want caching |
|
|
13
|
-
| `memory` | Single process | No | Hot in-process data with default TTL; smallest latency | Multi-process / multi-node |
|
|
14
|
-
| `memoryExtended` | Single process | No | Sliding-window TTL (TTL resets on every read) | Any multi-process deploy |
|
|
15
|
-
| `lru` | Single process | No | Bounded in-memory caches (capacity-based eviction) | Need cross-process sharing |
|
|
16
|
-
| `file` | Single host | Yes | Build artefacts, local dev persistence across restarts | Concurrency (no locks); multi-host |
|
|
17
|
-
| `redis` | Shared | Yes (Redis-managed) | Anything shared across processes / nodes | Single-process-only workload — overkill |
|
|
18
|
-
| `pg` | Shared | Yes (Postgres-managed) | You already run Postgres; semantic caching / RAG via pgvector | High-throughput hot reads (Redis is faster) |
|
|
19
|
-
|
|
20
|
-
## Capability matrix
|
|
21
|
-
|
|
22
|
-
| Capability | null | memory | memoryExt | lru | file | redis | pg |
|
|
23
|
-
| --- | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
|
24
|
-
| `set` / `get` / `remove` / `flush` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
25
|
-
| TTL (number or string) | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
26
|
-
| Sliding TTL on read | — | — | ✓ | — | — | — | — |
|
|
27
|
-
| `removeNamespace` | noop | ✓ | ✓ | ✓ (prefix-scan) | ✓ | ✓ | ✓ (LIKE prefix) |
|
|
28
|
-
| `onConflict: "create"` / `"update"` | noop | emulated | emulated | emulated | emulated | native `NX`/`XX` | native (INSERT ON CONFLICT) |
|
|
29
|
-
|
|
|
30
|
-
| `update()` / `merge()` | ✓ |
|
|
31
|
-
| List sub-API | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (O(n) JSON blob today; native LPUSH/LRANGE in v2.1) | ✓ |
|
|
32
|
-
| Tagged invalidation | noop | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (
|
|
33
|
-
| `similar()` / `set({ vector })` | returns `[]` / noop | ✓ brute force | ✓ brute force | ✓ brute force | ✗ throws | ✗ throws (Phase 2 backlog) | ✓ (with `vector` config — pgvector) |
|
|
34
|
-
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
1
|
+
---
|
|
2
|
+
name: pick-cache-driver
|
|
3
|
+
description: 'Pick a cache driver — null / memory / memoryExtended / lru / file / redis / pg / mock — and configure it. Triggers: `cache.setCacheConfigurations`, `BaseCacheDriver`, `cache.use`, `cache.load`, `cache.driver`, `globalPrefix`; "which cache driver should I use", "configure redis driver", "register custom cache driver", "multi-tenant scoping"; typical import `import { cache, BaseCacheDriver } from "@warlock.js/cache"`. Skip: cache CRUD — `@warlock.js/cache/cache-basics/SKILL.md`; pg setup — `@warlock.js/cache/configure-pg-cache/SKILL.md`; competing libs `lru-cache`, `node-cache`, `keyv`, `ioredis`; native `Map`.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cache drivers — pick the right one
|
|
7
|
+
|
|
8
|
+
Seven production drivers + a mock driver ship in-box. Pick by durability, scope, and workload.
|
|
9
|
+
|
|
10
|
+
| Driver | Process scope | Persists on restart | Good for | Avoid when |
|
|
11
|
+
| --- | --- | --- | --- | --- |
|
|
12
|
+
| `null` | — | — | Disabling cache in tests; feature-flagging off | You actually want caching |
|
|
13
|
+
| `memory` | Single process | No | Hot in-process data with default TTL; smallest latency | Multi-process / multi-node |
|
|
14
|
+
| `memoryExtended` | Single process | No | Sliding-window TTL (TTL resets on every read) | Any multi-process deploy |
|
|
15
|
+
| `lru` | Single process | No | Bounded in-memory caches (capacity-based eviction) | Need cross-process sharing |
|
|
16
|
+
| `file` | Single host | Yes | Build artefacts, local dev persistence across restarts | Concurrency (no locks); multi-host |
|
|
17
|
+
| `redis` | Shared | Yes (Redis-managed) | Anything shared across processes / nodes | Single-process-only workload — overkill |
|
|
18
|
+
| `pg` | Shared | Yes (Postgres-managed) | You already run Postgres; semantic caching / RAG via pgvector | High-throughput hot reads (Redis is faster) |
|
|
19
|
+
|
|
20
|
+
## Capability matrix
|
|
21
|
+
|
|
22
|
+
| Capability | null | memory | memoryExt | lru | file | redis | pg |
|
|
23
|
+
| --- | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
|
24
|
+
| `set` / `get` / `remove` / `flush` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
25
|
+
| TTL (number or string; numeric strings = seconds) | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
26
|
+
| Sliding TTL on read | — | — | ✓ | — | — | — | — |
|
|
27
|
+
| `removeNamespace` | noop | ✓ | ✓ | ✓ (prefix-scan) | ✓ | ✓ | ✓ (LIKE prefix) |
|
|
28
|
+
| `onConflict: "create"` / `"update"` | noop | emulated | emulated | emulated | emulated | native `NX`/`XX` | native (INSERT ON CONFLICT) |
|
|
29
|
+
| `increment` / `decrement` / `pull` | — | one process | one process | one process | one process | **cross-server** | **cross-server** |
|
|
30
|
+
| `update()` / `merge()` | ✓ | one process | one process | one process | ✗ throws | **cross-server** (compare-and-set) | **cross-server** (compare-and-set) |
|
|
31
|
+
| List sub-API | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (O(n) JSON blob today; native LPUSH/LRANGE in v2.1) | ✓ |
|
|
32
|
+
| Tagged invalidation | noop | ✓ | ✓ | ✓ | ✓ (serialized index) | ✓ (native SET) | ✓ (JSON-array merge) |
|
|
33
|
+
| `similar()` / `set({ vector })` | returns `[]` / noop | ✓ brute force | ✓ brute force | ✓ brute force | ✗ throws | ✗ throws (Phase 2 backlog) | ✓ (with `vector` config — pgvector) |
|
|
34
|
+
|
|
35
|
+
## Multi-server: use redis or pg
|
|
36
|
+
|
|
37
|
+
Memory, memoryExtended, lru and file caches are **per-process** (file: per host). With more than one server, use `redis` or `pg` for counters, throttles, one-time tokens (`pull`), shared `update`, `lock()` and tag invalidation; on memory each server counts and invalidates alone. `remember()`/`swr()`/`cached()` stampede protection is per process even on redis. Redis Cluster is unsupported.
|
|
38
|
+
|
|
39
|
+
Driver storage notes: `memory` stores flat keys (`get("users")` after `set("users.1")` is a miss) and `maxSize` counts entries in LRU order; redis `flush()` without a `globalPrefix` runs `FLUSHDB` (whole database), so set a prefix.
|
|
40
|
+
|
|
41
|
+
## Global config TTL — accepts number or string
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
options: {
|
|
45
|
+
redis: { url: "...", ttl: "7d" }, // string OK
|
|
46
|
+
memory: { ttl: 3600 }, // number OK
|
|
47
|
+
lru: { capacity: 10_000 }, // LRU has no TTL option today
|
|
48
|
+
file: { directory: () => "/var/cache/myapp", ttl: "1h" },
|
|
49
|
+
pg: { client: pool, ttl: "1h" }, // KV-only
|
|
50
|
+
// pg with pgvector:
|
|
51
|
+
// pg: { client: pool, vector: { dimensions: 1536, index: "hnsw" } },
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Global prefix (multi-tenant scoping)
|
|
56
|
+
|
|
57
|
+
Every driver accepts `globalPrefix: string | (() => string)`. The function form runs per call — pair it with request-local async context to scope every cached key to the current tenant / user / client automatically:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
options: {
|
|
61
|
+
redis: {
|
|
62
|
+
url: "...",
|
|
63
|
+
globalPrefix: () => `tenant-${currentContext.tenantId}`,
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Key & namespace safety (file / redis)
|
|
69
|
+
|
|
70
|
+
Cache keys can carry untrusted input (e.g. a `cached()` auto-key derived from a request param), so every built-in driver treats a key as data, never as a path or a query fragment:
|
|
71
|
+
|
|
72
|
+
- **`file`** — a key/namespace maps to exactly one on-disk directory component: `%`, `/`, and `\` are percent-encoded before the path is built, so `../../etc` becomes an inert directory name instead of a traversal. The resolved path is also asserted to stay inside the configured cache root (throws `CacheError` otherwise) — a second, encoding-independent check. `removeNamespace` clears both the namespace's own directory and every dotted child (`ns`, `ns.*`), honoring `globalPrefix`.
|
|
73
|
+
- **`redis`** — `removeNamespace` escapes glob metacharacters (`*`, `?`, `[`, `\`) in the namespace before building its match pattern, and walks matches with a `SCAN` cursor (`scanIterator`) instead of the blocking `KEYS` command, so clearing a namespace on a large keyspace doesn't stall the event loop for other tenants.
|
|
74
|
+
- **Errors that echo a connection string** (Redis `connect()` failures, any driver's failed op) are logged through a `safeErrorInfo()` helper that redacts `scheme://user:pass@` credentials to `scheme://[REDACTED]@` and never prints the raw `Error` object — see [`@warlock.js/cache/handle-cache-errors/SKILL.md`](@warlock.js/cache/handle-cache-errors/SKILL.md).
|
|
75
|
+
|
|
76
|
+
## Registering a custom driver
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { BaseCacheDriver, cache } from "@warlock.js/cache";
|
|
80
|
+
|
|
81
|
+
class MemcachedCacheDriver extends BaseCacheDriver<MyClient, MyOptions> {
|
|
82
|
+
public name = "memcached";
|
|
83
|
+
// … implement set / get / remove / flush / removeNamespace / connect
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
cache.setCacheConfigurations({
|
|
87
|
+
default: "memcached",
|
|
88
|
+
drivers: { memcached: MemcachedCacheDriver },
|
|
89
|
+
options: { memcached: { host: "localhost" } },
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Extending `BaseCacheDriver` gives you free: TTL parsing, key parsing, event emission, stampede-safe `remember`, deep-clone-on-read, default `update` / `merge` / `list` implementations.
|
|
94
|
+
|
|
95
|
+
## Runtime driver options — `cache.use(name, options)`
|
|
96
|
+
|
|
97
|
+
Some driver options can only be built at runtime (`pg`'s `client: pg.Pool`, pre-wired clients). Pass them as the second arg to `cache.use` / `cache.load` / `cache.driver` — they merge over `setCacheConfigurations({ options })` per-key, runtime wins.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
101
|
+
|
|
102
|
+
cache.setCacheConfigurations({
|
|
103
|
+
default: "pg",
|
|
104
|
+
drivers: { pg: PgCacheDriver },
|
|
105
|
+
options: { pg: { table: "cache" } }, // static
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await cache.use("pg", { client: pool }); // runtime — skip init() in this case
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Constraints:
|
|
112
|
+
- The driver name must be registered in `setCacheConfigurations({ drivers })` — runtime options don't bypass registration.
|
|
113
|
+
- Once a driver is loaded, calling `use`/`load`/`driver` again with **non-empty** new options throws `CacheConfigurationError`. Register a second driver name if you need a different config.
|
|
114
|
+
- Calling without options (or with `{}`) on an already-loaded driver returns the cached instance silently.
|
|
115
|
+
|
|
116
|
+
## Per-call driver override
|
|
117
|
+
|
|
118
|
+
When most writes go to the default driver but one call needs a different one:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
await cache.set("audit:event", event, { driver: "redis" });
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The manager loads (and connects) the override driver lazily on first use, then routes that single operation through it without mutating `currentDriver`.
|
|
125
|
+
|
|
126
|
+
## See also
|
|
127
|
+
|
|
128
|
+
- [`@warlock.js/cache/configure-pg-cache/SKILL.md`](@warlock.js/cache/configure-pg-cache/SKILL.md) — full pg setup (KV-only and pgvector mode)
|
|
129
|
+
- [`@warlock.js/cache/test-cache-code/SKILL.md`](@warlock.js/cache/test-cache-code/SKILL.md) — `MockCacheDriver` and `NullCacheDriver` for tests
|