@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.
Files changed (213) hide show
  1. package/README.md +85 -0
  2. package/cjs/index.cjs +4088 -0
  3. package/cjs/index.cjs.map +1 -0
  4. package/esm/cache-manager.d.mts +314 -0
  5. package/esm/cache-manager.d.mts.map +1 -0
  6. package/esm/cache-manager.mjs +486 -0
  7. package/esm/cache-manager.mjs.map +1 -0
  8. package/esm/cached/auto-key.d.mts +25 -0
  9. package/esm/cached/auto-key.d.mts.map +1 -0
  10. package/esm/cached/auto-key.mjs +55 -0
  11. package/esm/cached/auto-key.mjs.map +1 -0
  12. package/esm/cached/cached.d.mts +54 -0
  13. package/esm/cached/cached.d.mts.map +1 -0
  14. package/esm/cached/cached.mjs +25 -0
  15. package/esm/cached/cached.mjs.map +1 -0
  16. package/esm/cached/index.d.mts +3 -0
  17. package/esm/cached/index.mjs +5 -0
  18. package/esm/cached/normalize-args.d.mts +51 -0
  19. package/esm/cached/normalize-args.d.mts.map +1 -0
  20. package/esm/cached/normalize-args.mjs +26 -0
  21. package/esm/cached/normalize-args.mjs.map +1 -0
  22. package/esm/drivers/base-cache-driver.d.mts +322 -0
  23. package/esm/drivers/base-cache-driver.d.mts.map +1 -0
  24. package/esm/drivers/base-cache-driver.mjs +522 -0
  25. package/esm/drivers/base-cache-driver.mjs.map +1 -0
  26. package/esm/drivers/file-cache-driver.d.mts +68 -0
  27. package/esm/drivers/file-cache-driver.d.mts.map +1 -0
  28. package/esm/drivers/file-cache-driver.mjs +174 -0
  29. package/esm/drivers/file-cache-driver.mjs.map +1 -0
  30. package/esm/drivers/index.d.mts +9 -0
  31. package/esm/drivers/index.mjs +11 -0
  32. package/esm/drivers/lru-memory-cache-driver.d.mts +136 -0
  33. package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -0
  34. package/esm/drivers/lru-memory-cache-driver.mjs +317 -0
  35. package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -0
  36. package/esm/drivers/memory-cache-driver.d.mts +112 -0
  37. package/esm/drivers/memory-cache-driver.d.mts.map +1 -0
  38. package/esm/drivers/memory-cache-driver.mjs +241 -0
  39. package/esm/drivers/memory-cache-driver.mjs.map +1 -0
  40. package/esm/drivers/memory-extended-cache-driver.d.mts +17 -0
  41. package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -0
  42. package/esm/drivers/memory-extended-cache-driver.mjs +34 -0
  43. package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -0
  44. package/esm/drivers/mock-cache-driver.d.mts +137 -0
  45. package/esm/drivers/mock-cache-driver.d.mts.map +1 -0
  46. package/esm/drivers/mock-cache-driver.mjs +226 -0
  47. package/esm/drivers/mock-cache-driver.mjs.map +1 -0
  48. package/esm/drivers/null-cache-driver.d.mts +69 -0
  49. package/esm/drivers/null-cache-driver.d.mts.map +1 -0
  50. package/esm/drivers/null-cache-driver.mjs +92 -0
  51. package/esm/drivers/null-cache-driver.mjs.map +1 -0
  52. package/esm/drivers/pg-cache-driver.d.mts +148 -0
  53. package/esm/drivers/pg-cache-driver.d.mts.map +1 -0
  54. package/esm/drivers/pg-cache-driver.mjs +437 -0
  55. package/esm/drivers/pg-cache-driver.mjs.map +1 -0
  56. package/esm/drivers/redis-cache-driver.d.mts +86 -0
  57. package/esm/drivers/redis-cache-driver.d.mts.map +1 -0
  58. package/esm/drivers/redis-cache-driver.mjs +312 -0
  59. package/esm/drivers/redis-cache-driver.mjs.map +1 -0
  60. package/esm/index.d.mts +21 -0
  61. package/esm/index.mjs +24 -0
  62. package/esm/list/index.d.mts +1 -0
  63. package/esm/list/memory-cache-list.d.mts +77 -0
  64. package/esm/list/memory-cache-list.d.mts.map +1 -0
  65. package/esm/list/memory-cache-list.mjs +119 -0
  66. package/esm/list/memory-cache-list.mjs.map +1 -0
  67. package/esm/metrics.d.mts +118 -0
  68. package/esm/metrics.d.mts.map +1 -0
  69. package/esm/metrics.mjs +197 -0
  70. package/esm/metrics.mjs.map +1 -0
  71. package/esm/scoped-cache.d.mts +205 -0
  72. package/esm/scoped-cache.d.mts.map +1 -0
  73. package/esm/scoped-cache.mjs +274 -0
  74. package/esm/scoped-cache.mjs.map +1 -0
  75. package/esm/tagged-cache.d.mts +89 -0
  76. package/esm/tagged-cache.d.mts.map +1 -0
  77. package/esm/tagged-cache.mjs +147 -0
  78. package/esm/tagged-cache.mjs.map +1 -0
  79. package/esm/tagged-scoped-cache.d.mts +111 -0
  80. package/esm/tagged-scoped-cache.d.mts.map +1 -0
  81. package/esm/tagged-scoped-cache.mjs +142 -0
  82. package/esm/tagged-scoped-cache.mjs.map +1 -0
  83. package/esm/types.d.mts +1067 -0
  84. package/esm/types.d.mts.map +1 -0
  85. package/esm/types.mjs +62 -0
  86. package/esm/types.mjs.map +1 -0
  87. package/esm/utils.d.mts +161 -0
  88. package/esm/utils.d.mts.map +1 -0
  89. package/esm/utils.mjs +222 -0
  90. package/esm/utils.mjs.map +1 -0
  91. package/llms-full.txt +2071 -0
  92. package/llms.txt +28 -0
  93. package/package.json +53 -39
  94. package/skills/apply-cache-patterns/SKILL.md +97 -0
  95. package/skills/cache-basics/SKILL.md +121 -0
  96. package/skills/configure-pg-cache/SKILL.md +115 -0
  97. package/skills/configure-set-options/SKILL.md +96 -0
  98. package/skills/handle-cache-errors/SKILL.md +91 -0
  99. package/skills/observe-cache/SKILL.md +103 -0
  100. package/skills/overview/SKILL.md +69 -0
  101. package/skills/pick-cache-driver/SKILL.md +115 -0
  102. package/skills/test-cache-code/SKILL.md +219 -0
  103. package/skills/use-cache-atomic/SKILL.md +67 -0
  104. package/skills/use-cache-bulk/SKILL.md +57 -0
  105. package/skills/use-cache-list/SKILL.md +85 -0
  106. package/skills/use-cache-lock/SKILL.md +104 -0
  107. package/skills/use-cache-namespace/SKILL.md +88 -0
  108. package/skills/use-cache-similarity/SKILL.md +94 -0
  109. package/skills/use-cache-tags/SKILL.md +85 -0
  110. package/skills/use-cache-update-merge/SKILL.md +84 -0
  111. package/skills/use-cache-utils/SKILL.md +89 -0
  112. package/skills/use-cached-hof/SKILL.md +102 -0
  113. package/skills/use-swr/SKILL.md +104 -0
  114. package/cjs/cache-manager.d.ts +0 -163
  115. package/cjs/cache-manager.d.ts.map +0 -1
  116. package/cjs/cache-manager.js +0 -322
  117. package/cjs/cache-manager.js.map +0 -1
  118. package/cjs/drivers/base-cache-driver.d.ts +0 -152
  119. package/cjs/drivers/base-cache-driver.d.ts.map +0 -1
  120. package/cjs/drivers/base-cache-driver.js +0 -321
  121. package/cjs/drivers/base-cache-driver.js.map +0 -1
  122. package/cjs/drivers/file-cache-driver.d.ts +0 -45
  123. package/cjs/drivers/file-cache-driver.d.ts.map +0 -1
  124. package/cjs/drivers/file-cache-driver.js +0 -133
  125. package/cjs/drivers/file-cache-driver.js.map +0 -1
  126. package/cjs/drivers/index.d.ts +0 -8
  127. package/cjs/drivers/index.d.ts.map +0 -1
  128. package/cjs/drivers/lru-memory-cache-driver.d.ts +0 -98
  129. package/cjs/drivers/lru-memory-cache-driver.d.ts.map +0 -1
  130. package/cjs/drivers/lru-memory-cache-driver.js +0 -252
  131. package/cjs/drivers/lru-memory-cache-driver.js.map +0 -1
  132. package/cjs/drivers/memory-cache-driver.d.ts +0 -82
  133. package/cjs/drivers/memory-cache-driver.d.ts.map +0 -1
  134. package/cjs/drivers/memory-cache-driver.js +0 -218
  135. package/cjs/drivers/memory-cache-driver.js.map +0 -1
  136. package/cjs/drivers/memory-extended-cache-driver.d.ts +0 -13
  137. package/cjs/drivers/memory-extended-cache-driver.d.ts.map +0 -1
  138. package/cjs/drivers/memory-extended-cache-driver.js +0 -25
  139. package/cjs/drivers/memory-extended-cache-driver.js.map +0 -1
  140. package/cjs/drivers/null-cache-driver.d.ts +0 -58
  141. package/cjs/drivers/null-cache-driver.d.ts.map +0 -1
  142. package/cjs/drivers/null-cache-driver.js +0 -84
  143. package/cjs/drivers/null-cache-driver.js.map +0 -1
  144. package/cjs/drivers/redis-cache-driver.d.ts +0 -57
  145. package/cjs/drivers/redis-cache-driver.d.ts.map +0 -1
  146. package/cjs/drivers/redis-cache-driver.js +0 -263
  147. package/cjs/drivers/redis-cache-driver.js.map +0 -1
  148. package/cjs/index.d.ts +0 -6
  149. package/cjs/index.d.ts.map +0 -1
  150. package/cjs/index.js +0 -1
  151. package/cjs/index.js.map +0 -1
  152. package/cjs/tagged-cache.d.ts +0 -77
  153. package/cjs/tagged-cache.d.ts.map +0 -1
  154. package/cjs/tagged-cache.js +0 -160
  155. package/cjs/tagged-cache.js.map +0 -1
  156. package/cjs/types.d.ts +0 -391
  157. package/cjs/types.d.ts.map +0 -1
  158. package/cjs/types.js +0 -36
  159. package/cjs/types.js.map +0 -1
  160. package/cjs/utils.d.ts +0 -50
  161. package/cjs/utils.d.ts.map +0 -1
  162. package/cjs/utils.js +0 -55
  163. package/cjs/utils.js.map +0 -1
  164. package/esm/cache-manager.d.ts +0 -163
  165. package/esm/cache-manager.d.ts.map +0 -1
  166. package/esm/cache-manager.js +0 -322
  167. package/esm/cache-manager.js.map +0 -1
  168. package/esm/drivers/base-cache-driver.d.ts +0 -152
  169. package/esm/drivers/base-cache-driver.d.ts.map +0 -1
  170. package/esm/drivers/base-cache-driver.js +0 -321
  171. package/esm/drivers/base-cache-driver.js.map +0 -1
  172. package/esm/drivers/file-cache-driver.d.ts +0 -45
  173. package/esm/drivers/file-cache-driver.d.ts.map +0 -1
  174. package/esm/drivers/file-cache-driver.js +0 -133
  175. package/esm/drivers/file-cache-driver.js.map +0 -1
  176. package/esm/drivers/index.d.ts +0 -8
  177. package/esm/drivers/index.d.ts.map +0 -1
  178. package/esm/drivers/lru-memory-cache-driver.d.ts +0 -98
  179. package/esm/drivers/lru-memory-cache-driver.d.ts.map +0 -1
  180. package/esm/drivers/lru-memory-cache-driver.js +0 -252
  181. package/esm/drivers/lru-memory-cache-driver.js.map +0 -1
  182. package/esm/drivers/memory-cache-driver.d.ts +0 -82
  183. package/esm/drivers/memory-cache-driver.d.ts.map +0 -1
  184. package/esm/drivers/memory-cache-driver.js +0 -218
  185. package/esm/drivers/memory-cache-driver.js.map +0 -1
  186. package/esm/drivers/memory-extended-cache-driver.d.ts +0 -13
  187. package/esm/drivers/memory-extended-cache-driver.d.ts.map +0 -1
  188. package/esm/drivers/memory-extended-cache-driver.js +0 -25
  189. package/esm/drivers/memory-extended-cache-driver.js.map +0 -1
  190. package/esm/drivers/null-cache-driver.d.ts +0 -58
  191. package/esm/drivers/null-cache-driver.d.ts.map +0 -1
  192. package/esm/drivers/null-cache-driver.js +0 -84
  193. package/esm/drivers/null-cache-driver.js.map +0 -1
  194. package/esm/drivers/redis-cache-driver.d.ts +0 -57
  195. package/esm/drivers/redis-cache-driver.d.ts.map +0 -1
  196. package/esm/drivers/redis-cache-driver.js +0 -263
  197. package/esm/drivers/redis-cache-driver.js.map +0 -1
  198. package/esm/index.d.ts +0 -6
  199. package/esm/index.d.ts.map +0 -1
  200. package/esm/index.js +0 -1
  201. package/esm/index.js.map +0 -1
  202. package/esm/tagged-cache.d.ts +0 -77
  203. package/esm/tagged-cache.d.ts.map +0 -1
  204. package/esm/tagged-cache.js +0 -160
  205. package/esm/tagged-cache.js.map +0 -1
  206. package/esm/types.d.ts +0 -391
  207. package/esm/types.d.ts.map +0 -1
  208. package/esm/types.js +0 -36
  209. package/esm/types.js.map +0 -1
  210. package/esm/utils.d.ts +0 -50
  211. package/esm/utils.d.ts.map +0 -1
  212. package/esm/utils.js +0 -55
  213. 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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"}