@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,522 @@
1
+ import { CacheUnsupportedError } from "../types.mjs";
2
+ import { normalizeToOptions, parseCacheKey, parseTtl, resolveTtl } from "../utils.mjs";
3
+ import { MemoryCacheList } from "../list/memory-cache-list.mjs";
4
+ import { TaggedCache } from "../tagged-cache.mjs";
5
+ import { log } from "@warlock.js/logger";
6
+
7
+ //#region ../../@warlock.js/cache/src/drivers/base-cache-driver.ts
8
+ const messages = {
9
+ clearing: "Clearing namespace",
10
+ cleared: "Namespace cleared",
11
+ fetching: "Fetching key",
12
+ fetched: "Key fetched",
13
+ caching: "Caching key",
14
+ cached: "Key cached",
15
+ flushing: "Flushing cache",
16
+ flushed: "Cache flushed",
17
+ removing: "Removing key",
18
+ removed: "Key removed",
19
+ expired: "Key expired",
20
+ notFound: "Key not found",
21
+ connecting: "Connecting to the cache engine.",
22
+ connected: "Connected to the cache engine.",
23
+ disconnecting: "Disconnecting from the cache engine.",
24
+ disconnected: "Disconnected from the cache engine.",
25
+ error: "Error occurred"
26
+ };
27
+ var BaseCacheDriver = class {
28
+ constructor() {
29
+ this.shouldLog = true;
30
+ this.eventListeners = /* @__PURE__ */ new Map();
31
+ this.locks = /* @__PURE__ */ new Map();
32
+ }
33
+ /**
34
+ * {@inheritdoc}
35
+ */
36
+ get client() {
37
+ return this.clientDriver || this;
38
+ }
39
+ /**
40
+ * Set logging state
41
+ */
42
+ setLoggingState(shouldLog) {
43
+ this.shouldLog = shouldLog;
44
+ return this;
45
+ }
46
+ /**
47
+ * Set client driver
48
+ */
49
+ set client(client) {
50
+ this.clientDriver = client;
51
+ }
52
+ /**
53
+ * {@inheritdoc}
54
+ */
55
+ parseKey(key) {
56
+ return parseCacheKey(key, this.options);
57
+ }
58
+ /**
59
+ * {@inheritdoc}
60
+ */
61
+ setOptions(options) {
62
+ this.options = options || {};
63
+ return this;
64
+ }
65
+ /**
66
+ * Register an event listener
67
+ */
68
+ on(event, handler) {
69
+ if (!this.eventListeners.has(event)) this.eventListeners.set(event, /* @__PURE__ */ new Set());
70
+ this.eventListeners.get(event).add(handler);
71
+ return this;
72
+ }
73
+ /**
74
+ * Remove an event listener
75
+ */
76
+ off(event, handler) {
77
+ const handlers = this.eventListeners.get(event);
78
+ if (handlers) handlers.delete(handler);
79
+ return this;
80
+ }
81
+ /**
82
+ * Register a one-time event listener
83
+ */
84
+ once(event, handler) {
85
+ const onceHandler = async (data) => {
86
+ await handler(data);
87
+ this.off(event, onceHandler);
88
+ };
89
+ return this.on(event, onceHandler);
90
+ }
91
+ /**
92
+ * Emit an event to all registered listeners
93
+ */
94
+ async emit(event, data = {}) {
95
+ const handlers = this.eventListeners.get(event);
96
+ if (!handlers || handlers.size === 0) return;
97
+ const eventData = {
98
+ driver: this.name,
99
+ ...data
100
+ };
101
+ const promises = [];
102
+ for (const handler of handlers) try {
103
+ const result = handler(eventData);
104
+ if (result instanceof Promise) promises.push(result);
105
+ } catch (error) {
106
+ this.logError(`Error in event handler for '${event}'`, error);
107
+ }
108
+ if (promises.length > 0) await Promise.allSettled(promises);
109
+ }
110
+ /**
111
+ * Normalize the 3rd argument of a `set` call into a single shape every driver
112
+ * can act on. Handles TTL parsing (number | string | Infinity), `expiresAt` →
113
+ * relative TTL conversion, and mutual-exclusion validation.
114
+ *
115
+ * @throws {CacheConfigurationError} when `ttl` and `expiresAt` are passed together
116
+ * or an unparseable duration string is supplied.
117
+ */
118
+ resolveSetOptions(ttlOrOptions) {
119
+ const options = normalizeToOptions(ttlOrOptions);
120
+ return {
121
+ ttl: resolveTtl(options.ttl, options.expiresAt, this.ttl),
122
+ tags: options.tags,
123
+ onConflict: options.onConflict ?? "upsert",
124
+ vector: options.vector,
125
+ staleAt: options.staleAt
126
+ };
127
+ }
128
+ /**
129
+ * Resolve the union of cache keys associated with any of the given tags.
130
+ * Used by `similar()` to narrow the candidate pool before similarity ranking.
131
+ *
132
+ * Returns `null` when no tags are passed (callers should treat that as "no filter").
133
+ */
134
+ async getKeysForTags(tags) {
135
+ if (!tags || tags.length === 0) return null;
136
+ const allKeys = /* @__PURE__ */ new Set();
137
+ for (const tag of tags) {
138
+ const tagKey = `cache:tags:${tag}`;
139
+ const keys = await this.get(tagKey) || [];
140
+ for (const k of keys) allKeys.add(k);
141
+ }
142
+ return allKeys;
143
+ }
144
+ /**
145
+ * Apply tag relationships after a successful write. Called by drivers once
146
+ * the value is in storage.
147
+ */
148
+ async applyTags(parsedKey, tags) {
149
+ if (tags.length === 0) return;
150
+ await this.tags(tags).storeTagRelationship(parsedKey);
151
+ }
152
+ /**
153
+ * {@inheritdoc}
154
+ */
155
+ async has(key) {
156
+ return await this.get(key) !== null;
157
+ }
158
+ /**
159
+ * {@inheritdoc}
160
+ */
161
+ async remember(key, ttlOrOptions, callback) {
162
+ const parsedKey = this.parseKey(key);
163
+ const setOptions = this.normalizeRememberOptions(ttlOrOptions);
164
+ const cachedValue = await this.get(key);
165
+ if (cachedValue) return cachedValue;
166
+ const existingLock = this.locks.get(parsedKey);
167
+ if (existingLock) return existingLock;
168
+ const promise = callback().then(async (result) => {
169
+ await this.set(key, result, setOptions);
170
+ this.locks.delete(parsedKey);
171
+ return result;
172
+ }).catch((err) => {
173
+ this.locks.delete(parsedKey);
174
+ throw err;
175
+ });
176
+ this.locks.set(parsedKey, promise);
177
+ return promise;
178
+ }
179
+ /**
180
+ * Resolve the TTL-or-options arg of `remember` into a `CacheSetOptions` object
181
+ * that can be passed straight to `set()`. Keeps the implementation unbranched.
182
+ */
183
+ normalizeRememberOptions(ttlOrOptions) {
184
+ if (typeof ttlOrOptions === "number" || typeof ttlOrOptions === "string") return { ttl: ttlOrOptions };
185
+ return {
186
+ ttl: ttlOrOptions.ttl,
187
+ tags: ttlOrOptions.tags
188
+ };
189
+ }
190
+ /**
191
+ * {@inheritdoc}
192
+ *
193
+ * Default implementation: read raw entry, branch on freshness/staleness,
194
+ * trigger background refresh in the stale window, fall through to
195
+ * `callback` on miss/expiry. Concurrent stale-window callers share a
196
+ * single in-flight refresh via {@link locks}.
197
+ *
198
+ * Drivers without a real {@link getEntry} override degrade gracefully —
199
+ * the synthetic entry has no `staleAt`, which the freshness check treats
200
+ * as "always fresh," so SWR behaves like a TTL-only cached read on those
201
+ * drivers (no background refresh, but no double-fetch either).
202
+ */
203
+ async swr(key, options, callback) {
204
+ const parsedKey = this.parseKey(key);
205
+ const freshSeconds = parseTtl(options.freshTtl);
206
+ const staleSeconds = parseTtl(options.staleTtl);
207
+ if (staleSeconds <= freshSeconds) throw new Error(`cache.swr: 'staleTtl' (${staleSeconds}s) must be greater than 'freshTtl' (${freshSeconds}s).`);
208
+ const entry = await this.getEntry(key);
209
+ const now = Date.now();
210
+ const isExpired = entry?.expiresAt !== void 0 && entry.expiresAt <= now;
211
+ if (!entry || isExpired) return this.swrFetchAndStore(key, options, callback, freshSeconds, staleSeconds);
212
+ if (entry.staleAt === void 0 || entry.staleAt > now) return entry.data;
213
+ this.scheduleSwrRefresh(parsedKey, key, options, callback, freshSeconds, staleSeconds);
214
+ return entry.data;
215
+ }
216
+ /**
217
+ * Read the raw {@link CacheData} wrapper for a key, including any
218
+ * `expiresAt` / `staleAt` metadata. Default implementation falls back to
219
+ * `get()` and synthesizes a metadata-less wrapper — drivers that store
220
+ * the wrapper directly (memory, lru, file, redis, pg, mock) override
221
+ * this to return real metadata so SWR can branch on freshness.
222
+ */
223
+ async getEntry(key) {
224
+ const value = await this.get(key);
225
+ if (value === null) return null;
226
+ return { data: value };
227
+ }
228
+ /**
229
+ * Remaining lifetime of an existing entry, in seconds — used by TTL-preserving
230
+ * writes such as `update()` / `merge()` when the caller passes no explicit
231
+ * `ttl`.
232
+ *
233
+ * - `Infinity` — the entry exists with no expiry (preserve "never expires").
234
+ * - positive number — seconds left before the entry expires.
235
+ * - `undefined` — the key is missing or already past its deadline; the caller
236
+ * should fall back to the driver default TTL.
237
+ *
238
+ * Default reads `expiresAt` from {@link getEntry}, which the metadata-aware
239
+ * drivers (memory, lru, mock, pg) populate. Drivers that track TTL natively
240
+ * and don't carry `expiresAt` in their payload (Redis) override this.
241
+ */
242
+ async getRemainingTtl(key) {
243
+ const entry = await this.getEntry(key);
244
+ if (!entry) return;
245
+ if (!entry.expiresAt || entry.expiresAt === Infinity) return Infinity;
246
+ const remainingSeconds = Math.ceil((entry.expiresAt - Date.now()) / 1e3);
247
+ return remainingSeconds > 0 ? remainingSeconds : void 0;
248
+ }
249
+ /**
250
+ * Block-and-fetch path of `swr()`: invoked on miss or past-`staleTtl`
251
+ * expiry. Writes through `set()` with the SWR options translated into
252
+ * standard `CacheSetOptions` (ttl = staleTtl, staleAt = now + freshTtl).
253
+ */
254
+ async swrFetchAndStore(key, options, callback, freshSeconds, staleSeconds) {
255
+ const result = await callback();
256
+ await this.set(key, result, {
257
+ ttl: staleSeconds,
258
+ staleAt: Date.now() + freshSeconds * 1e3,
259
+ tags: options.tags
260
+ });
261
+ return result;
262
+ }
263
+ /**
264
+ * Stale-window background refresh. Registers a single in-flight promise
265
+ * per parsed key so concurrent SWR callers share one refresh. Failed
266
+ * refreshes preserve the stale entry, log via `logError`, and emit on
267
+ * `error` — the stale-returning caller never sees the failure.
268
+ */
269
+ scheduleSwrRefresh(parsedKey, key, options, callback, freshSeconds, staleSeconds) {
270
+ if (this.locks.has(parsedKey)) return;
271
+ let refresh;
272
+ refresh = (async () => {
273
+ try {
274
+ const result = await callback();
275
+ await this.set(key, result, {
276
+ ttl: staleSeconds,
277
+ staleAt: Date.now() + freshSeconds * 1e3,
278
+ tags: options.tags
279
+ });
280
+ } catch (error) {
281
+ this.logError(`SWR background refresh failed for ${parsedKey}`, error);
282
+ await this.emit("error", {
283
+ key: parsedKey,
284
+ error
285
+ });
286
+ } finally {
287
+ if (this.locks.get(parsedKey) === refresh) this.locks.delete(parsedKey);
288
+ }
289
+ })();
290
+ this.locks.set(parsedKey, refresh);
291
+ }
292
+ /**
293
+ * {@inheritdoc}
294
+ */
295
+ async pull(key) {
296
+ const value = await this.get(key);
297
+ if (value !== null) await this.remove(key);
298
+ return value;
299
+ }
300
+ /**
301
+ * {@inheritdoc}
302
+ */
303
+ async forever(key, value) {
304
+ return this.set(key, value, Infinity);
305
+ }
306
+ /**
307
+ * {@inheritdoc}
308
+ */
309
+ async increment(key, value = 1) {
310
+ const current = await this.get(key) || 0;
311
+ if (typeof current !== "number") throw new Error(`Cannot increment non-numeric value for key: ${this.parseKey(key)}`);
312
+ const newValue = current + value;
313
+ await this.set(key, newValue);
314
+ return newValue;
315
+ }
316
+ /**
317
+ * {@inheritdoc}
318
+ */
319
+ async decrement(key, value = 1) {
320
+ return this.increment(key, -value);
321
+ }
322
+ /**
323
+ * {@inheritdoc}
324
+ */
325
+ async many(keys) {
326
+ return Promise.all(keys.map((key) => this.get(key)));
327
+ }
328
+ /**
329
+ * {@inheritdoc}
330
+ */
331
+ async setMany(items, ttl) {
332
+ await Promise.all(Object.entries(items).map(([key, value]) => this.set(key, value, ttl)));
333
+ }
334
+ /**
335
+ * Log the operation
336
+ */
337
+ log(operation, key) {
338
+ if (!this.shouldLog) return;
339
+ if (key) key = key.replaceAll("/", ".");
340
+ if (operation == "notFound" || operation == "expired") return log.warn("cache." + this.name, operation, (key ? key + " " : "") + messages[operation]);
341
+ if (operation.endsWith("ed")) return log.success("cache." + this.name, operation, (key ? key + " " : "") + messages[operation]);
342
+ log.info("cache." + this.name, operation, (key ? key + " " : "") + messages[operation]);
343
+ }
344
+ /**
345
+ * Log error message
346
+ */
347
+ logError(message, error) {
348
+ log.error("cache." + this.name, "error", message);
349
+ if (error) console.log(error);
350
+ }
351
+ /**
352
+ * Get the default TTL in seconds. Parses human-readable strings (`"1h"`, `"30m"`)
353
+ * from driver options if present; falls back to `Infinity` when no default is set.
354
+ */
355
+ get ttl() {
356
+ if (this.options.ttl === void 0) return Infinity;
357
+ return parseTtl(this.options.ttl);
358
+ }
359
+ /**
360
+ * Get time to live value in milliseconds
361
+ */
362
+ getExpiresAt(ttl = this.ttl) {
363
+ if (ttl) return (/* @__PURE__ */ new Date()).getTime() + ttl * 1e3;
364
+ }
365
+ /**
366
+ * Wrap a value with TTL and optional freshness metadata for backend
367
+ * storage. `staleAt` persists alongside `expiresAt` when supplied — used
368
+ * by the SWR flow to mark when the entry stops being fresh.
369
+ */
370
+ prepareDataForStorage(data, ttl, staleAt) {
371
+ const preparedData = { data };
372
+ if (ttl) {
373
+ preparedData.ttl = ttl;
374
+ preparedData.expiresAt = this.getExpiresAt(ttl);
375
+ }
376
+ if (staleAt !== void 0) preparedData.staleAt = staleAt;
377
+ return preparedData;
378
+ }
379
+ /**
380
+ * Parse fetched data from cache
381
+ */
382
+ async parseCachedData(key, data) {
383
+ this.log("fetched", key);
384
+ if (data.expiresAt && data.expiresAt < Date.now()) {
385
+ this.remove(key);
386
+ return null;
387
+ }
388
+ const value = data.data;
389
+ if (value === null || value === void 0) return value;
390
+ const type = typeof value;
391
+ if (type === "string" || type === "number" || type === "boolean") return value;
392
+ try {
393
+ return structuredClone(value);
394
+ } catch (error) {
395
+ console.log(value);
396
+ this.logError(`Failed to clone cached value for ${key}, typeof value: ${typeof value}`, error);
397
+ throw error;
398
+ }
399
+ }
400
+ /**
401
+ * {@inheritdoc}
402
+ */
403
+ async connect() {
404
+ this.log("connecting");
405
+ this.log("connected");
406
+ await this.emit("connected");
407
+ }
408
+ /**
409
+ * {@inheritdoc}
410
+ */
411
+ async disconnect() {
412
+ this.log("disconnected");
413
+ await this.emit("disconnected");
414
+ }
415
+ /**
416
+ * Create a tagged cache instance for the given tags
417
+ */
418
+ tags(tags) {
419
+ return new TaggedCache(tags, this);
420
+ }
421
+ /**
422
+ * {@inheritdoc}
423
+ *
424
+ * Default implementation: read → transform → write under a per-key in-process
425
+ * lock. Drivers that can offer stronger semantics (Redis via `WATCH`/`MULTI`)
426
+ * should override.
427
+ */
428
+ async update(key, fn, options = {}) {
429
+ const parsedKey = this.parseKey(key);
430
+ const next = (this.locks.get(parsedKey) ?? Promise.resolve()).catch(() => void 0).then(async () => {
431
+ const result = await fn(await this.get(key));
432
+ if (result === null) {
433
+ await this.remove(key);
434
+ return null;
435
+ }
436
+ if (options.ttl !== void 0) {
437
+ await this.set(key, result, { ttl: options.ttl });
438
+ return result;
439
+ }
440
+ const remainingTtl = await this.getRemainingTtl(key);
441
+ if (remainingTtl !== void 0) await this.set(key, result, { ttl: remainingTtl });
442
+ else await this.set(key, result);
443
+ return result;
444
+ });
445
+ this.locks.set(parsedKey, next);
446
+ next.finally(() => {
447
+ if (this.locks.get(parsedKey) === next) this.locks.delete(parsedKey);
448
+ });
449
+ return next;
450
+ }
451
+ /**
452
+ * {@inheritdoc}
453
+ */
454
+ async merge(key, partial, options = {}) {
455
+ return await this.update(key, (current) => {
456
+ return {
457
+ ...current ?? {},
458
+ ...partial
459
+ };
460
+ }, options);
461
+ }
462
+ /**
463
+ * {@inheritdoc}
464
+ *
465
+ * Default implementation: read-mutate-write array backed by the underlying
466
+ * cache entry. Concrete drivers (e.g. Redis) override with native commands.
467
+ */
468
+ list(key) {
469
+ return new MemoryCacheList(this, key);
470
+ }
471
+ /**
472
+ * {@inheritdoc}
473
+ *
474
+ * Built on top of `set({ onConflict: "create" })` — Redis-native `SET … NX EX`
475
+ * under the hood on Redis, emulated via key-existence check on other drivers.
476
+ * The lock value is the resolved `owner` (defaults to `pid.<process.pid>`).
477
+ *
478
+ * Always releases in `finally`, even if `fn` throws — the thrown error
479
+ * propagates to the caller unchanged.
480
+ */
481
+ async lock(key, ttlOrOptions, fn) {
482
+ const { ttl, owner } = this.normalizeLockOptions(ttlOrOptions);
483
+ const lockOwner = owner ?? `pid.${process.pid}`;
484
+ const setResult = await this.set(key, lockOwner, {
485
+ onConflict: "create",
486
+ ttl
487
+ });
488
+ if (!(typeof setResult === "object" && setResult !== null && "wasSet" in setResult ? setResult.wasSet : true)) return { acquired: false };
489
+ try {
490
+ return {
491
+ acquired: true,
492
+ value: await fn()
493
+ };
494
+ } finally {
495
+ await this.remove(key);
496
+ }
497
+ }
498
+ /**
499
+ * {@inheritdoc}
500
+ *
501
+ * Default implementation throws {@link CacheUnsupportedError}. Drivers that
502
+ * support similarity retrieval (memory family, `pg`, `redis` w/ RediSearch)
503
+ * override this with a real impl.
504
+ */
505
+ async similar(_vector, _options) {
506
+ throw new CacheUnsupportedError(`'${this.name}' driver does not support similarity retrieval. Use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).`);
507
+ }
508
+ /**
509
+ * Resolve the TTL-or-options arg of `lock` into a uniform shape.
510
+ */
511
+ normalizeLockOptions(ttlOrOptions) {
512
+ if (typeof ttlOrOptions === "number" || typeof ttlOrOptions === "string") return { ttl: ttlOrOptions };
513
+ return {
514
+ ttl: ttlOrOptions.ttl,
515
+ owner: ttlOrOptions.owner
516
+ };
517
+ }
518
+ };
519
+
520
+ //#endregion
521
+ export { BaseCacheDriver };
522
+ //# sourceMappingURL=base-cache-driver.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-cache-driver.mjs","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/base-cache-driver.ts"],"sourcesContent":["import { log } from \"@warlock.js/logger\";\r\nimport { MemoryCacheList } from \"../list/memory-cache-list\";\r\nimport { TaggedCache } from \"../tagged-cache\";\r\nimport type {\r\n CacheConflictPolicy,\r\n CacheData,\r\n CacheDriver,\r\n CacheEventData,\r\n CacheEventHandler,\r\n CacheEventType,\r\n CacheKey,\r\n CacheListAccessor,\r\n CacheOperationType,\r\n CacheSetOptions,\r\n CacheSetResult,\r\n CacheSimilarHit,\r\n CacheSimilarOptions,\r\n CacheSwrOptions,\r\n CacheTtl,\r\n LockOptions,\r\n LockOutcome,\r\n RememberOptions,\r\n} from \"../types\";\r\nimport { CacheUnsupportedError } from \"../types\";\r\nimport { normalizeToOptions, parseCacheKey, parseTtl, resolveTtl } from \"../utils\";\r\n\r\n/**\r\n * Normalized form of the 3rd `set` argument.\r\n *\r\n * All drivers operate on this shape internally regardless of whether the caller\r\n * passed a positional TTL, a duration string, or a rich options object.\r\n */\r\nexport type NormalizedSetOptions = {\r\n /**\r\n * Final TTL in seconds — already merged with the driver-level default\r\n * (`this.options.ttl`). `Infinity` means \"no expiration\".\r\n *\r\n * Always populated. Drivers do NOT need to fall back to `this.ttl`\r\n * themselves — `resolveSetOptions` does the merge centrally so that\r\n * every driver respects the configured default without ceremony.\r\n */\r\n ttl: number;\r\n /**\r\n * Inline tag list, or undefined when none were provided.\r\n */\r\n tags?: string[];\r\n /**\r\n * Conflict policy. Defaults to `\"upsert\"`.\r\n */\r\n onConflict: CacheConflictPolicy;\r\n /**\r\n * Optional embedding vector for similarity retrieval. Drivers that do not\r\n * support similarity must throw {@link CacheUnsupportedError} when this is\r\n * present (rather than silently dropping it).\r\n */\r\n vector?: number[];\r\n /**\r\n * Optional freshness deadline as a millisecond timestamp. Set by `swr()`\r\n * to mark when the entry stops being \"fresh\" and becomes\r\n * \"stale-but-revalidatable.\" Drivers route this through\r\n * `prepareDataForStorage` so it persists in the wrapper.\r\n */\r\n staleAt?: number;\r\n};\r\n\r\nconst messages = {\r\n clearing: \"Clearing namespace\",\r\n cleared: \"Namespace cleared\",\r\n fetching: \"Fetching key\",\r\n fetched: \"Key fetched\",\r\n caching: \"Caching key\",\r\n cached: \"Key cached\",\r\n flushing: \"Flushing cache\",\r\n flushed: \"Cache flushed\",\r\n removing: \"Removing key\",\r\n removed: \"Key removed\",\r\n expired: \"Key expired\",\r\n notFound: \"Key not found\",\r\n connecting: \"Connecting to the cache engine.\",\r\n connected: \"Connected to the cache engine.\",\r\n disconnecting: \"Disconnecting from the cache engine.\",\r\n disconnected: \"Disconnected from the cache engine.\",\r\n error: \"Error occurred\",\r\n};\r\n\r\nexport abstract class BaseCacheDriver<\r\n ClientType,\r\n Options extends Record<string, any>,\r\n> implements CacheDriver<ClientType, Options> {\r\n /**\r\n * CLient driver\r\n */\r\n protected clientDriver!: ClientType;\r\n\r\n /**\r\n * Determine whether to log or not\r\n */\r\n protected shouldLog: boolean = true;\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public get client() {\r\n return (this.clientDriver || this) as unknown as ClientType;\r\n }\r\n\r\n /**\r\n * Set logging state\r\n */\r\n public setLoggingState(shouldLog: boolean) {\r\n this.shouldLog = shouldLog;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Set client driver\r\n */\r\n public set client(client: ClientType) {\r\n this.clientDriver = client;\r\n }\r\n\r\n /**\r\n * Get the cache driver name\r\n */\r\n public abstract name: string;\r\n\r\n /**\r\n * Options list\r\n */\r\n public options!: Options;\r\n\r\n /**\r\n * Event listeners storage\r\n */\r\n protected eventListeners: Map<CacheEventType, Set<CacheEventHandler>> = new Map();\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public parseKey(key: CacheKey) {\r\n return parseCacheKey(key, this.options);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public setOptions(options: Options) {\r\n this.options = options || {};\r\n return this;\r\n }\r\n\r\n /**\r\n * Register an event listener\r\n */\r\n public on(event: CacheEventType, handler: CacheEventHandler): this {\r\n if (!this.eventListeners.has(event)) {\r\n this.eventListeners.set(event, new Set());\r\n }\r\n this.eventListeners.get(event)!.add(handler);\r\n return this;\r\n }\r\n\r\n /**\r\n * Remove an event listener\r\n */\r\n public off(event: CacheEventType, handler: CacheEventHandler): this {\r\n const handlers = this.eventListeners.get(event);\r\n if (handlers) {\r\n handlers.delete(handler);\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Register a one-time event listener\r\n */\r\n public once(event: CacheEventType, handler: CacheEventHandler): this {\r\n const onceHandler: CacheEventHandler = async (data) => {\r\n await handler(data);\r\n this.off(event, onceHandler);\r\n };\r\n return this.on(event, onceHandler);\r\n }\r\n\r\n /**\r\n * Emit an event to all registered listeners\r\n */\r\n protected async emit(event: CacheEventType, data: Partial<CacheEventData> = {}): Promise<void> {\r\n const handlers = this.eventListeners.get(event);\r\n if (!handlers || handlers.size === 0) return;\r\n\r\n const eventData: CacheEventData = {\r\n driver: this.name,\r\n ...data,\r\n };\r\n\r\n // Execute all handlers\r\n const promises: Promise<void>[] = [];\r\n for (const handler of handlers) {\r\n try {\r\n const result = handler(eventData);\r\n if (result instanceof Promise) {\r\n promises.push(result);\r\n }\r\n } catch (error) {\r\n this.logError(`Error in event handler for '${event}'`, error);\r\n }\r\n }\r\n\r\n // Wait for all async handlers\r\n if (promises.length > 0) {\r\n await Promise.allSettled(promises);\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public abstract removeNamespace(namespace: string): Promise<any>;\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public abstract set(\r\n key: CacheKey,\r\n value: any,\r\n ttlOrOptions?: CacheTtl | CacheSetOptions,\r\n ): Promise<any>;\r\n\r\n /**\r\n * Normalize the 3rd argument of a `set` call into a single shape every driver\r\n * can act on. Handles TTL parsing (number | string | Infinity), `expiresAt` →\r\n * relative TTL conversion, and mutual-exclusion validation.\r\n *\r\n * @throws {CacheConfigurationError} when `ttl` and `expiresAt` are passed together\r\n * or an unparseable duration string is supplied.\r\n */\r\n protected resolveSetOptions(\r\n ttlOrOptions?: CacheTtl | CacheSetOptions,\r\n ): NormalizedSetOptions {\r\n const options = normalizeToOptions(ttlOrOptions);\r\n\r\n return {\r\n ttl: resolveTtl(options.ttl, options.expiresAt, this.ttl),\r\n tags: options.tags,\r\n onConflict: options.onConflict ?? \"upsert\",\r\n vector: options.vector,\r\n staleAt: options.staleAt,\r\n };\r\n }\r\n\r\n /**\r\n * Resolve the union of cache keys associated with any of the given tags.\r\n * Used by `similar()` to narrow the candidate pool before similarity ranking.\r\n *\r\n * Returns `null` when no tags are passed (callers should treat that as \"no filter\").\r\n */\r\n protected async getKeysForTags(tags: string[] | undefined): Promise<Set<string> | null> {\r\n if (!tags || tags.length === 0) {\r\n return null;\r\n }\r\n\r\n const allKeys = new Set<string>();\r\n for (const tag of tags) {\r\n const tagKey = `cache:tags:${tag}`;\r\n const keys = ((await this.get(tagKey)) as string[] | null) || [];\r\n for (const k of keys) {\r\n allKeys.add(k);\r\n }\r\n }\r\n\r\n return allKeys;\r\n }\r\n\r\n /**\r\n * Apply tag relationships after a successful write. Called by drivers once\r\n * the value is in storage.\r\n */\r\n protected async applyTags(parsedKey: string, tags: string[]): Promise<void> {\r\n if (tags.length === 0) {\r\n return;\r\n }\r\n\r\n const tagged = this.tags(tags);\r\n await (tagged as TaggedCache).storeTagRelationship(parsedKey);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public abstract get(key: CacheKey): Promise<any>;\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public abstract remove(key: CacheKey): Promise<void>;\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public abstract flush(): Promise<void>;\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async has(key: CacheKey): Promise<boolean> {\r\n const value = await this.get(key);\r\n // Event is emitted by get() method\r\n return value !== null;\r\n }\r\n\r\n /**\r\n * Lock storage for preventing cache stampede\r\n */\r\n protected locks: Map<string, Promise<any>> = new Map();\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remember(\r\n key: CacheKey,\r\n ttlOrOptions: CacheTtl | RememberOptions,\r\n callback: () => Promise<any>,\r\n ): Promise<any> {\r\n const parsedKey = this.parseKey(key);\r\n\r\n // The options-form lets callers forward tags / driver-override through to\r\n // the cache-miss write. Normalize both shapes into a single CacheSetOptions\r\n // blob so there's one path from here on.\r\n const setOptions = this.normalizeRememberOptions(ttlOrOptions);\r\n\r\n const cachedValue = await this.get(key);\r\n if (cachedValue) {\r\n return cachedValue;\r\n }\r\n\r\n const existingLock = this.locks.get(parsedKey);\r\n if (existingLock) {\r\n return existingLock;\r\n }\r\n\r\n const promise = callback()\r\n .then(async (result) => {\r\n await this.set(key, result, setOptions);\r\n this.locks.delete(parsedKey);\r\n return result;\r\n })\r\n .catch((err) => {\r\n this.locks.delete(parsedKey);\r\n throw err;\r\n });\r\n\r\n this.locks.set(parsedKey, promise);\r\n return promise;\r\n }\r\n\r\n /**\r\n * Resolve the TTL-or-options arg of `remember` into a `CacheSetOptions` object\r\n * that can be passed straight to `set()`. Keeps the implementation unbranched.\r\n */\r\n protected normalizeRememberOptions(\r\n ttlOrOptions: CacheTtl | RememberOptions,\r\n ): CacheSetOptions {\r\n if (typeof ttlOrOptions === \"number\" || typeof ttlOrOptions === \"string\") {\r\n return { ttl: ttlOrOptions };\r\n }\r\n\r\n return {\r\n ttl: ttlOrOptions.ttl,\r\n tags: ttlOrOptions.tags,\r\n };\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Default implementation: read raw entry, branch on freshness/staleness,\r\n * trigger background refresh in the stale window, fall through to\r\n * `callback` on miss/expiry. Concurrent stale-window callers share a\r\n * single in-flight refresh via {@link locks}.\r\n *\r\n * Drivers without a real {@link getEntry} override degrade gracefully —\r\n * the synthetic entry has no `staleAt`, which the freshness check treats\r\n * as \"always fresh,\" so SWR behaves like a TTL-only cached read on those\r\n * drivers (no background refresh, but no double-fetch either).\r\n */\r\n public async swr<T = any>(\r\n key: CacheKey,\r\n options: CacheSwrOptions,\r\n callback: () => Promise<T>,\r\n ): Promise<T> {\r\n const parsedKey = this.parseKey(key);\r\n const freshSeconds = parseTtl(options.freshTtl);\r\n const staleSeconds = parseTtl(options.staleTtl);\r\n\r\n if (staleSeconds <= freshSeconds) {\r\n throw new Error(\r\n `cache.swr: 'staleTtl' (${staleSeconds}s) must be greater than 'freshTtl' (${freshSeconds}s).`,\r\n );\r\n }\r\n\r\n const entry = await this.getEntry(key);\r\n const now = Date.now();\r\n\r\n const isExpired = entry?.expiresAt !== undefined && entry.expiresAt <= now;\r\n\r\n if (!entry || isExpired) {\r\n return this.swrFetchAndStore<T>(key, options, callback, freshSeconds, staleSeconds);\r\n }\r\n\r\n const isFresh = entry.staleAt === undefined || entry.staleAt > now;\r\n\r\n if (isFresh) {\r\n return entry.data as T;\r\n }\r\n\r\n this.scheduleSwrRefresh<T>(parsedKey, key, options, callback, freshSeconds, staleSeconds);\r\n\r\n return entry.data as T;\r\n }\r\n\r\n /**\r\n * Read the raw {@link CacheData} wrapper for a key, including any\r\n * `expiresAt` / `staleAt` metadata. Default implementation falls back to\r\n * `get()` and synthesizes a metadata-less wrapper — drivers that store\r\n * the wrapper directly (memory, lru, file, redis, pg, mock) override\r\n * this to return real metadata so SWR can branch on freshness.\r\n */\r\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\r\n const value = await this.get(key);\r\n\r\n if (value === null) {\r\n return null;\r\n }\r\n\r\n return { data: value };\r\n }\r\n\r\n /**\r\n * Remaining lifetime of an existing entry, in seconds — used by TTL-preserving\r\n * writes such as `update()` / `merge()` when the caller passes no explicit\r\n * `ttl`.\r\n *\r\n * - `Infinity` — the entry exists with no expiry (preserve \"never expires\").\r\n * - positive number — seconds left before the entry expires.\r\n * - `undefined` — the key is missing or already past its deadline; the caller\r\n * should fall back to the driver default TTL.\r\n *\r\n * Default reads `expiresAt` from {@link getEntry}, which the metadata-aware\r\n * drivers (memory, lru, mock, pg) populate. Drivers that track TTL natively\r\n * and don't carry `expiresAt` in their payload (Redis) override this.\r\n */\r\n protected async getRemainingTtl(key: CacheKey): Promise<number | undefined> {\r\n const entry = await this.getEntry(key);\r\n\r\n if (!entry) {\r\n return undefined;\r\n }\r\n\r\n if (!entry.expiresAt || entry.expiresAt === Infinity) {\r\n return Infinity;\r\n }\r\n\r\n const remainingSeconds = Math.ceil((entry.expiresAt - Date.now()) / 1000);\r\n\r\n return remainingSeconds > 0 ? remainingSeconds : undefined;\r\n }\r\n\r\n /**\r\n * Block-and-fetch path of `swr()`: invoked on miss or past-`staleTtl`\r\n * expiry. Writes through `set()` with the SWR options translated into\r\n * standard `CacheSetOptions` (ttl = staleTtl, staleAt = now + freshTtl).\r\n */\r\n protected async swrFetchAndStore<T>(\r\n key: CacheKey,\r\n options: CacheSwrOptions,\r\n callback: () => Promise<T>,\r\n freshSeconds: number,\r\n staleSeconds: number,\r\n ): Promise<T> {\r\n const result = await callback();\r\n\r\n await this.set(key, result, {\r\n ttl: staleSeconds,\r\n staleAt: Date.now() + freshSeconds * 1000,\r\n tags: options.tags,\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Stale-window background refresh. Registers a single in-flight promise\r\n * per parsed key so concurrent SWR callers share one refresh. Failed\r\n * refreshes preserve the stale entry, log via `logError`, and emit on\r\n * `error` — the stale-returning caller never sees the failure.\r\n */\r\n protected scheduleSwrRefresh<T>(\r\n parsedKey: string,\r\n key: CacheKey,\r\n options: CacheSwrOptions,\r\n callback: () => Promise<T>,\r\n freshSeconds: number,\r\n staleSeconds: number,\r\n ): void {\r\n if (this.locks.has(parsedKey)) {\r\n return;\r\n }\r\n\r\n let refresh!: Promise<void>;\r\n refresh = (async () => {\r\n try {\r\n const result = await callback();\r\n\r\n await this.set(key, result, {\r\n ttl: staleSeconds,\r\n staleAt: Date.now() + freshSeconds * 1000,\r\n tags: options.tags,\r\n });\r\n } catch (error) {\r\n this.logError(`SWR background refresh failed for ${parsedKey}`, error);\r\n await this.emit(\"error\", { key: parsedKey, error });\r\n } finally {\r\n if (this.locks.get(parsedKey) === refresh) {\r\n this.locks.delete(parsedKey);\r\n }\r\n }\r\n })();\r\n\r\n this.locks.set(parsedKey, refresh);\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 if (value !== null) {\r\n await this.remove(key);\r\n }\r\n // Events are emitted by get() and remove() methods\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 // Event is emitted by set() method\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(`Cannot increment non-numeric value for key: ${this.parseKey(key)}`);\r\n }\r\n\r\n const newValue = current + value;\r\n await this.set(key, newValue);\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 /**\r\n * {@inheritdoc}\r\n */\r\n public async many(keys: CacheKey[]): Promise<any[]> {\r\n return Promise.all(keys.map((key) => this.get(key)));\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async setMany(items: Record<string, any>, ttl?: number): Promise<void> {\r\n await Promise.all(Object.entries(items).map(([key, value]) => this.set(key, value, ttl)));\r\n }\r\n\r\n /**\r\n * Log the operation\r\n */\r\n protected log(operation: CacheOperationType, key?: string) {\r\n if (!this.shouldLog) return;\r\n\r\n if (key) {\r\n // this will be likely used with file cache driver as it will convert the dot to slash\r\n // to make it consistent and not to confuse developers we will output the key by making sure it's a dot\r\n key = key.replaceAll(\"/\", \".\");\r\n }\r\n\r\n if (operation == \"notFound\" || operation == \"expired\") {\r\n return log.warn(\r\n \"cache.\" + this.name,\r\n operation,\r\n (key ? key + \" \" : \"\") + messages[operation],\r\n );\r\n }\r\n\r\n if (operation.endsWith(\"ed\")) {\r\n return log.success(\r\n \"cache.\" + this.name,\r\n operation,\r\n (key ? key + \" \" : \"\") + messages[operation],\r\n );\r\n }\r\n\r\n log.info(\"cache.\" + this.name, operation, (key ? key + \" \" : \"\") + messages[operation]);\r\n }\r\n\r\n /**\r\n * Log error message\r\n */\r\n protected logError(message: string, error?: any) {\r\n log.error(\"cache.\" + this.name, \"error\", message);\r\n if (error) {\r\n console.log(error);\r\n }\r\n }\r\n\r\n /**\r\n * Get the default TTL in seconds. Parses human-readable strings (`\"1h\"`, `\"30m\"`)\r\n * from driver options if present; falls back to `Infinity` when no default is set.\r\n */\r\n public get ttl() {\r\n if (this.options.ttl === undefined) {\r\n return Infinity;\r\n }\r\n\r\n return parseTtl(this.options.ttl);\r\n }\r\n\r\n /**\r\n * Get time to live value in milliseconds\r\n */\r\n public getExpiresAt(ttl: number = this.ttl) {\r\n if (ttl) {\r\n return new Date().getTime() + ttl * 1000;\r\n }\r\n }\r\n\r\n /**\r\n * Wrap a value with TTL and optional freshness metadata for backend\r\n * storage. `staleAt` persists alongside `expiresAt` when supplied — used\r\n * by the SWR flow to mark when the entry stops being fresh.\r\n */\r\n protected prepareDataForStorage(data: any, ttl?: number, staleAt?: number) {\r\n const preparedData: CacheData = {\r\n data,\r\n };\r\n\r\n if (ttl) {\r\n preparedData.ttl = ttl;\r\n preparedData.expiresAt = this.getExpiresAt(ttl);\r\n }\r\n\r\n if (staleAt !== undefined) {\r\n preparedData.staleAt = staleAt;\r\n }\r\n\r\n return preparedData;\r\n }\r\n\r\n /**\r\n * Parse fetched data from cache\r\n */\r\n protected async parseCachedData(key: string, data: CacheData) {\r\n this.log(\"fetched\", key);\r\n\r\n if (data.expiresAt && data.expiresAt < Date.now()) {\r\n this.remove(key);\r\n return null;\r\n }\r\n\r\n const value = data.data;\r\n\r\n // Skip cloning for primitives (immutable types)\r\n if (value === null || value === undefined) {\r\n return value;\r\n }\r\n\r\n const type = typeof value;\r\n if (type === \"string\" || type === \"number\" || type === \"boolean\") {\r\n return value;\r\n }\r\n\r\n // Deep clone objects/arrays to prevent cache mutation\r\n try {\r\n return structuredClone(value);\r\n } catch (error) {\r\n console.log(value);\r\n\r\n this.logError(\r\n `Failed to clone cached value for ${key}, typeof value: ${typeof value}`,\r\n error,\r\n );\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async connect() {\r\n this.log(\"connecting\");\r\n this.log(\"connected\");\r\n await this.emit(\"connected\");\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async disconnect() {\r\n this.log(\"disconnected\");\r\n await this.emit(\"disconnected\");\r\n }\r\n\r\n /**\r\n * Create a tagged cache instance for the given tags\r\n */\r\n public tags(tags: string[]): any {\r\n return new TaggedCache(tags, this);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Default implementation: read → transform → write under a per-key in-process\r\n * lock. Drivers that can offer stronger semantics (Redis via `WATCH`/`MULTI`)\r\n * should override.\r\n */\r\n public async update<T = any>(\r\n key: CacheKey,\r\n fn: (current: T | null) => T | null | Promise<T | null>,\r\n options: { ttl?: CacheTtl } = {},\r\n ): Promise<T | null> {\r\n const parsedKey = this.parseKey(key);\r\n\r\n // Chain each update onto the previous one for the same key so concurrent\r\n // callers are serialized end-to-end, not merely awakened together when an\r\n // earlier lock resolves.\r\n const previous = this.locks.get(parsedKey) ?? Promise.resolve();\r\n\r\n const next = previous.catch(() => undefined).then(async () => {\r\n const current = (await this.get(key)) as T | null;\r\n const result = await fn(current);\r\n\r\n if (result === null) {\r\n await this.remove(key);\r\n return null;\r\n }\r\n\r\n if (options.ttl !== undefined) {\r\n await this.set(key, result, { ttl: options.ttl });\r\n\r\n return result;\r\n }\r\n\r\n // No explicit TTL → preserve the existing entry's remaining lifetime\r\n // rather than resetting it to the driver default.\r\n const remainingTtl = await this.getRemainingTtl(key);\r\n\r\n if (remainingTtl !== undefined) {\r\n await this.set(key, result, { ttl: remainingTtl });\r\n } else {\r\n await this.set(key, result);\r\n }\r\n\r\n return result;\r\n });\r\n\r\n this.locks.set(parsedKey, next);\r\n\r\n // Clean up the slot once this link finishes — but only if nobody chained\r\n // a follow-up onto it in the meantime.\r\n next.finally(() => {\r\n if (this.locks.get(parsedKey) === next) {\r\n this.locks.delete(parsedKey);\r\n }\r\n });\r\n\r\n return next;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async merge<T extends Record<string, any> = Record<string, any>>(\r\n key: CacheKey,\r\n partial: Partial<T>,\r\n options: { ttl?: CacheTtl } = {},\r\n ): Promise<T> {\r\n const result = await this.update<T>(\r\n key,\r\n (current) => {\r\n const base = (current ?? {}) as T;\r\n return { ...base, ...partial } as T;\r\n },\r\n options,\r\n );\r\n\r\n return result as T;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Default implementation: read-mutate-write array backed by the underlying\r\n * cache entry. Concrete drivers (e.g. Redis) override with native commands.\r\n */\r\n public list<T = any>(key: CacheKey): CacheListAccessor<T> {\r\n return new MemoryCacheList<T>(this, key);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Built on top of `set({ onConflict: \"create\" })` — Redis-native `SET … NX EX`\r\n * under the hood on Redis, emulated via key-existence check on other drivers.\r\n * The lock value is the resolved `owner` (defaults to `pid.<process.pid>`).\r\n *\r\n * Always releases in `finally`, even if `fn` throws — the thrown error\r\n * propagates to the caller unchanged.\r\n */\r\n public async lock<T>(\r\n key: CacheKey,\r\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\r\n fn: () => Promise<T>,\r\n ): Promise<LockOutcome<T>> {\r\n const { ttl, owner } = this.normalizeLockOptions(ttlOrOptions);\r\n const lockOwner = owner ?? `pid.${process.pid}`;\r\n\r\n const setResult = (await this.set(key, lockOwner, {\r\n onConflict: \"create\",\r\n ttl,\r\n })) as CacheSetResult | unknown;\r\n\r\n // `onConflict` drivers return CacheSetResult. Drivers that no-op on set\r\n // (e.g. the null driver) may return anything else — treat that as\r\n // \"acquired\" since there's nothing to collide with.\r\n const wasSet =\r\n typeof setResult === \"object\" && setResult !== null && \"wasSet\" in setResult\r\n ? (setResult as CacheSetResult).wasSet\r\n : true;\r\n\r\n if (!wasSet) {\r\n return { acquired: false };\r\n }\r\n\r\n try {\r\n const value = await fn();\r\n return { acquired: true, value };\r\n } finally {\r\n await this.remove(key);\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Default implementation throws {@link CacheUnsupportedError}. Drivers that\r\n * support similarity retrieval (memory family, `pg`, `redis` w/ RediSearch)\r\n * override this with a real impl.\r\n */\r\n public async similar<T = any>(\r\n _vector: number[],\r\n _options: CacheSimilarOptions,\r\n ): Promise<CacheSimilarHit<T>[]> {\r\n throw new CacheUnsupportedError(\r\n `'${this.name}' driver does not support similarity retrieval. Use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).`,\r\n );\r\n }\r\n\r\n /**\r\n * Resolve the TTL-or-options arg of `lock` into a uniform shape.\r\n */\r\n protected normalizeLockOptions(\r\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\r\n ): { ttl: CacheTtl; owner?: string } {\r\n if (typeof ttlOrOptions === \"number\" || typeof ttlOrOptions === \"string\") {\r\n return { ttl: ttlOrOptions };\r\n }\r\n\r\n return { ttl: ttlOrOptions.ttl, owner: ttlOrOptions.owner };\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { BaseCacheDriver } from "./base-cache-driver.mjs";
2
+ import { CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheTtl, FileCacheOptions } from "../types.mjs";
3
+
4
+ //#region ../../@warlock.js/cache/src/drivers/file-cache-driver.d.ts
5
+ declare class FileCacheDriver extends BaseCacheDriver<FileCacheDriver, FileCacheOptions> implements CacheDriver<FileCacheDriver, FileCacheOptions> {
6
+ /**
7
+ * {@inheritdoc}
8
+ */
9
+ name: string;
10
+ /**
11
+ * {@inheritdoc}
12
+ */
13
+ setOptions(options: FileCacheOptions): this;
14
+ /**
15
+ * Get the cache directory
16
+ */
17
+ get directory(): string;
18
+ /**
19
+ * Get file name
20
+ */
21
+ get fileName(): string;
22
+ /**
23
+ * {@inheritdoc}
24
+ */
25
+ removeNamespace(namespace: string): Promise<this>;
26
+ /**
27
+ * {@inheritdoc}
28
+ */
29
+ set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;
30
+ /**
31
+ * {@inheritdoc}
32
+ *
33
+ * File driver does not yet ship with a file-lock primitive, so concurrent
34
+ * writers could clobber each other. Rather than ship an unsafe default, we
35
+ * throw — consumers can fall back to memory/redis for `update` until a
36
+ * proper file lock lands (tracked in `domains/cache/backlog.md`).
37
+ */
38
+ update(): Promise<never>;
39
+ /**
40
+ * {@inheritdoc}
41
+ */
42
+ merge(): Promise<never>;
43
+ /**
44
+ * Read the raw {@link CacheData} wrapper from disk, including `staleAt`
45
+ * metadata. Returns `null` for missing or expired files — `swr()`
46
+ * consumes this to branch on freshness.
47
+ */
48
+ protected getEntry(key: CacheKey): Promise<CacheData | null>;
49
+ /**
50
+ * {@inheritdoc}
51
+ */
52
+ get(key: CacheKey): Promise<any>;
53
+ /**
54
+ * {@inheritdoc}
55
+ */
56
+ remove(key: CacheKey): Promise<void>;
57
+ /**
58
+ * {@inheritdoc}
59
+ */
60
+ flush(): Promise<void>;
61
+ /**
62
+ * {@inheritdoc}
63
+ */
64
+ connect(): Promise<void>;
65
+ }
66
+ //#endregion
67
+ export { FileCacheDriver };
68
+ //# sourceMappingURL=file-cache-driver.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-cache-driver.d.mts","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/file-cache-driver.ts"],"mappings":";;;;cAmBa,eAAA,SACH,eAAA,CAAgB,eAAA,EAAiB,gBAAA,aAC9B,WAAA,CAAY,eAAA,EAAiB,gBAAA;;AAF1C;;EAOS,IAAA;EANiB;;;EAWjB,UAAA,CAAW,OAAA,EAAS,gBAAA;EAAA;;;EAAA,IAahB,SAAA,CAAA;EAgDiB;;;EAAA,IAjCjB,QAAA,CAAA;EA+GmB;;;EAlGjB,eAAA,CAAgB,SAAA,WAAiB,OAAA;EA4HhB;;;EA3GjB,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EAkLiB;;;;;;;;EAzHP,MAAA,CAAA,GAAU,OAAA;EAjIA;;;EA0IV,KAAA,CAAA,GAAS,OAAA;EAhIK;;;;;EAAA,UA2IX,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;EAlGH;;;EA4HjC,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;EAzG5B;;;EA8IW,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;EA5I9B;;;EAgKU,KAAA,CAAA,GAAK,OAAA;EA9FI;;;EAgHT,OAAA,CAAA,GAAO,OAAA;AAAA"}