@warlock.js/cache 4.0.171 → 4.1.1
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 +85 -0
- package/cjs/index.cjs +4088 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/cache-manager.d.mts +314 -0
- package/esm/cache-manager.d.mts.map +1 -0
- package/esm/cache-manager.mjs +486 -0
- package/esm/cache-manager.mjs.map +1 -0
- package/esm/cached/auto-key.d.mts +25 -0
- package/esm/cached/auto-key.d.mts.map +1 -0
- package/esm/cached/auto-key.mjs +55 -0
- package/esm/cached/auto-key.mjs.map +1 -0
- package/esm/cached/cached.d.mts +54 -0
- package/esm/cached/cached.d.mts.map +1 -0
- package/esm/cached/cached.mjs +25 -0
- package/esm/cached/cached.mjs.map +1 -0
- package/esm/cached/index.d.mts +3 -0
- package/esm/cached/index.mjs +5 -0
- package/esm/cached/normalize-args.d.mts +51 -0
- package/esm/cached/normalize-args.d.mts.map +1 -0
- package/esm/cached/normalize-args.mjs +26 -0
- package/esm/cached/normalize-args.mjs.map +1 -0
- package/esm/drivers/base-cache-driver.d.mts +322 -0
- package/esm/drivers/base-cache-driver.d.mts.map +1 -0
- package/esm/drivers/base-cache-driver.mjs +522 -0
- package/esm/drivers/base-cache-driver.mjs.map +1 -0
- package/esm/drivers/file-cache-driver.d.mts +68 -0
- package/esm/drivers/file-cache-driver.d.mts.map +1 -0
- package/esm/drivers/file-cache-driver.mjs +174 -0
- package/esm/drivers/file-cache-driver.mjs.map +1 -0
- package/esm/drivers/index.d.mts +9 -0
- package/esm/drivers/index.mjs +11 -0
- package/esm/drivers/lru-memory-cache-driver.d.mts +136 -0
- package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -0
- package/esm/drivers/lru-memory-cache-driver.mjs +317 -0
- package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -0
- package/esm/drivers/memory-cache-driver.d.mts +112 -0
- package/esm/drivers/memory-cache-driver.d.mts.map +1 -0
- package/esm/drivers/memory-cache-driver.mjs +241 -0
- package/esm/drivers/memory-cache-driver.mjs.map +1 -0
- package/esm/drivers/memory-extended-cache-driver.d.mts +17 -0
- package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -0
- package/esm/drivers/memory-extended-cache-driver.mjs +34 -0
- package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -0
- package/esm/drivers/mock-cache-driver.d.mts +137 -0
- package/esm/drivers/mock-cache-driver.d.mts.map +1 -0
- package/esm/drivers/mock-cache-driver.mjs +226 -0
- package/esm/drivers/mock-cache-driver.mjs.map +1 -0
- package/esm/drivers/null-cache-driver.d.mts +69 -0
- package/esm/drivers/null-cache-driver.d.mts.map +1 -0
- package/esm/drivers/null-cache-driver.mjs +92 -0
- package/esm/drivers/null-cache-driver.mjs.map +1 -0
- package/esm/drivers/pg-cache-driver.d.mts +148 -0
- package/esm/drivers/pg-cache-driver.d.mts.map +1 -0
- package/esm/drivers/pg-cache-driver.mjs +437 -0
- package/esm/drivers/pg-cache-driver.mjs.map +1 -0
- package/esm/drivers/redis-cache-driver.d.mts +86 -0
- package/esm/drivers/redis-cache-driver.d.mts.map +1 -0
- package/esm/drivers/redis-cache-driver.mjs +312 -0
- package/esm/drivers/redis-cache-driver.mjs.map +1 -0
- package/esm/index.d.mts +21 -0
- package/esm/index.mjs +24 -0
- package/esm/list/index.d.mts +1 -0
- package/esm/list/memory-cache-list.d.mts +77 -0
- package/esm/list/memory-cache-list.d.mts.map +1 -0
- package/esm/list/memory-cache-list.mjs +119 -0
- package/esm/list/memory-cache-list.mjs.map +1 -0
- package/esm/metrics.d.mts +118 -0
- package/esm/metrics.d.mts.map +1 -0
- package/esm/metrics.mjs +197 -0
- package/esm/metrics.mjs.map +1 -0
- package/esm/scoped-cache.d.mts +205 -0
- package/esm/scoped-cache.d.mts.map +1 -0
- package/esm/scoped-cache.mjs +274 -0
- package/esm/scoped-cache.mjs.map +1 -0
- package/esm/tagged-cache.d.mts +89 -0
- package/esm/tagged-cache.d.mts.map +1 -0
- package/esm/tagged-cache.mjs +147 -0
- package/esm/tagged-cache.mjs.map +1 -0
- package/esm/tagged-scoped-cache.d.mts +111 -0
- package/esm/tagged-scoped-cache.d.mts.map +1 -0
- package/esm/tagged-scoped-cache.mjs +142 -0
- package/esm/tagged-scoped-cache.mjs.map +1 -0
- package/esm/types.d.mts +1067 -0
- package/esm/types.d.mts.map +1 -0
- package/esm/types.mjs +62 -0
- package/esm/types.mjs.map +1 -0
- package/esm/utils.d.mts +161 -0
- package/esm/utils.d.mts.map +1 -0
- package/esm/utils.mjs +222 -0
- package/esm/utils.mjs.map +1 -0
- package/llms-full.txt +2071 -0
- package/llms.txt +28 -0
- package/package.json +53 -39
- package/skills/apply-cache-patterns/SKILL.md +97 -0
- package/skills/cache-basics/SKILL.md +121 -0
- package/skills/configure-pg-cache/SKILL.md +115 -0
- package/skills/configure-set-options/SKILL.md +96 -0
- package/skills/handle-cache-errors/SKILL.md +91 -0
- package/skills/observe-cache/SKILL.md +103 -0
- package/skills/overview/SKILL.md +69 -0
- package/skills/pick-cache-driver/SKILL.md +115 -0
- package/skills/test-cache-code/SKILL.md +219 -0
- package/skills/use-cache-atomic/SKILL.md +67 -0
- package/skills/use-cache-bulk/SKILL.md +57 -0
- package/skills/use-cache-list/SKILL.md +85 -0
- package/skills/use-cache-lock/SKILL.md +104 -0
- package/skills/use-cache-namespace/SKILL.md +88 -0
- package/skills/use-cache-similarity/SKILL.md +94 -0
- package/skills/use-cache-tags/SKILL.md +85 -0
- package/skills/use-cache-update-merge/SKILL.md +84 -0
- package/skills/use-cache-utils/SKILL.md +89 -0
- package/skills/use-cached-hof/SKILL.md +102 -0
- package/skills/use-swr/SKILL.md +104 -0
- package/cjs/cache-manager.d.ts +0 -163
- package/cjs/cache-manager.d.ts.map +0 -1
- package/cjs/cache-manager.js +0 -322
- package/cjs/cache-manager.js.map +0 -1
- package/cjs/drivers/base-cache-driver.d.ts +0 -152
- package/cjs/drivers/base-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/base-cache-driver.js +0 -321
- package/cjs/drivers/base-cache-driver.js.map +0 -1
- package/cjs/drivers/file-cache-driver.d.ts +0 -45
- package/cjs/drivers/file-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/file-cache-driver.js +0 -133
- package/cjs/drivers/file-cache-driver.js.map +0 -1
- package/cjs/drivers/index.d.ts +0 -8
- package/cjs/drivers/index.d.ts.map +0 -1
- package/cjs/drivers/lru-memory-cache-driver.d.ts +0 -98
- package/cjs/drivers/lru-memory-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/lru-memory-cache-driver.js +0 -252
- package/cjs/drivers/lru-memory-cache-driver.js.map +0 -1
- package/cjs/drivers/memory-cache-driver.d.ts +0 -82
- package/cjs/drivers/memory-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/memory-cache-driver.js +0 -218
- package/cjs/drivers/memory-cache-driver.js.map +0 -1
- package/cjs/drivers/memory-extended-cache-driver.d.ts +0 -13
- package/cjs/drivers/memory-extended-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/memory-extended-cache-driver.js +0 -25
- package/cjs/drivers/memory-extended-cache-driver.js.map +0 -1
- package/cjs/drivers/null-cache-driver.d.ts +0 -58
- package/cjs/drivers/null-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/null-cache-driver.js +0 -84
- package/cjs/drivers/null-cache-driver.js.map +0 -1
- package/cjs/drivers/redis-cache-driver.d.ts +0 -57
- package/cjs/drivers/redis-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/redis-cache-driver.js +0 -263
- package/cjs/drivers/redis-cache-driver.js.map +0 -1
- package/cjs/index.d.ts +0 -6
- package/cjs/index.d.ts.map +0 -1
- package/cjs/index.js +0 -1
- package/cjs/index.js.map +0 -1
- package/cjs/tagged-cache.d.ts +0 -77
- package/cjs/tagged-cache.d.ts.map +0 -1
- package/cjs/tagged-cache.js +0 -160
- package/cjs/tagged-cache.js.map +0 -1
- package/cjs/types.d.ts +0 -391
- package/cjs/types.d.ts.map +0 -1
- package/cjs/types.js +0 -36
- package/cjs/types.js.map +0 -1
- package/cjs/utils.d.ts +0 -50
- package/cjs/utils.d.ts.map +0 -1
- package/cjs/utils.js +0 -55
- package/cjs/utils.js.map +0 -1
- package/esm/cache-manager.d.ts +0 -163
- package/esm/cache-manager.d.ts.map +0 -1
- package/esm/cache-manager.js +0 -322
- package/esm/cache-manager.js.map +0 -1
- package/esm/drivers/base-cache-driver.d.ts +0 -152
- package/esm/drivers/base-cache-driver.d.ts.map +0 -1
- package/esm/drivers/base-cache-driver.js +0 -321
- package/esm/drivers/base-cache-driver.js.map +0 -1
- package/esm/drivers/file-cache-driver.d.ts +0 -45
- package/esm/drivers/file-cache-driver.d.ts.map +0 -1
- package/esm/drivers/file-cache-driver.js +0 -133
- package/esm/drivers/file-cache-driver.js.map +0 -1
- package/esm/drivers/index.d.ts +0 -8
- package/esm/drivers/index.d.ts.map +0 -1
- package/esm/drivers/lru-memory-cache-driver.d.ts +0 -98
- package/esm/drivers/lru-memory-cache-driver.d.ts.map +0 -1
- package/esm/drivers/lru-memory-cache-driver.js +0 -252
- package/esm/drivers/lru-memory-cache-driver.js.map +0 -1
- package/esm/drivers/memory-cache-driver.d.ts +0 -82
- package/esm/drivers/memory-cache-driver.d.ts.map +0 -1
- package/esm/drivers/memory-cache-driver.js +0 -218
- package/esm/drivers/memory-cache-driver.js.map +0 -1
- package/esm/drivers/memory-extended-cache-driver.d.ts +0 -13
- package/esm/drivers/memory-extended-cache-driver.d.ts.map +0 -1
- package/esm/drivers/memory-extended-cache-driver.js +0 -25
- package/esm/drivers/memory-extended-cache-driver.js.map +0 -1
- package/esm/drivers/null-cache-driver.d.ts +0 -58
- package/esm/drivers/null-cache-driver.d.ts.map +0 -1
- package/esm/drivers/null-cache-driver.js +0 -84
- package/esm/drivers/null-cache-driver.js.map +0 -1
- package/esm/drivers/redis-cache-driver.d.ts +0 -57
- package/esm/drivers/redis-cache-driver.d.ts.map +0 -1
- package/esm/drivers/redis-cache-driver.js +0 -263
- package/esm/drivers/redis-cache-driver.js.map +0 -1
- package/esm/index.d.ts +0 -6
- package/esm/index.d.ts.map +0 -1
- package/esm/index.js +0 -1
- package/esm/index.js.map +0 -1
- package/esm/tagged-cache.d.ts +0 -77
- package/esm/tagged-cache.d.ts.map +0 -1
- package/esm/tagged-cache.js +0 -160
- package/esm/tagged-cache.js.map +0 -1
- package/esm/types.d.ts +0 -391
- package/esm/types.d.ts.map +0 -1
- package/esm/types.js +0 -36
- package/esm/types.js.map +0 -1
- package/esm/utils.d.ts +0 -50
- package/esm/utils.d.ts.map +0 -1
- package/esm/utils.js +0 -55
- package/esm/utils.js.map +0 -1
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { mergeTagSets, normalizeToOptions, normalizeToRememberOptions, parseCacheKey, parseTtl } from "./utils.mjs";
|
|
2
|
+
import { TaggedScopedCache } from "./tagged-scoped-cache.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../../@warlock.js/cache/src/scoped-cache.ts
|
|
5
|
+
/**
|
|
6
|
+
* Scoped view over a cache source. Returned by `cache.namespace(prefix, options?)`.
|
|
7
|
+
*
|
|
8
|
+
* **Role.** A `ScopedCache` is a stateless wrapper that prepends a fixed
|
|
9
|
+
* prefix to every key and applies optional default `ttl` / `tags` to every
|
|
10
|
+
* write. Stores nothing itself — every call forwards to the underlying
|
|
11
|
+
* `source` (typically the `CacheManager`, but any `CacheDriver` works).
|
|
12
|
+
*
|
|
13
|
+
* **Responsibility.**
|
|
14
|
+
* - Owns: prefix-prepending of keys, normalization of nested-scope prefixes,
|
|
15
|
+
* merging scope defaults into write options (`ttl`, `tags`), filtering
|
|
16
|
+
* `similar()` hits to its own scope, and exposing `.clear()` as a sugar
|
|
17
|
+
* for `removeNamespace(prefix)`.
|
|
18
|
+
* - Does NOT own: actual storage, connection lifecycle, event listeners,
|
|
19
|
+
* driver selection, or tag-index bookkeeping (delegated to the source's
|
|
20
|
+
* tagged-cache machinery).
|
|
21
|
+
*
|
|
22
|
+
* Per-call options always win over scope defaults; tags merge additively
|
|
23
|
+
* across (scope defaults + per-call) layers. Nested scopes inherit and may
|
|
24
|
+
* override the parent's defaults — see {@link ScopedCache.namespace}.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const chat = cache.namespace(`chats.${id}`, { ttl: "30d" });
|
|
28
|
+
*
|
|
29
|
+
* await chat.set("messages.10", msg); // 30d default
|
|
30
|
+
* await chat.set("draft", d, { ttl: "1h" }); // per-call override
|
|
31
|
+
* await chat.namespace("typing", { ttl: "5s" }).set("user.42", true);
|
|
32
|
+
* await chat.clear();
|
|
33
|
+
*/
|
|
34
|
+
var ScopedCache = class ScopedCache {
|
|
35
|
+
/**
|
|
36
|
+
* Build a scope. Constructed via `cache.namespace(prefix, options)` —
|
|
37
|
+
* users never call this directly.
|
|
38
|
+
*/
|
|
39
|
+
constructor(source, prefix, defaults = {}) {
|
|
40
|
+
this.source = source;
|
|
41
|
+
this.prefix = parseCacheKey(prefix);
|
|
42
|
+
this.defaults = {
|
|
43
|
+
ttl: defaults.ttl,
|
|
44
|
+
tags: defaults.tags && defaults.tags.length > 0 ? [...defaults.tags] : void 0
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build a nested scope. The child's prefix is `parent.child`; child options
|
|
49
|
+
* override the parent's `ttl` and union into `tags`.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const chat = cache.namespace("chats.10", { ttl: "30d" });
|
|
53
|
+
* const typing = chat.namespace("typing", { ttl: "5s" });
|
|
54
|
+
* // typing.prefix === "chats.10.typing"
|
|
55
|
+
*/
|
|
56
|
+
namespace(prefix, options = {}) {
|
|
57
|
+
const childPrefix = `${this.prefix}.${parseCacheKey(prefix)}`;
|
|
58
|
+
return new ScopedCache(this.source, childPrefix, {
|
|
59
|
+
ttl: options.ttl ?? this.defaults.ttl,
|
|
60
|
+
tags: mergeTagSets(this.defaults.tags, options.tags)
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Return a one-shot tagged write handle. The handle's tags merge additively
|
|
65
|
+
* with scope-level defaults — final tag list per write is the union of
|
|
66
|
+
* (scope tags + handle tags + per-call tags), deduped.
|
|
67
|
+
*/
|
|
68
|
+
tags(tags) {
|
|
69
|
+
return new TaggedScopedCache(this, tags);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Wipe every entry under this scope's prefix. Sugar over
|
|
73
|
+
* `source.removeNamespace(prefix)` — siblings outside the scope are
|
|
74
|
+
* untouched.
|
|
75
|
+
*/
|
|
76
|
+
clear() {
|
|
77
|
+
return this.source.removeNamespace(this.prefix);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Read the value at the scoped key. Forwards to the source after prefixing.
|
|
81
|
+
*/
|
|
82
|
+
get(key) {
|
|
83
|
+
return this.source.get(this.scopedKey(key));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check presence of the scoped key without fetching the value.
|
|
87
|
+
*/
|
|
88
|
+
has(key) {
|
|
89
|
+
return this.source.has(this.scopedKey(key));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Batch-read scoped keys. Each input key is prefixed before forwarding.
|
|
93
|
+
*/
|
|
94
|
+
many(keys) {
|
|
95
|
+
return this.source.many(keys.map((key) => this.scopedKey(key)));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Read-and-remove. Returns the value or `null`; the entry is gone after.
|
|
99
|
+
*/
|
|
100
|
+
pull(key) {
|
|
101
|
+
return this.source.pull(this.scopedKey(key));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Write the scoped key. Per-call `ttl`/`tags` win over scope defaults;
|
|
105
|
+
* `expiresAt` is preserved as-is (absolute deadlines are never overridden
|
|
106
|
+
* by the scope's relative-ttl default).
|
|
107
|
+
*/
|
|
108
|
+
set(key, value, ttlOrOptions) {
|
|
109
|
+
return this.source.set(this.scopedKey(key), value, this.mergeSetOptions(ttlOrOptions));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Batch-write under the scope. Caller's positional `ttl` wins; otherwise
|
|
113
|
+
* the scope default is parsed to seconds (since `setMany` accepts only a
|
|
114
|
+
* numeric ttl).
|
|
115
|
+
*/
|
|
116
|
+
setMany(items, ttl) {
|
|
117
|
+
const scoped = {};
|
|
118
|
+
for (const [key, value] of Object.entries(items)) scoped[this.scopedKey(key)] = value;
|
|
119
|
+
return this.source.setMany(scoped, ttl ?? this.scopeTtlSeconds());
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Atomic create-or-skip on the scoped key. Throws when the underlying
|
|
123
|
+
* source has no `setNX` (driver-specific — Redis-only today).
|
|
124
|
+
*/
|
|
125
|
+
setNX(key, value, ttl) {
|
|
126
|
+
if (!this.source.setNX) throw new Error(`setNX is not supported by the underlying cache source: ${this.source.name}`);
|
|
127
|
+
return this.source.setNX(this.scopedKey(key), value, ttl ?? this.scopeTtlSeconds());
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Permanent write (no expiration). Bypasses the scope's `ttl` default —
|
|
131
|
+
* `forever` always means forever, regardless of scope policy.
|
|
132
|
+
*/
|
|
133
|
+
forever(key, value) {
|
|
134
|
+
return this.source.forever(this.scopedKey(key), value);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Remove a single scoped key.
|
|
138
|
+
*/
|
|
139
|
+
remove(key) {
|
|
140
|
+
return this.source.remove(this.scopedKey(key));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Read-or-compute. Cache-miss writes pick up the scope's default `ttl`
|
|
144
|
+
* and `tags` unless the caller passed an options object that overrides.
|
|
145
|
+
*/
|
|
146
|
+
remember(key, ttlOrOptions, callback) {
|
|
147
|
+
return this.source.remember(this.scopedKey(key), this.mergeRememberOptions(ttlOrOptions), callback);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Stale-while-revalidate on the scoped key. Scope-level `tags` merge
|
|
151
|
+
* additively with `options.tags`; `freshTtl`/`staleTtl` always come from
|
|
152
|
+
* the caller (no scope-default precedence — the SWR shape is too
|
|
153
|
+
* specific to the call site to inherit).
|
|
154
|
+
*/
|
|
155
|
+
swr(key, options, callback) {
|
|
156
|
+
const merged = {
|
|
157
|
+
...options,
|
|
158
|
+
tags: mergeTagSets(this.defaults.tags, options.tags)
|
|
159
|
+
};
|
|
160
|
+
return this.source.swr(this.scopedKey(key), merged, callback);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Atomic counter increment on the scoped key. TTL is preserved by the
|
|
164
|
+
* underlying driver — scope ttl is only applied on first write via `set`.
|
|
165
|
+
*/
|
|
166
|
+
increment(key, value) {
|
|
167
|
+
return this.source.increment(this.scopedKey(key), value);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Atomic counter decrement on the scoped key. See {@link increment} for
|
|
171
|
+
* TTL semantics.
|
|
172
|
+
*/
|
|
173
|
+
decrement(key, value) {
|
|
174
|
+
return this.source.decrement(this.scopedKey(key), value);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Atomic read-modify-write. Falls back to the scope's `ttl` when the caller
|
|
178
|
+
* doesn't provide one; the source still keeps the existing entry's TTL on
|
|
179
|
+
* an update unless `options.ttl` is explicitly set.
|
|
180
|
+
*/
|
|
181
|
+
update(key, fn, options) {
|
|
182
|
+
return this.source.update(this.scopedKey(key), fn, { ttl: options?.ttl ?? this.defaults.ttl });
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Shallow-merge a partial object into the scoped entry. Same TTL semantics
|
|
186
|
+
* as {@link update}.
|
|
187
|
+
*/
|
|
188
|
+
merge(key, partial, options) {
|
|
189
|
+
return this.source.merge(this.scopedKey(key), partial, { ttl: options?.ttl ?? this.defaults.ttl });
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Return a list accessor bound to the scoped key. The accessor itself
|
|
193
|
+
* does its own read-mutate-write under the prefixed entry.
|
|
194
|
+
*/
|
|
195
|
+
list(key) {
|
|
196
|
+
return this.source.list(this.scopedKey(key));
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Acquire a distributed lock on the scoped key. Caller's TTL wins; when
|
|
200
|
+
* the options form omits `ttl`, the scope default fills in.
|
|
201
|
+
*/
|
|
202
|
+
lock(key, ttlOrOptions, fn) {
|
|
203
|
+
if (typeof ttlOrOptions === "object" && ttlOrOptions !== null) {
|
|
204
|
+
const merged = {
|
|
205
|
+
...ttlOrOptions,
|
|
206
|
+
ttl: ttlOrOptions.ttl ?? this.defaults.ttl ?? ttlOrOptions.ttl
|
|
207
|
+
};
|
|
208
|
+
return this.source.lock(this.scopedKey(key), merged, fn);
|
|
209
|
+
}
|
|
210
|
+
return this.source.lock(this.scopedKey(key), ttlOrOptions, fn);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Similarity retrieval, scope-isolated. Hits whose keys fall outside this
|
|
214
|
+
* scope are filtered out before the result is returned. `topK` applies to
|
|
215
|
+
* the underlying retrieval — when the scope contains fewer than `topK`
|
|
216
|
+
* matches but other scopes do, the caller will see fewer hits than `topK`.
|
|
217
|
+
*/
|
|
218
|
+
async similar(vector, options) {
|
|
219
|
+
const hits = await this.source.similar(vector, options);
|
|
220
|
+
const parsedPrefix = this.source.parseKey(this.prefix);
|
|
221
|
+
return hits.filter((hit) => hit.key === parsedPrefix || hit.key.startsWith(parsedPrefix + "."));
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Build the source-side key by prepending the scope prefix. Object keys
|
|
225
|
+
* are normalized via {@link parseCacheKey} first so they compose with the
|
|
226
|
+
* prefix as plain dot-strings.
|
|
227
|
+
*/
|
|
228
|
+
scopedKey(key) {
|
|
229
|
+
const keyString = typeof key === "string" ? key : parseCacheKey(key);
|
|
230
|
+
if (!keyString) return this.prefix;
|
|
231
|
+
return `${this.prefix}.${keyString}`;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Coerce the polymorphic 3rd `set` argument into a {@link CacheSetOptions}
|
|
235
|
+
* with scope defaults filled in. Per-call values always win; tags merge
|
|
236
|
+
* additively. `expiresAt` is preserved without injecting the scope's `ttl`
|
|
237
|
+
* default (absolute deadlines override relative ones).
|
|
238
|
+
*/
|
|
239
|
+
mergeSetOptions(input) {
|
|
240
|
+
const options = normalizeToOptions(input);
|
|
241
|
+
const ttl = options.ttl ?? (options.expiresAt === void 0 ? this.defaults.ttl : void 0);
|
|
242
|
+
const tags = mergeTagSets(this.defaults.tags, options.tags);
|
|
243
|
+
const merged = { ...options };
|
|
244
|
+
if (ttl !== void 0) merged.ttl = ttl;
|
|
245
|
+
if (tags !== void 0) merged.tags = tags;
|
|
246
|
+
return merged;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Same merge as {@link mergeSetOptions} but for the `remember()` shape
|
|
250
|
+
* ({@link RememberOptions} — no `expiresAt`).
|
|
251
|
+
*/
|
|
252
|
+
mergeRememberOptions(input) {
|
|
253
|
+
const options = normalizeToRememberOptions(input);
|
|
254
|
+
const ttl = options.ttl ?? this.defaults.ttl;
|
|
255
|
+
const tags = mergeTagSets(this.defaults.tags, options.tags);
|
|
256
|
+
const merged = { ...options };
|
|
257
|
+
if (ttl !== void 0) merged.ttl = ttl;
|
|
258
|
+
if (tags !== void 0) merged.tags = tags;
|
|
259
|
+
return merged;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Convert the scope's default `ttl` (which may be a duration string) into
|
|
263
|
+
* seconds, for the few methods (`setMany`, `setNX`) that accept only a
|
|
264
|
+
* numeric ttl.
|
|
265
|
+
*/
|
|
266
|
+
scopeTtlSeconds() {
|
|
267
|
+
if (this.defaults.ttl === void 0) return;
|
|
268
|
+
return parseTtl(this.defaults.ttl);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
//#endregion
|
|
273
|
+
export { ScopedCache };
|
|
274
|
+
//# sourceMappingURL=scoped-cache.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoped-cache.mjs","names":[],"sources":["../../../../../@warlock.js/cache/src/scoped-cache.ts"],"sourcesContent":["import { TaggedScopedCache } from \"./tagged-scoped-cache\";\nimport type {\n CacheDriver,\n CacheKey,\n CacheListAccessor,\n CacheNamespaceOptions,\n CacheSetOptions,\n CacheSimilarHit,\n CacheSimilarOptions,\n CacheSwrOptions,\n CacheTtl,\n LockOptions,\n LockOutcome,\n RememberOptions,\n ScopedCacheContract,\n TaggedScopedCacheContract,\n} from \"./types\";\nimport {\n mergeTagSets,\n normalizeToOptions,\n normalizeToRememberOptions,\n parseCacheKey,\n parseTtl,\n} from \"./utils\";\n\n/**\n * Scoped view over a cache source. Returned by `cache.namespace(prefix, options?)`.\n *\n * **Role.** A `ScopedCache` is a stateless wrapper that prepends a fixed\n * prefix to every key and applies optional default `ttl` / `tags` to every\n * write. Stores nothing itself — every call forwards to the underlying\n * `source` (typically the `CacheManager`, but any `CacheDriver` works).\n *\n * **Responsibility.**\n * - Owns: prefix-prepending of keys, normalization of nested-scope prefixes,\n * merging scope defaults into write options (`ttl`, `tags`), filtering\n * `similar()` hits to its own scope, and exposing `.clear()` as a sugar\n * for `removeNamespace(prefix)`.\n * - Does NOT own: actual storage, connection lifecycle, event listeners,\n * driver selection, or tag-index bookkeeping (delegated to the source's\n * tagged-cache machinery).\n *\n * Per-call options always win over scope defaults; tags merge additively\n * across (scope defaults + per-call) layers. Nested scopes inherit and may\n * override the parent's defaults — see {@link ScopedCache.namespace}.\n *\n * @example\n * const chat = cache.namespace(`chats.${id}`, { ttl: \"30d\" });\n *\n * await chat.set(\"messages.10\", msg); // 30d default\n * await chat.set(\"draft\", d, { ttl: \"1h\" }); // per-call override\n * await chat.namespace(\"typing\", { ttl: \"5s\" }).set(\"user.42\", true);\n * await chat.clear();\n */\nexport class ScopedCache implements ScopedCacheContract {\n /**\n * Fully-qualified prefix prepended to every key handled by this scope.\n * Normalized through {@link parseCacheKey} on construction so colon-form\n * input (`\"chats:10\"`) and trailing dots compose cleanly with nested scopes.\n */\n public readonly prefix: string;\n\n /**\n * Underlying cache source. Public-readonly so co-located helpers\n * (`TaggedScopedCache`) can delegate without ceremony — not part of the\n * stable consumer API.\n *\n * @internal\n */\n public readonly source: CacheDriver<any, any>;\n\n /**\n * Defaults applied to every write through this scope. Per-call options\n * override `ttl`; `tags` merge additively across layers.\n *\n * @internal\n */\n public readonly defaults: CacheNamespaceOptions;\n\n /**\n * Build a scope. Constructed via `cache.namespace(prefix, options)` —\n * users never call this directly.\n */\n public constructor(\n source: CacheDriver<any, any>,\n prefix: string,\n defaults: CacheNamespaceOptions = {},\n ) {\n this.source = source;\n this.prefix = parseCacheKey(prefix);\n this.defaults = {\n ttl: defaults.ttl,\n tags: defaults.tags && defaults.tags.length > 0 ? [...defaults.tags] : undefined,\n };\n }\n\n /**\n * Build a nested scope. The child's prefix is `parent.child`; child options\n * override the parent's `ttl` and union into `tags`.\n *\n * @example\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\n * const typing = chat.namespace(\"typing\", { ttl: \"5s\" });\n * // typing.prefix === \"chats.10.typing\"\n */\n public namespace(prefix: string, options: CacheNamespaceOptions = {}): ScopedCacheContract {\n const childPrefix = `${this.prefix}.${parseCacheKey(prefix)}`;\n\n return new ScopedCache(this.source, childPrefix, {\n ttl: options.ttl ?? this.defaults.ttl,\n tags: mergeTagSets(this.defaults.tags, options.tags),\n });\n }\n\n /**\n * Return a one-shot tagged write handle. The handle's tags merge additively\n * with scope-level defaults — final tag list per write is the union of\n * (scope tags + handle tags + per-call tags), deduped.\n */\n public tags(tags: string[]): TaggedScopedCacheContract {\n return new TaggedScopedCache(this, tags);\n }\n\n /**\n * Wipe every entry under this scope's prefix. Sugar over\n * `source.removeNamespace(prefix)` — siblings outside the scope are\n * untouched.\n */\n public clear(): Promise<void> {\n return this.source.removeNamespace(this.prefix);\n }\n\n /**\n * Read the value at the scoped key. Forwards to the source after prefixing.\n */\n public get<T = any>(key: CacheKey): Promise<T | null> {\n return this.source.get<T>(this.scopedKey(key));\n }\n\n /**\n * Check presence of the scoped key without fetching the value.\n */\n public has(key: CacheKey): Promise<boolean> {\n return this.source.has(this.scopedKey(key));\n }\n\n /**\n * Batch-read scoped keys. Each input key is prefixed before forwarding.\n */\n public many(keys: CacheKey[]): Promise<any[]> {\n return this.source.many(keys.map((key) => this.scopedKey(key)));\n }\n\n /**\n * Read-and-remove. Returns the value or `null`; the entry is gone after.\n */\n public pull<T = any>(key: CacheKey): Promise<T | null> {\n return this.source.pull<T>(this.scopedKey(key));\n }\n\n /**\n * Write the scoped key. Per-call `ttl`/`tags` win over scope defaults;\n * `expiresAt` is preserved as-is (absolute deadlines are never overridden\n * by the scope's relative-ttl default).\n */\n public set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n return this.source.set(this.scopedKey(key), value, this.mergeSetOptions(ttlOrOptions));\n }\n\n /**\n * Batch-write under the scope. Caller's positional `ttl` wins; otherwise\n * the scope default is parsed to seconds (since `setMany` accepts only a\n * numeric ttl).\n */\n public setMany(items: Record<string, any>, ttl?: number): Promise<void> {\n const scoped: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(items)) {\n scoped[this.scopedKey(key)] = value;\n }\n\n return this.source.setMany(scoped, ttl ?? this.scopeTtlSeconds());\n }\n\n /**\n * Atomic create-or-skip on the scoped key. Throws when the underlying\n * source has no `setNX` (driver-specific — Redis-only today).\n */\n public setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\n if (!this.source.setNX) {\n throw new Error(\n `setNX is not supported by the underlying cache source: ${this.source.name}`,\n );\n }\n\n return this.source.setNX(this.scopedKey(key), value, ttl ?? this.scopeTtlSeconds());\n }\n\n /**\n * Permanent write (no expiration). Bypasses the scope's `ttl` default —\n * `forever` always means forever, regardless of scope policy.\n */\n public forever<T = any>(key: CacheKey, value: T): Promise<T> {\n return this.source.forever<T>(this.scopedKey(key), value);\n }\n\n /**\n * Remove a single scoped key.\n */\n public remove(key: CacheKey): Promise<void> {\n return this.source.remove(this.scopedKey(key));\n }\n\n /**\n * Read-or-compute. Cache-miss writes pick up the scope's default `ttl`\n * and `tags` unless the caller passed an options object that overrides.\n */\n public remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n return this.source.remember<T>(\n this.scopedKey(key),\n this.mergeRememberOptions(ttlOrOptions),\n callback,\n );\n }\n\n /**\n * Stale-while-revalidate on the scoped key. Scope-level `tags` merge\n * additively with `options.tags`; `freshTtl`/`staleTtl` always come from\n * the caller (no scope-default precedence — the SWR shape is too\n * specific to the call site to inherit).\n */\n public swr<T = any>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n const merged: CacheSwrOptions = {\n ...options,\n tags: mergeTagSets(this.defaults.tags, options.tags),\n };\n\n return this.source.swr<T>(this.scopedKey(key), merged, callback);\n }\n\n /**\n * Atomic counter increment on the scoped key. TTL is preserved by the\n * underlying driver — scope ttl is only applied on first write via `set`.\n */\n public increment(key: CacheKey, value?: number): Promise<number> {\n return this.source.increment(this.scopedKey(key), value);\n }\n\n /**\n * Atomic counter decrement on the scoped key. See {@link increment} for\n * TTL semantics.\n */\n public decrement(key: CacheKey, value?: number): Promise<number> {\n return this.source.decrement(this.scopedKey(key), value);\n }\n\n /**\n * Atomic read-modify-write. Falls back to the scope's `ttl` when the caller\n * doesn't provide one; the source still keeps the existing entry's TTL on\n * an update unless `options.ttl` is explicitly set.\n */\n public update<T = any>(\n key: CacheKey,\n fn: (current: T | null) => T | null | Promise<T | null>,\n options?: { ttl?: CacheTtl },\n ): Promise<T | null> {\n return this.source.update<T>(this.scopedKey(key), fn, {\n ttl: options?.ttl ?? this.defaults.ttl,\n });\n }\n\n /**\n * Shallow-merge a partial object into the scoped entry. Same TTL semantics\n * as {@link update}.\n */\n public merge<T extends Record<string, any> = Record<string, any>>(\n key: CacheKey,\n partial: Partial<T>,\n options?: { ttl?: CacheTtl },\n ): Promise<T> {\n return this.source.merge<T>(this.scopedKey(key), partial, {\n ttl: options?.ttl ?? this.defaults.ttl,\n });\n }\n\n /**\n * Return a list accessor bound to the scoped key. The accessor itself\n * does its own read-mutate-write under the prefixed entry.\n */\n public list<T = any>(key: CacheKey): CacheListAccessor<T> {\n return this.source.list<T>(this.scopedKey(key));\n }\n\n /**\n * Acquire a distributed lock on the scoped key. Caller's TTL wins; when\n * the options form omits `ttl`, the scope default fills in.\n */\n public lock<T>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\n fn: () => Promise<T>,\n ): Promise<LockOutcome<T>> {\n if (typeof ttlOrOptions === \"object\" && ttlOrOptions !== null) {\n const merged: Omit<LockOptions, \"driver\"> = {\n ...ttlOrOptions,\n ttl: ttlOrOptions.ttl ?? this.defaults.ttl ?? ttlOrOptions.ttl,\n };\n\n // The third coalesce is intentional — when both caller and scope omit\n // the TTL, we still pass the original (undefined) through so the source\n // raises its own validation error rather than us swallowing it silently.\n return this.source.lock<T>(this.scopedKey(key), merged, fn);\n }\n\n return this.source.lock<T>(this.scopedKey(key), ttlOrOptions, fn);\n }\n\n /**\n * Similarity retrieval, scope-isolated. Hits whose keys fall outside this\n * scope are filtered out before the result is returned. `topK` applies to\n * the underlying retrieval — when the scope contains fewer than `topK`\n * matches but other scopes do, the caller will see fewer hits than `topK`.\n */\n public async similar<T = any>(\n vector: number[],\n options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]> {\n const hits = await this.source.similar<T>(vector, options);\n const parsedPrefix = this.source.parseKey(this.prefix);\n\n return hits.filter(\n (hit) => hit.key === parsedPrefix || hit.key.startsWith(parsedPrefix + \".\"),\n );\n }\n\n /**\n * Build the source-side key by prepending the scope prefix. Object keys\n * are normalized via {@link parseCacheKey} first so they compose with the\n * prefix as plain dot-strings.\n */\n protected scopedKey(key: CacheKey): string {\n const keyString = typeof key === \"string\" ? key : parseCacheKey(key);\n\n if (!keyString) {\n return this.prefix;\n }\n\n return `${this.prefix}.${keyString}`;\n }\n\n /**\n * Coerce the polymorphic 3rd `set` argument into a {@link CacheSetOptions}\n * with scope defaults filled in. Per-call values always win; tags merge\n * additively. `expiresAt` is preserved without injecting the scope's `ttl`\n * default (absolute deadlines override relative ones).\n */\n protected mergeSetOptions(\n input?: CacheTtl | CacheSetOptions,\n ): CacheSetOptions | undefined {\n const options = normalizeToOptions(input);\n const ttl =\n options.ttl ?? (options.expiresAt === undefined ? this.defaults.ttl : undefined);\n const tags = mergeTagSets(this.defaults.tags, options.tags);\n\n const merged: CacheSetOptions = { ...options };\n\n if (ttl !== undefined) {\n merged.ttl = ttl;\n }\n\n if (tags !== undefined) {\n merged.tags = tags;\n }\n\n return merged;\n }\n\n /**\n * Same merge as {@link mergeSetOptions} but for the `remember()` shape\n * ({@link RememberOptions} — no `expiresAt`).\n */\n protected mergeRememberOptions(\n input: CacheTtl | RememberOptions,\n ): CacheTtl | RememberOptions {\n const options = normalizeToRememberOptions(input);\n const ttl = options.ttl ?? this.defaults.ttl;\n const tags = mergeTagSets(this.defaults.tags, options.tags);\n\n const merged: RememberOptions = { ...options };\n\n if (ttl !== undefined) {\n merged.ttl = ttl;\n }\n\n if (tags !== undefined) {\n merged.tags = tags;\n }\n\n return merged;\n }\n\n /**\n * Convert the scope's default `ttl` (which may be a duration string) into\n * seconds, for the few methods (`setMany`, `setNX`) that accept only a\n * numeric ttl.\n */\n protected scopeTtlSeconds(): number | undefined {\n if (this.defaults.ttl === undefined) {\n return undefined;\n }\n\n return parseTtl(this.defaults.ttl);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { CacheDriver, CacheKey, CacheSetOptions, CacheTtl, TaggedCacheDriver } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../../@warlock.js/cache/src/tagged-cache.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Tagged Cache Wrapper
|
|
6
|
+
* Wraps a cache driver to automatically manage tag relationships
|
|
7
|
+
*/
|
|
8
|
+
declare class TaggedCache implements TaggedCacheDriver {
|
|
9
|
+
/**
|
|
10
|
+
* The tags associated with this tagged cache instance
|
|
11
|
+
*/
|
|
12
|
+
protected cacheTags: string[];
|
|
13
|
+
/**
|
|
14
|
+
* The underlying cache driver
|
|
15
|
+
*/
|
|
16
|
+
protected driver: CacheDriver<any, any>;
|
|
17
|
+
/**
|
|
18
|
+
* Constructor
|
|
19
|
+
*/
|
|
20
|
+
constructor(tags: string[], driver: CacheDriver<any, any>);
|
|
21
|
+
/**
|
|
22
|
+
* Get the tag key prefix for storing tag-key relationships
|
|
23
|
+
*/
|
|
24
|
+
protected tagKey(tag: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Store tag-key relationship
|
|
27
|
+
*/
|
|
28
|
+
protected storeTaggedKey(key: string): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Public alias of the tag-index writer. Called by `BaseCacheDriver.applyTags`
|
|
31
|
+
* when tags are passed inline through `CacheSetOptions.tags`.
|
|
32
|
+
*
|
|
33
|
+
* @internal — public for cross-class use within this package; not part of the
|
|
34
|
+
* stable consumer API.
|
|
35
|
+
*/
|
|
36
|
+
storeTagRelationship(parsedKey: string): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Get all keys associated with tags
|
|
39
|
+
*/
|
|
40
|
+
protected getTaggedKeys(): Promise<Set<string>>;
|
|
41
|
+
/**
|
|
42
|
+
* {@inheritdoc}
|
|
43
|
+
*/
|
|
44
|
+
set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;
|
|
45
|
+
/**
|
|
46
|
+
* {@inheritdoc}
|
|
47
|
+
*/
|
|
48
|
+
get(key: CacheKey): Promise<any | null>;
|
|
49
|
+
/**
|
|
50
|
+
* {@inheritdoc}
|
|
51
|
+
*/
|
|
52
|
+
remove(key: CacheKey): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Invalidate (clear) all keys associated with the current tags
|
|
55
|
+
*/
|
|
56
|
+
invalidate(): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Flush all keys associated with the current tags
|
|
59
|
+
* @deprecated Use invalidate() instead for better semantics
|
|
60
|
+
*/
|
|
61
|
+
flush(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* {@inheritdoc}
|
|
64
|
+
*/
|
|
65
|
+
has(key: CacheKey): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* {@inheritdoc}
|
|
68
|
+
*/
|
|
69
|
+
remember(key: CacheKey, ttl: number, callback: () => Promise<any>): Promise<any>;
|
|
70
|
+
/**
|
|
71
|
+
* {@inheritdoc}
|
|
72
|
+
*/
|
|
73
|
+
pull(key: CacheKey): Promise<any | null>;
|
|
74
|
+
/**
|
|
75
|
+
* {@inheritdoc}
|
|
76
|
+
*/
|
|
77
|
+
forever(key: CacheKey, value: any): Promise<any>;
|
|
78
|
+
/**
|
|
79
|
+
* {@inheritdoc}
|
|
80
|
+
*/
|
|
81
|
+
increment(key: CacheKey, value?: number): Promise<number>;
|
|
82
|
+
/**
|
|
83
|
+
* {@inheritdoc}
|
|
84
|
+
*/
|
|
85
|
+
decrement(key: CacheKey, value?: number): Promise<number>;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { TaggedCache };
|
|
89
|
+
//# sourceMappingURL=tagged-cache.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tagged-cache.d.mts","names":[],"sources":["../../../../../@warlock.js/cache/src/tagged-cache.ts"],"mappings":";;;;;AAYA;;cAAa,WAAA,YAAuB,iBAAA;EAShB;;;EAAA,UALR,SAAA;EAmD+B;;;EAAA,UA9C/B,MAAA,EAAQ,WAAA;EAmEU;;;cA9DT,IAAA,YAAgB,MAAA,EAAQ,WAAA;EAmFlB;;;EAAA,UA3Ef,MAAA,CAAO,GAAA;EAsHK;;;EAAA,UA/GN,cAAA,CAAe,GAAA,WAAc,OAAA;EA0H1C;;;;;;;EA/GU,oBAAA,CAAqB,SAAA,WAAoB,OAAA;EAqKI;;;EAAA,UAtJ1C,aAAA,CAAA,GAAiB,OAAA,CAAQ,GAAA;EAvDP;;;EAyErB,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;;;;EAaU,GAAA,CAAI,GAAA,EAAK,QAAA,GAAW,OAAA;EApEvB;;;EA2EG,MAAA,CAAO,GAAA,EAAK,QAAA,GAAW,OAAA;EApES;;;EAsFhC,UAAA,CAAA,GAAc,OAAA;EA5DX;;;;EA8EH,KAAA,CAAA,GAAS,OAAA;EA3DpB;;;EAkEW,GAAA,CAAI,GAAA,EAAK,QAAA,GAAW,OAAA;EAhE/B;;;EAuEW,QAAA,CACX,GAAA,EAAK,QAAA,EACL,GAAA,UACA,QAAA,QAAgB,OAAA,QACf,OAAA;EA7Dc;;;EA6EJ,IAAA,CAAK,GAAA,EAAK,QAAA,GAAW,OAAA;EAtEd;;;EAmFP,OAAA,CAAQ,GAAA,EAAK,QAAA,EAAU,KAAA,QAAa,OAAA;EA/CpC;;;EAsDA,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,YAAoB,OAAA;EA/CzC;;;EAiEJ,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,YAAoB,OAAA;AAAA"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
//#region ../../@warlock.js/cache/src/tagged-cache.ts
|
|
2
|
+
/**
|
|
3
|
+
* Tagged Cache Wrapper
|
|
4
|
+
* Wraps a cache driver to automatically manage tag relationships
|
|
5
|
+
*/
|
|
6
|
+
var TaggedCache = class {
|
|
7
|
+
/**
|
|
8
|
+
* Constructor
|
|
9
|
+
*/
|
|
10
|
+
constructor(tags, driver) {
|
|
11
|
+
this.cacheTags = tags;
|
|
12
|
+
this.driver = driver;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the tag key prefix for storing tag-key relationships
|
|
16
|
+
*/
|
|
17
|
+
tagKey(tag) {
|
|
18
|
+
return `cache:tags:${tag}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Store tag-key relationship
|
|
22
|
+
*/
|
|
23
|
+
async storeTaggedKey(key) {
|
|
24
|
+
await this.storeTagRelationship(key);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Public alias of the tag-index writer. Called by `BaseCacheDriver.applyTags`
|
|
28
|
+
* when tags are passed inline through `CacheSetOptions.tags`.
|
|
29
|
+
*
|
|
30
|
+
* @internal — public for cross-class use within this package; not part of the
|
|
31
|
+
* stable consumer API.
|
|
32
|
+
*/
|
|
33
|
+
async storeTagRelationship(parsedKey) {
|
|
34
|
+
for (const tag of this.cacheTags) {
|
|
35
|
+
const tagKey = this.tagKey(tag);
|
|
36
|
+
const keys = await this.driver.get(tagKey) || [];
|
|
37
|
+
if (!keys.includes(parsedKey)) {
|
|
38
|
+
keys.push(parsedKey);
|
|
39
|
+
await this.driver.set(tagKey, keys, Infinity);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get all keys associated with tags
|
|
45
|
+
*/
|
|
46
|
+
async getTaggedKeys() {
|
|
47
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
48
|
+
for (const tag of this.cacheTags) {
|
|
49
|
+
const tagKey = this.tagKey(tag);
|
|
50
|
+
const keys = await this.driver.get(tagKey) || [];
|
|
51
|
+
for (const key of keys) allKeys.add(key);
|
|
52
|
+
}
|
|
53
|
+
return allKeys;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* {@inheritdoc}
|
|
57
|
+
*/
|
|
58
|
+
async set(key, value, ttlOrOptions) {
|
|
59
|
+
const parsedKey = this.driver.parseKey(key);
|
|
60
|
+
await this.driver.set(key, value, ttlOrOptions);
|
|
61
|
+
await this.storeTaggedKey(parsedKey);
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* {@inheritdoc}
|
|
66
|
+
*/
|
|
67
|
+
async get(key) {
|
|
68
|
+
return this.driver.get(key);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* {@inheritdoc}
|
|
72
|
+
*/
|
|
73
|
+
async remove(key) {
|
|
74
|
+
const parsedKey = this.driver.parseKey(key);
|
|
75
|
+
await this.driver.remove(key);
|
|
76
|
+
for (const tag of this.cacheTags) {
|
|
77
|
+
const tagKey = this.tagKey(tag);
|
|
78
|
+
const updatedKeys = (await this.driver.get(tagKey) || []).filter((k) => k !== parsedKey);
|
|
79
|
+
await this.driver.set(tagKey, updatedKeys, Infinity);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Invalidate (clear) all keys associated with the current tags
|
|
84
|
+
*/
|
|
85
|
+
async invalidate() {
|
|
86
|
+
const keysToRemove = await this.getTaggedKeys();
|
|
87
|
+
for (const key of keysToRemove) await this.driver.remove(key);
|
|
88
|
+
for (const tag of this.cacheTags) await this.driver.remove(this.tagKey(tag));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Flush all keys associated with the current tags
|
|
92
|
+
* @deprecated Use invalidate() instead for better semantics
|
|
93
|
+
*/
|
|
94
|
+
async flush() {
|
|
95
|
+
return this.invalidate();
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* {@inheritdoc}
|
|
99
|
+
*/
|
|
100
|
+
async has(key) {
|
|
101
|
+
return this.driver.has(key);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* {@inheritdoc}
|
|
105
|
+
*/
|
|
106
|
+
async remember(key, ttl, callback) {
|
|
107
|
+
const value = await this.get(key);
|
|
108
|
+
if (value !== null) return value;
|
|
109
|
+
const result = await callback();
|
|
110
|
+
await this.set(key, result, ttl);
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* {@inheritdoc}
|
|
115
|
+
*/
|
|
116
|
+
async pull(key) {
|
|
117
|
+
const value = await this.get(key);
|
|
118
|
+
if (value !== null) await this.remove(key);
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* {@inheritdoc}
|
|
123
|
+
*/
|
|
124
|
+
async forever(key, value) {
|
|
125
|
+
return this.set(key, value, Infinity);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* {@inheritdoc}
|
|
129
|
+
*/
|
|
130
|
+
async increment(key, value = 1) {
|
|
131
|
+
const current = await this.get(key) || 0;
|
|
132
|
+
if (typeof current !== "number") throw new Error(`Cannot increment non-numeric value for key: ${this.driver.parseKey(key)}`);
|
|
133
|
+
const newValue = current + value;
|
|
134
|
+
await this.set(key, newValue);
|
|
135
|
+
return newValue;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* {@inheritdoc}
|
|
139
|
+
*/
|
|
140
|
+
async decrement(key, value = 1) {
|
|
141
|
+
return this.increment(key, -value);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
//#endregion
|
|
146
|
+
export { TaggedCache };
|
|
147
|
+
//# sourceMappingURL=tagged-cache.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tagged-cache.mjs","names":[],"sources":["../../../../../@warlock.js/cache/src/tagged-cache.ts"],"sourcesContent":["import type {\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheTtl,\r\n TaggedCacheDriver,\r\n} from \"./types\";\r\n\r\n/**\r\n * Tagged Cache Wrapper\r\n * Wraps a cache driver to automatically manage tag relationships\r\n */\r\nexport class TaggedCache implements TaggedCacheDriver {\r\n /**\r\n * The tags associated with this tagged cache instance\r\n */\r\n protected cacheTags: string[];\r\n\r\n /**\r\n * The underlying cache driver\r\n */\r\n protected driver: CacheDriver<any, any>;\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor(tags: string[], driver: CacheDriver<any, any>) {\r\n this.cacheTags = tags;\r\n this.driver = driver;\r\n }\r\n\r\n /**\r\n * Get the tag key prefix for storing tag-key relationships\r\n */\r\n protected tagKey(tag: string): string {\r\n return `cache:tags:${tag}`;\r\n }\r\n\r\n /**\r\n * Store tag-key relationship\r\n */\r\n protected async storeTaggedKey(key: string): Promise<void> {\r\n await this.storeTagRelationship(key);\r\n }\r\n\r\n /**\r\n * Public alias of the tag-index writer. Called by `BaseCacheDriver.applyTags`\r\n * when tags are passed inline through `CacheSetOptions.tags`.\r\n *\r\n * @internal — public for cross-class use within this package; not part of the\r\n * stable consumer API.\r\n */\r\n public async storeTagRelationship(parsedKey: string): Promise<void> {\r\n for (const tag of this.cacheTags) {\r\n const tagKey = this.tagKey(tag);\r\n const keys = (await this.driver.get(tagKey)) || [];\r\n\r\n if (!keys.includes(parsedKey)) {\r\n keys.push(parsedKey);\r\n await this.driver.set(tagKey, keys, Infinity);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Get all keys associated with tags\r\n */\r\n protected async getTaggedKeys(): Promise<Set<string>> {\r\n const allKeys = new Set<string>();\r\n\r\n for (const tag of this.cacheTags) {\r\n const tagKey = this.tagKey(tag);\r\n const keys = (await this.driver.get(tagKey)) || [];\r\n\r\n for (const key of keys) {\r\n allKeys.add(key);\r\n }\r\n }\r\n\r\n return allKeys;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async set(\r\n key: CacheKey,\r\n value: any,\r\n ttlOrOptions?: CacheTtl | CacheSetOptions,\r\n ): Promise<any> {\r\n const parsedKey = this.driver.parseKey(key);\r\n\r\n await this.driver.set(key, value, ttlOrOptions);\r\n\r\n await this.storeTaggedKey(parsedKey);\r\n\r\n return value;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async get(key: CacheKey): Promise<any | null> {\r\n return this.driver.get(key);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remove(key: CacheKey): Promise<void> {\r\n const parsedKey = this.driver.parseKey(key);\r\n\r\n // Remove the value\r\n await this.driver.remove(key);\r\n\r\n // Remove from all tag relationships\r\n for (const tag of this.cacheTags) {\r\n const tagKey = this.tagKey(tag);\r\n const keys = (await this.driver.get(tagKey)) || [];\r\n const updatedKeys = keys.filter((k: string) => k !== parsedKey);\r\n await this.driver.set(tagKey, updatedKeys, Infinity);\r\n }\r\n }\r\n\r\n /**\r\n * Invalidate (clear) all keys associated with the current tags\r\n */\r\n public async invalidate(): Promise<void> {\r\n const keysToRemove = await this.getTaggedKeys();\r\n\r\n // Remove all tagged keys\r\n for (const key of keysToRemove) {\r\n await this.driver.remove(key);\r\n }\r\n\r\n // Clear tag relationship keys\r\n for (const tag of this.cacheTags) {\r\n await this.driver.remove(this.tagKey(tag));\r\n }\r\n }\r\n\r\n /**\r\n * Flush all keys associated with the current tags\r\n * @deprecated Use invalidate() instead for better semantics\r\n */\r\n public async flush(): Promise<void> {\r\n return this.invalidate();\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async has(key: CacheKey): Promise<boolean> {\r\n return this.driver.has(key);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remember(\r\n key: CacheKey,\r\n ttl: number,\r\n callback: () => Promise<any>,\r\n ): Promise<any> {\r\n const value = await this.get(key);\r\n\r\n if (value !== null) {\r\n return value;\r\n }\r\n\r\n const result = await callback();\r\n await this.set(key, result, ttl);\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async pull(key: CacheKey): Promise<any | null> {\r\n const value = await this.get(key);\r\n\r\n if (value !== null) {\r\n await this.remove(key);\r\n }\r\n\r\n return value;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async forever(key: CacheKey, value: any): Promise<any> {\r\n return this.set(key, value, Infinity);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async increment(key: CacheKey, value: number = 1): Promise<number> {\r\n const current = (await this.get(key)) || 0;\r\n\r\n if (typeof current !== \"number\") {\r\n throw new Error(\r\n `Cannot increment non-numeric value for key: ${this.driver.parseKey(key)}`,\r\n );\r\n }\r\n\r\n const newValue = current + value;\r\n await this.set(key, newValue);\r\n\r\n return newValue;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async decrement(key: CacheKey, value: number = 1): Promise<number> {\r\n return this.increment(key, -value);\r\n }\r\n}\r\n"],"mappings":"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"}
|