@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,312 @@
1
+ import { CacheConfigurationError, CacheUnsupportedError } from "../types.mjs";
2
+ import { BaseCacheDriver } from "./base-cache-driver.mjs";
3
+ import { log } from "@warlock.js/logger";
4
+
5
+ //#region ../../@warlock.js/cache/src/drivers/redis-cache-driver.ts
6
+ /**
7
+ * Cached Redis module (loaded once, reused)
8
+ */
9
+ let RedisClient;
10
+ let isModuleExists = null;
11
+ /**
12
+ * Installation instructions for Redis package
13
+ */
14
+ const REDIS_INSTALL_INSTRUCTIONS = `
15
+ Redis cache driver requires the redis package.
16
+ Install it with:
17
+
18
+ npm install redis
19
+
20
+ Or with your preferred package manager:
21
+
22
+ pnpm add redis
23
+ yarn add redis
24
+ `.trim();
25
+ /**
26
+ * Load Redis module
27
+ */
28
+ async function loadRedis() {
29
+ try {
30
+ RedisClient = await import("redis");
31
+ isModuleExists = true;
32
+ } catch {
33
+ isModuleExists = false;
34
+ }
35
+ }
36
+ loadRedis();
37
+ var RedisCacheDriver = class extends BaseCacheDriver {
38
+ constructor(..._args) {
39
+ super(..._args);
40
+ this.name = "redis";
41
+ }
42
+ /**
43
+ * {@inheritdoc}
44
+ */
45
+ setOptions(options) {
46
+ if (!options.url && !options.host) throw new CacheConfigurationError("Redis driver requires either 'url' or 'host' option to be configured.");
47
+ return super.setOptions(options);
48
+ }
49
+ /**
50
+ * {@inheritDoc}
51
+ */
52
+ async removeNamespace(namespace) {
53
+ namespace = this.parseKey(namespace);
54
+ this.log("clearing", namespace);
55
+ const keys = await this.client?.keys(`${namespace}*`);
56
+ if (!keys || keys.length === 0) {
57
+ this.log("notFound", namespace);
58
+ return;
59
+ }
60
+ await this.client?.del(keys);
61
+ this.log("cleared", namespace);
62
+ return keys;
63
+ }
64
+ /**
65
+ * {@inheritDoc}
66
+ */
67
+ async set(key, value, ttlOrOptions) {
68
+ const parsedKey = this.parseKey(key);
69
+ const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);
70
+ if (vector) throw new CacheUnsupportedError("'redis' driver does not yet support similarity retrieval. Phase 2 (RediSearch) is on the backlog — use a memory driver or the 'pg' driver (with pgvector) for now.");
71
+ this.log("caching", parsedKey);
72
+ const serialized = JSON.stringify(value);
73
+ const hasExpiry = Boolean(ttl) && ttl !== Infinity;
74
+ let reply;
75
+ if (onConflict === "create") {
76
+ const options = { NX: true };
77
+ if (hasExpiry) options.EX = ttl;
78
+ reply = await this.client?.set(parsedKey, serialized, options);
79
+ } else if (onConflict === "update") {
80
+ const options = { XX: true };
81
+ if (hasExpiry) options.EX = ttl;
82
+ reply = await this.client?.set(parsedKey, serialized, options);
83
+ } else if (hasExpiry) reply = await this.client?.set(parsedKey, serialized, { EX: ttl });
84
+ else reply = await this.client?.set(parsedKey, serialized);
85
+ if ((onConflict === "create" || onConflict === "update") && !(reply === "OK")) return {
86
+ wasSet: false,
87
+ existing: onConflict === "create" ? await this.get(key) : null
88
+ };
89
+ if (tags && tags.length > 0) await this.applyTags(parsedKey, tags);
90
+ if (staleAt !== void 0) {
91
+ const sidecarOptions = {};
92
+ if (hasExpiry) sidecarOptions.EX = ttl;
93
+ await this.client?.set(this.swrMetaKey(parsedKey), String(staleAt), sidecarOptions);
94
+ }
95
+ this.log("cached", parsedKey);
96
+ await this.emit("set", {
97
+ key: parsedKey,
98
+ value,
99
+ ttl
100
+ });
101
+ if (onConflict === "create" || onConflict === "update") return {
102
+ wasSet: true,
103
+ existing: null
104
+ };
105
+ return value;
106
+ }
107
+ /**
108
+ * Build the sidecar key Redis uses to track SWR freshness without
109
+ * wrapping the main value JSON.
110
+ */
111
+ swrMetaKey(parsedKey) {
112
+ return `__swrmeta:${parsedKey}`;
113
+ }
114
+ /**
115
+ * Read the raw {@link CacheData} wrapper, fetching the value and the
116
+ * SWR sidecar in parallel. Returns `null` when the main key is missing
117
+ * or expired (Redis handles expiry natively, so the absence of the
118
+ * value alone tells us).
119
+ */
120
+ async getEntry(key) {
121
+ const parsedKey = this.parseKey(key);
122
+ const [valueRaw, staleAtRaw] = await Promise.all([this.client?.get(parsedKey), this.client?.get(this.swrMetaKey(parsedKey))]);
123
+ if (!valueRaw) return null;
124
+ const data = JSON.parse(valueRaw);
125
+ const staleAt = staleAtRaw ? Number(staleAtRaw) : void 0;
126
+ return staleAt !== void 0 ? {
127
+ data,
128
+ staleAt
129
+ } : { data };
130
+ }
131
+ /**
132
+ * {@inheritdoc}
133
+ *
134
+ * Redis tracks expiry natively (the payload carries no `expiresAt`), so read
135
+ * the remaining lifetime with the `TTL` command. Redis returns `-2` for a
136
+ * missing key and `-1` for a key with no expiry.
137
+ */
138
+ async getRemainingTtl(key) {
139
+ const parsedKey = this.parseKey(key);
140
+ const ttl = await this.client?.ttl(parsedKey);
141
+ if (ttl === void 0 || ttl === -2) return;
142
+ if (ttl === -1) return Infinity;
143
+ return ttl;
144
+ }
145
+ /**
146
+ * {@inheritDoc}
147
+ */
148
+ async get(key) {
149
+ key = this.parseKey(key);
150
+ this.log("fetching", key);
151
+ const value = await this.client?.get(key);
152
+ if (!value) {
153
+ this.log("notFound", key);
154
+ await this.emit("miss", { key });
155
+ return null;
156
+ }
157
+ this.log("fetched", key);
158
+ const parsedValue = JSON.parse(value);
159
+ if (parsedValue === null || parsedValue === void 0) {
160
+ await this.emit("hit", {
161
+ key,
162
+ value: parsedValue
163
+ });
164
+ return parsedValue;
165
+ }
166
+ const type = typeof parsedValue;
167
+ if (type === "string" || type === "number" || type === "boolean") {
168
+ await this.emit("hit", {
169
+ key,
170
+ value: parsedValue
171
+ });
172
+ return parsedValue;
173
+ }
174
+ try {
175
+ const clonedValue = structuredClone(parsedValue);
176
+ await this.emit("hit", {
177
+ key,
178
+ value: clonedValue
179
+ });
180
+ return clonedValue;
181
+ } catch (error) {
182
+ this.logError(`Failed to clone cached value for ${key}`, error);
183
+ throw error;
184
+ }
185
+ }
186
+ /**
187
+ * {@inheritDoc}
188
+ */
189
+ async remove(key) {
190
+ key = this.parseKey(key);
191
+ this.log("removing", key);
192
+ await this.client?.del([key, this.swrMetaKey(key)]);
193
+ this.log("removed", key);
194
+ await this.emit("removed", { key });
195
+ }
196
+ /**
197
+ * {@inheritDoc}
198
+ */
199
+ async flush() {
200
+ this.log("flushing");
201
+ if (this.options.globalPrefix) await this.removeNamespace("");
202
+ else await this.client?.flushAll();
203
+ this.log("flushed");
204
+ await this.emit("flushed");
205
+ }
206
+ /**
207
+ * {@inheritDoc}
208
+ */
209
+ async connect() {
210
+ if (this.clientDriver) return;
211
+ if (!isModuleExists) throw new Error(REDIS_INSTALL_INSTRUCTIONS);
212
+ const options = this.options;
213
+ if (options && !options.url && options.host) {
214
+ const auth = options.password || options.username ? `${options.username}:${options.password}@` : "";
215
+ if (!options.url) options.url = `redis://${auth}${options.host || "localhost"}:${options.port || 6379}`;
216
+ }
217
+ const clientOptions = {
218
+ ...options,
219
+ ...this.options.clientOptions || {}
220
+ };
221
+ this.log("connecting");
222
+ const { createClient } = RedisClient;
223
+ this.client = createClient(clientOptions);
224
+ this.client.on("error", (error) => {
225
+ this.log("error", error.message);
226
+ });
227
+ try {
228
+ await this.client.connect();
229
+ this.log("connected");
230
+ await this.emit("connected");
231
+ } catch (error) {
232
+ log.error("cache", "redis", error);
233
+ await this.emit("error", { error });
234
+ }
235
+ }
236
+ /**
237
+ * {@inheritDoc}
238
+ *
239
+ * Guards against disconnecting when the client was never created. The base
240
+ * `client` getter falls back to `this` when no client is set, so we check
241
+ * the backing `clientDriver` directly — using `this.client` for this guard
242
+ * would always be truthy and crash with "this.quit is not a function".
243
+ */
244
+ async disconnect() {
245
+ if (!this.clientDriver) return;
246
+ this.log("disconnecting");
247
+ await this.clientDriver.quit();
248
+ this.log("disconnected");
249
+ await this.emit("disconnected");
250
+ }
251
+ /**
252
+ * Atomic increment using Redis native INCRBY command
253
+ * {@inheritdoc}
254
+ */
255
+ async increment(key, value = 1) {
256
+ const parsedKey = this.parseKey(key);
257
+ this.log("caching", parsedKey);
258
+ const result = await this.client?.incrBy(parsedKey, value);
259
+ this.log("cached", parsedKey);
260
+ await this.emit("set", {
261
+ key: parsedKey,
262
+ value: result,
263
+ ttl: void 0
264
+ });
265
+ return result || 0;
266
+ }
267
+ /**
268
+ * Atomic decrement using Redis native DECRBY command
269
+ * {@inheritdoc}
270
+ */
271
+ async decrement(key, value = 1) {
272
+ const parsedKey = this.parseKey(key);
273
+ this.log("caching", parsedKey);
274
+ const result = await this.client?.decrBy(parsedKey, value);
275
+ this.log("cached", parsedKey);
276
+ await this.emit("set", {
277
+ key: parsedKey,
278
+ value: result,
279
+ ttl: void 0
280
+ });
281
+ return result || 0;
282
+ }
283
+ /**
284
+ * Set if not exists (atomic operation)
285
+ * Returns true if key was set, false if key already existed
286
+ */
287
+ async setNX(key, value, ttl) {
288
+ const parsedKey = this.parseKey(key);
289
+ this.log("caching", parsedKey);
290
+ if (ttl === void 0) ttl = this.ttl;
291
+ let result;
292
+ if (ttl && ttl !== Infinity) result = await this.client?.set(parsedKey, JSON.stringify(value), {
293
+ NX: true,
294
+ EX: ttl
295
+ });
296
+ else result = await this.client?.set(parsedKey, JSON.stringify(value), { NX: true });
297
+ const wasSet = result === "OK";
298
+ if (wasSet) {
299
+ this.log("cached", parsedKey);
300
+ await this.emit("set", {
301
+ key: parsedKey,
302
+ value,
303
+ ttl
304
+ });
305
+ } else this.log("notFound", parsedKey);
306
+ return wasSet;
307
+ }
308
+ };
309
+
310
+ //#endregion
311
+ export { RedisCacheDriver };
312
+ //# sourceMappingURL=redis-cache-driver.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-cache-driver.mjs","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/redis-cache-driver.ts"],"sourcesContent":["import { log } from \"@warlock.js/logger\";\r\nimport type { createClient } from \"redis\";\r\nimport type {\r\n CacheData,\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheSetResult,\r\n CacheTtl,\r\n RedisOptions,\r\n} from \"../types\";\r\nimport { CacheConfigurationError, CacheUnsupportedError } from \"../types\";\r\nimport { BaseCacheDriver } from \"./base-cache-driver\";\r\n\r\n// ============================================================\r\n// Lazy-loaded Redis SDK Types\r\n// ============================================================\r\n\r\n/**\r\n * Cached Redis module (loaded once, reused)\r\n */\r\nlet RedisClient: typeof import(\"redis\");\r\n\r\nlet isModuleExists: boolean | null = null;\r\n\r\n/**\r\n * Installation instructions for Redis package\r\n */\r\nconst REDIS_INSTALL_INSTRUCTIONS = `\r\nRedis cache driver requires the redis package.\r\nInstall it with:\r\n\r\n npm install redis\r\n\r\nOr with your preferred package manager:\r\n\r\n pnpm add redis\r\n yarn add redis\r\n`.trim();\r\n\r\n/**\r\n * Load Redis module\r\n */\r\nasync function loadRedis() {\r\n try {\r\n RedisClient = await import(\"redis\");\r\n isModuleExists = true;\r\n } catch {\r\n isModuleExists = false;\r\n }\r\n}\r\n\r\nloadRedis();\r\n\r\n// ============================================================\r\n// RedisCacheDriver Class\r\n// ============================================================\r\n\r\nexport class RedisCacheDriver\r\n extends BaseCacheDriver<ReturnType<typeof createClient>, RedisOptions>\r\n implements CacheDriver<ReturnType<typeof createClient>, RedisOptions>\r\n{\r\n /**\r\n * Cache driver name\r\n */\r\n public name = \"redis\";\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public setOptions(options: RedisOptions) {\r\n if (!options.url && !options.host) {\r\n throw new CacheConfigurationError(\r\n \"Redis driver requires either 'url' or 'host' option to be configured.\",\r\n );\r\n }\r\n\r\n return super.setOptions(options);\r\n }\r\n\r\n /**\r\n * {@inheritDoc}\r\n */\r\n public async removeNamespace(namespace: string) {\r\n namespace = this.parseKey(namespace);\r\n\r\n this.log(\"clearing\", namespace);\r\n\r\n const keys = await this.client?.keys(`${namespace}*`);\r\n\r\n if (!keys || keys.length === 0) {\r\n this.log(\"notFound\", namespace);\r\n return;\r\n }\r\n\r\n await this.client?.del(keys);\r\n\r\n this.log(\"cleared\", namespace);\r\n\r\n return keys;\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.parseKey(key);\r\n const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);\r\n\r\n if (vector) {\r\n throw new CacheUnsupportedError(\r\n \"'redis' driver does not yet support similarity retrieval. Phase 2 (RediSearch) is on the backlog — use a memory driver or the 'pg' driver (with pgvector) for now.\",\r\n );\r\n }\r\n\r\n this.log(\"caching\", parsedKey);\r\n\r\n const serialized = JSON.stringify(value);\r\n const hasExpiry = Boolean(ttl) && ttl !== Infinity;\r\n\r\n let reply: string | null | undefined;\r\n\r\n if (onConflict === \"create\") {\r\n const options: { NX: true; EX?: number } = { NX: true };\r\n if (hasExpiry) {\r\n options.EX = ttl as number;\r\n }\r\n reply = await this.client?.set(parsedKey, serialized, options);\r\n } else if (onConflict === \"update\") {\r\n const options: { XX: true; EX?: number } = { XX: true };\r\n if (hasExpiry) {\r\n options.EX = ttl as number;\r\n }\r\n reply = await this.client?.set(parsedKey, serialized, options);\r\n } else if (hasExpiry) {\r\n reply = await this.client?.set(parsedKey, serialized, { EX: ttl as number });\r\n } else {\r\n reply = await this.client?.set(parsedKey, serialized);\r\n }\r\n\r\n const wasSet = reply === \"OK\";\r\n\r\n if ((onConflict === \"create\" || onConflict === \"update\") && !wasSet) {\r\n const existing = onConflict === \"create\" ? ((await this.get(key)) as any) : null;\r\n return { wasSet: false, existing } satisfies CacheSetResult;\r\n }\r\n\r\n if (tags && tags.length > 0) {\r\n await this.applyTags(parsedKey, tags);\r\n }\r\n\r\n if (staleAt !== undefined) {\r\n // Sidecar key for SWR freshness — keeps the main value JSON\r\n // backwards-compatible with entries written before SWR landed.\r\n const sidecarOptions: { EX?: number } = {};\r\n\r\n if (hasExpiry) {\r\n sidecarOptions.EX = ttl as number;\r\n }\r\n\r\n await this.client?.set(this.swrMetaKey(parsedKey), String(staleAt), sidecarOptions);\r\n }\r\n\r\n this.log(\"cached\", parsedKey);\r\n\r\n await this.emit(\"set\", { key: parsedKey, value, ttl });\r\n\r\n if (onConflict === \"create\" || onConflict === \"update\") {\r\n return { wasSet: true, existing: null } satisfies CacheSetResult;\r\n }\r\n\r\n return value;\r\n }\r\n\r\n /**\r\n * Build the sidecar key Redis uses to track SWR freshness without\r\n * wrapping the main value JSON.\r\n */\r\n protected swrMetaKey(parsedKey: string): string {\r\n return `__swrmeta:${parsedKey}`;\r\n }\r\n\r\n /**\r\n * Read the raw {@link CacheData} wrapper, fetching the value and the\r\n * SWR sidecar in parallel. Returns `null` when the main key is missing\r\n * or expired (Redis handles expiry natively, so the absence of the\r\n * value alone tells us).\r\n */\r\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\r\n const parsedKey = this.parseKey(key);\r\n\r\n const [valueRaw, staleAtRaw] = await Promise.all([\r\n this.client?.get(parsedKey),\r\n this.client?.get(this.swrMetaKey(parsedKey)),\r\n ]);\r\n\r\n if (!valueRaw) {\r\n return null;\r\n }\r\n\r\n const data = JSON.parse(valueRaw);\r\n const staleAt = staleAtRaw ? Number(staleAtRaw) : undefined;\r\n\r\n return staleAt !== undefined ? { data, staleAt } : { data };\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Redis tracks expiry natively (the payload carries no `expiresAt`), so read\r\n * the remaining lifetime with the `TTL` command. Redis returns `-2` for a\r\n * missing key and `-1` for a key with no expiry.\r\n */\r\n protected async getRemainingTtl(key: CacheKey): Promise<number | undefined> {\r\n const parsedKey = this.parseKey(key);\r\n const ttl = await this.client?.ttl(parsedKey);\r\n\r\n if (ttl === undefined || ttl === -2) {\r\n return undefined;\r\n }\r\n\r\n if (ttl === -1) {\r\n return Infinity;\r\n }\r\n\r\n return ttl;\r\n }\r\n\r\n /**\r\n * {@inheritDoc}\r\n */\r\n public async get(key: CacheKey) {\r\n key = this.parseKey(key);\r\n\r\n this.log(\"fetching\", key);\r\n\r\n const value = await this.client?.get(key);\r\n\r\n if (!value) {\r\n this.log(\"notFound\", key);\r\n // Emit miss event\r\n await this.emit(\"miss\", { key });\r\n return null;\r\n }\r\n\r\n this.log(\"fetched\", key);\r\n\r\n // Parse and return the value directly (Redis handles expiration natively)\r\n const parsedValue = JSON.parse(value);\r\n\r\n // Apply cloning for immutability protection\r\n if (parsedValue === null || parsedValue === undefined) {\r\n // Emit hit event\r\n await this.emit(\"hit\", { key, value: parsedValue });\r\n return parsedValue;\r\n }\r\n\r\n const type = typeof parsedValue;\r\n if (type === \"string\" || type === \"number\" || type === \"boolean\") {\r\n // Emit hit event\r\n await this.emit(\"hit\", { key, value: parsedValue });\r\n return parsedValue;\r\n }\r\n\r\n try {\r\n const clonedValue = structuredClone(parsedValue);\r\n // Emit hit event\r\n await this.emit(\"hit\", { key, value: clonedValue });\r\n return clonedValue;\r\n } catch (error) {\r\n this.logError(`Failed to clone cached value for ${key}`, error);\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritDoc}\r\n */\r\n public async remove(key: CacheKey) {\r\n key = this.parseKey(key);\r\n\r\n this.log(\"removing\", key);\r\n\r\n // Drop the SWR sidecar alongside the main key — keeps metadata from\r\n // surviving a `remove` and confusing a later `swr` read.\r\n await this.client?.del([key, this.swrMetaKey(key)]);\r\n\r\n this.log(\"removed\", key);\r\n\r\n await this.emit(\"removed\", { key });\r\n }\r\n\r\n /**\r\n * {@inheritDoc}\r\n */\r\n public async flush() {\r\n this.log(\"flushing\");\r\n\r\n if (this.options.globalPrefix) {\r\n await this.removeNamespace(\"\");\r\n } else {\r\n await this.client?.flushAll();\r\n }\r\n\r\n this.log(\"flushed\");\r\n\r\n // Emit flushed event\r\n await this.emit(\"flushed\");\r\n }\r\n\r\n /**\r\n * {@inheritDoc}\r\n */\r\n public async connect() {\r\n if (this.clientDriver) return;\r\n\r\n if (!isModuleExists) {\r\n throw new Error(REDIS_INSTALL_INSTRUCTIONS);\r\n }\r\n\r\n const options = this.options;\r\n\r\n if (options && !options.url && options.host) {\r\n const auth =\r\n options.password || options.username ? `${options.username}:${options.password}@` : \"\";\r\n\r\n if (!options.url) {\r\n const host = options.host || \"localhost\";\r\n const port = options.port || 6379;\r\n options.url = `redis://${auth}${host}:${port}`;\r\n }\r\n }\r\n\r\n const clientOptions = {\r\n ...options,\r\n ...(this.options.clientOptions || {}),\r\n };\r\n\r\n this.log(\"connecting\");\r\n const { createClient } = RedisClient;\r\n\r\n this.client = createClient(clientOptions);\r\n\r\n this.client.on(\"error\", (error: Error) => {\r\n this.log(\"error\", error.message);\r\n });\r\n try {\r\n await this.client.connect();\r\n\r\n this.log(\"connected\");\r\n await this.emit(\"connected\");\r\n } catch (error) {\r\n log.error(\"cache\", \"redis\", error);\r\n await this.emit(\"error\", { error });\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritDoc}\r\n *\r\n * Guards against disconnecting when the client was never created. The base\r\n * `client` getter falls back to `this` when no client is set, so we check\r\n * the backing `clientDriver` directly — using `this.client` for this guard\r\n * would always be truthy and crash with \"this.quit is not a function\".\r\n */\r\n public async disconnect() {\r\n if (!this.clientDriver) {\r\n return;\r\n }\r\n\r\n this.log(\"disconnecting\");\r\n\r\n await this.clientDriver.quit();\r\n\r\n this.log(\"disconnected\");\r\n await this.emit(\"disconnected\");\r\n }\r\n\r\n /**\r\n * Atomic increment using Redis native INCRBY command\r\n * {@inheritdoc}\r\n */\r\n public async increment(key: CacheKey, value: number = 1): Promise<number> {\r\n const parsedKey = this.parseKey(key);\r\n\r\n this.log(\"caching\", parsedKey);\r\n\r\n const result = await this.client?.incrBy(parsedKey, value);\r\n\r\n this.log(\"cached\", parsedKey);\r\n\r\n // Emit set event\r\n await this.emit(\"set\", { key: parsedKey, value: result, ttl: undefined });\r\n\r\n return result || 0;\r\n }\r\n\r\n /**\r\n * Atomic decrement using Redis native DECRBY command\r\n * {@inheritdoc}\r\n */\r\n public async decrement(key: CacheKey, value: number = 1): Promise<number> {\r\n const parsedKey = this.parseKey(key);\r\n\r\n this.log(\"caching\", parsedKey);\r\n\r\n const result = await this.client?.decrBy(parsedKey, value);\r\n\r\n this.log(\"cached\", parsedKey);\r\n\r\n // Emit set event\r\n await this.emit(\"set\", { key: parsedKey, value: result, ttl: undefined });\r\n\r\n return result || 0;\r\n }\r\n\r\n /**\r\n * Set if not exists (atomic operation)\r\n * Returns true if key was set, false if key already existed\r\n */\r\n public async setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\r\n const parsedKey = this.parseKey(key);\r\n\r\n this.log(\"caching\", parsedKey);\r\n\r\n if (ttl === undefined) {\r\n ttl = this.ttl;\r\n }\r\n\r\n let result: string | null;\r\n\r\n // Use Redis native SET with NX option\r\n if (ttl && ttl !== Infinity) {\r\n result = await this.client?.set(parsedKey, JSON.stringify(value), {\r\n NX: true,\r\n EX: ttl,\r\n });\r\n } else {\r\n result = await this.client?.set(parsedKey, JSON.stringify(value), {\r\n NX: true,\r\n });\r\n }\r\n\r\n const wasSet = result === \"OK\";\r\n\r\n if (wasSet) {\r\n this.log(\"cached\", parsedKey);\r\n // Emit set event\r\n await this.emit(\"set\", { key: parsedKey, value, ttl });\r\n } else {\r\n this.log(\"notFound\", parsedKey);\r\n }\r\n\r\n return wasSet;\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { BaseCacheDriver, NormalizedSetOptions } from "./drivers/base-cache-driver.mjs";
2
+ import { FileCacheDriver } from "./drivers/file-cache-driver.mjs";
3
+ import { LRUMemoryCacheDriver } from "./drivers/lru-memory-cache-driver.mjs";
4
+ import { MemoryCacheDriver } from "./drivers/memory-cache-driver.mjs";
5
+ import { MemoryExtendedCacheDriver } from "./drivers/memory-extended-cache-driver.mjs";
6
+ import { MockCacheDriver } from "./drivers/mock-cache-driver.mjs";
7
+ import { NullCacheDriver } from "./drivers/null-cache-driver.mjs";
8
+ import { PgCacheDriver } from "./drivers/pg-cache-driver.mjs";
9
+ import { RedisCacheDriver } from "./drivers/redis-cache-driver.mjs";
10
+ import { CacheCall, CacheConcurrencyError, CacheConfigurationError, CacheConfigurations, CacheConflictPolicy, CacheConnectionError, CacheData, CacheDriver, CacheDriverNotInitializedError, CacheError, CacheEventData, CacheEventHandler, CacheEventType, CacheKey, CacheListAccessor, CacheMetricsSnapshot, CacheNamespaceOptions, CacheOperationType, CacheSetOptions, CacheSetResult, CacheSimilarHit, CacheSimilarOptions, CacheSwrOptions, CacheTtl, CacheUnsupportedError, DriverClass, FileCacheOptions, LRUMemoryCacheOptions, LockOptions, LockOutcome, MemoryCacheOptions, MemoryExtendedCacheOptions, MockCacheOptions, NullCacheDriverOptions, PgCacheOptions, PgClientLike, RedisOptions, RememberOptions, ScopedCacheContract, TaggedCacheDriver, TaggedScopedCacheContract } from "./types.mjs";
11
+ import { CacheMetricsCollector } from "./metrics.mjs";
12
+ import { CacheManager, cache } from "./cache-manager.mjs";
13
+ import { deriveAutoKey } from "./cached/auto-key.mjs";
14
+ import { CachedOptions, NormalizedCachedConfig, normalizeCachedArgs } from "./cached/normalize-args.mjs";
15
+ import { CachedFn, cached } from "./cached/cached.mjs";
16
+ import { MemoryCacheList } from "./list/memory-cache-list.mjs";
17
+ import { ScopedCache } from "./scoped-cache.mjs";
18
+ import { TaggedCache } from "./tagged-cache.mjs";
19
+ import { TaggedScopedCache } from "./tagged-scoped-cache.mjs";
20
+ import { CACHE_FOR, cosineSimilarity, expiresAtToTtl, injectTags, mergeTagSets, normalizeToOptions, normalizeToRememberOptions, parseCacheKey, parseTtl, resolveTtl } from "./utils.mjs";
21
+ export { BaseCacheDriver, CACHE_FOR, CacheCall, CacheConcurrencyError, CacheConfigurationError, CacheConfigurations, CacheConflictPolicy, CacheConnectionError, CacheData, CacheDriver, CacheDriverNotInitializedError, CacheError, CacheEventData, CacheEventHandler, CacheEventType, CacheKey, CacheListAccessor, CacheManager, CacheMetricsCollector, CacheMetricsSnapshot, CacheNamespaceOptions, CacheOperationType, CacheSetOptions, CacheSetResult, CacheSimilarHit, CacheSimilarOptions, CacheSwrOptions, CacheTtl, CacheUnsupportedError, CachedFn, CachedOptions, DriverClass, FileCacheDriver, FileCacheOptions, LRUMemoryCacheDriver, LRUMemoryCacheOptions, LockOptions, LockOutcome, MemoryCacheDriver, MemoryCacheList, MemoryCacheOptions, MemoryExtendedCacheDriver, MemoryExtendedCacheOptions, MockCacheDriver, MockCacheOptions, NormalizedCachedConfig, NormalizedSetOptions, NullCacheDriver, NullCacheDriverOptions, PgCacheDriver, PgCacheOptions, PgClientLike, RedisCacheDriver, RedisOptions, RememberOptions, ScopedCache, ScopedCacheContract, TaggedCache, TaggedCacheDriver, TaggedScopedCache, TaggedScopedCacheContract, cache, cached, cosineSimilarity, deriveAutoKey, expiresAtToTtl, injectTags, mergeTagSets, normalizeCachedArgs, normalizeToOptions, normalizeToRememberOptions, parseCacheKey, parseTtl, resolveTtl };
package/esm/index.mjs ADDED
@@ -0,0 +1,24 @@
1
+ import { CacheMetricsCollector } from "./metrics.mjs";
2
+ import { CacheConcurrencyError, CacheConfigurationError, CacheConnectionError, CacheDriverNotInitializedError, CacheError, CacheUnsupportedError } from "./types.mjs";
3
+ import { CACHE_FOR, cosineSimilarity, expiresAtToTtl, injectTags, mergeTagSets, normalizeToOptions, normalizeToRememberOptions, parseCacheKey, parseTtl, resolveTtl } from "./utils.mjs";
4
+ import { TaggedScopedCache } from "./tagged-scoped-cache.mjs";
5
+ import { ScopedCache } from "./scoped-cache.mjs";
6
+ import { CacheManager, cache } from "./cache-manager.mjs";
7
+ import { deriveAutoKey } from "./cached/auto-key.mjs";
8
+ import { normalizeCachedArgs } from "./cached/normalize-args.mjs";
9
+ import { cached } from "./cached/cached.mjs";
10
+ import "./cached/index.mjs";
11
+ import { MemoryCacheList } from "./list/memory-cache-list.mjs";
12
+ import { TaggedCache } from "./tagged-cache.mjs";
13
+ import { BaseCacheDriver } from "./drivers/base-cache-driver.mjs";
14
+ import { FileCacheDriver } from "./drivers/file-cache-driver.mjs";
15
+ import { LRUMemoryCacheDriver } from "./drivers/lru-memory-cache-driver.mjs";
16
+ import { MemoryCacheDriver } from "./drivers/memory-cache-driver.mjs";
17
+ import { MemoryExtendedCacheDriver } from "./drivers/memory-extended-cache-driver.mjs";
18
+ import { MockCacheDriver } from "./drivers/mock-cache-driver.mjs";
19
+ import { NullCacheDriver } from "./drivers/null-cache-driver.mjs";
20
+ import { PgCacheDriver } from "./drivers/pg-cache-driver.mjs";
21
+ import { RedisCacheDriver } from "./drivers/redis-cache-driver.mjs";
22
+ import "./drivers/index.mjs";
23
+
24
+ export { BaseCacheDriver, CACHE_FOR, CacheConcurrencyError, CacheConfigurationError, CacheConnectionError, CacheDriverNotInitializedError, CacheError, CacheManager, CacheMetricsCollector, CacheUnsupportedError, FileCacheDriver, LRUMemoryCacheDriver, MemoryCacheDriver, MemoryCacheList, MemoryExtendedCacheDriver, MockCacheDriver, NullCacheDriver, PgCacheDriver, RedisCacheDriver, ScopedCache, TaggedCache, TaggedScopedCache, cache, cached, cosineSimilarity, deriveAutoKey, expiresAtToTtl, injectTags, mergeTagSets, normalizeCachedArgs, normalizeToOptions, normalizeToRememberOptions, parseCacheKey, parseTtl, resolveTtl };
@@ -0,0 +1 @@
1
+ import { MemoryCacheList } from "./memory-cache-list.mjs";
@@ -0,0 +1,77 @@
1
+ import { CacheDriver, CacheKey, CacheListAccessor } from "../types.mjs";
2
+
3
+ //#region ../../@warlock.js/cache/src/list/memory-cache-list.d.ts
4
+ /**
5
+ * Generic array-backed {@link CacheListAccessor}.
6
+ *
7
+ * Stores the full list as a single cache entry and performs read-mutate-write
8
+ * for every operation. Correct for any driver, but O(n) per op. The Redis
9
+ * driver overrides `list()` to return a native-command accessor instead.
10
+ *
11
+ * **Role.** Fallback list accessor bound to a driver + key. Every mutation
12
+ * fetches the array, transforms it in memory, and writes it back.
13
+ *
14
+ * **Responsibility.**
15
+ * - Owns: translating list operations into array mutations + driver writes.
16
+ * - Does NOT own: concurrency control (callers should wrap in a distributed
17
+ * lock when multi-process writers are possible), TTL preservation across
18
+ * ops (writes use driver defaults), or tagging of list entries.
19
+ *
20
+ * @example
21
+ * // Never constructed directly — obtained via driver.list():
22
+ * const list = cache.list<Event>("recent-events");
23
+ * await list.push(event);
24
+ */
25
+ declare class MemoryCacheList<T> implements CacheListAccessor<T> {
26
+ private readonly driver;
27
+ private readonly key;
28
+ constructor(driver: CacheDriver<any, any>, key: CacheKey);
29
+ /**
30
+ * Read the backing array from the driver. Returns an empty array on miss.
31
+ */
32
+ private read;
33
+ /**
34
+ * Persist the backing array. Removes the entry when empty to keep the
35
+ * store clean.
36
+ */
37
+ private write;
38
+ /**
39
+ * {@inheritdoc}
40
+ */
41
+ push(...items: T[]): Promise<number>;
42
+ /**
43
+ * {@inheritdoc}
44
+ */
45
+ unshift(...items: T[]): Promise<number>;
46
+ /**
47
+ * {@inheritdoc}
48
+ */
49
+ pop(): Promise<T | null>;
50
+ /**
51
+ * {@inheritdoc}
52
+ */
53
+ shift(): Promise<T | null>;
54
+ /**
55
+ * {@inheritdoc}
56
+ */
57
+ slice(start?: number, end?: number): Promise<T[]>;
58
+ /**
59
+ * {@inheritdoc}
60
+ */
61
+ all(): Promise<T[]>;
62
+ /**
63
+ * {@inheritdoc}
64
+ */
65
+ length(): Promise<number>;
66
+ /**
67
+ * {@inheritdoc}
68
+ */
69
+ trim(start: number, end: number): Promise<void>;
70
+ /**
71
+ * {@inheritdoc}
72
+ */
73
+ clear(): Promise<void>;
74
+ }
75
+ //#endregion
76
+ export { MemoryCacheList };
77
+ //# sourceMappingURL=memory-cache-list.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-cache-list.d.mts","names":[],"sources":["../../../../../../@warlock.js/cache/src/list/memory-cache-list.ts"],"mappings":";;;;;AAuBA;;;;;;;;;;;;;;;;;;;cAAa,eAAA,eAA8B,iBAAA,CAAkB,CAAA;EAAA,iBAExC,MAAA;EAAA,iBACA,GAAA;cADA,MAAA,EAAQ,WAAA,YACR,GAAA,EAAK,QAAA;EAHkC;;;EAAA,QAS5C,IAAA;EAPK;;;;EAAA,QAgBL,KAAA;EAfU;;;EA2BX,IAAA,CAAA,GAAQ,KAAA,EAAO,CAAA,KAAM,OAAA;EAArB;;;EAWA,OAAA,CAAA,GAAW,KAAA,EAAO,CAAA,KAAM,OAAA;EAAxB;;;EAWA,GAAA,CAAA,GAAO,OAAA,CAAQ,CAAA;EAAf;;;EAgBA,KAAA,CAAA,GAAS,OAAA,CAAQ,CAAA;EAAR;;;EAgBT,KAAA,CAAM,KAAA,WAAgB,GAAA,YAAe,OAAA,CAAQ,CAAA;EAAvB;;;EAQtB,GAAA,CAAA,GAAO,OAAA,CAAQ,CAAA;EAAR;;;EAOP,MAAA,CAAA,GAAU,OAAA;EAQV;;;EAAA,IAAA,CAAK,KAAA,UAAe,GAAA,WAAc,OAAA;EASlC;;;EAAA,KAAA,CAAA,GAAS,OAAA;AAAA"}
@@ -0,0 +1,119 @@
1
+ //#region ../../@warlock.js/cache/src/list/memory-cache-list.ts
2
+ /**
3
+ * Generic array-backed {@link CacheListAccessor}.
4
+ *
5
+ * Stores the full list as a single cache entry and performs read-mutate-write
6
+ * for every operation. Correct for any driver, but O(n) per op. The Redis
7
+ * driver overrides `list()` to return a native-command accessor instead.
8
+ *
9
+ * **Role.** Fallback list accessor bound to a driver + key. Every mutation
10
+ * fetches the array, transforms it in memory, and writes it back.
11
+ *
12
+ * **Responsibility.**
13
+ * - Owns: translating list operations into array mutations + driver writes.
14
+ * - Does NOT own: concurrency control (callers should wrap in a distributed
15
+ * lock when multi-process writers are possible), TTL preservation across
16
+ * ops (writes use driver defaults), or tagging of list entries.
17
+ *
18
+ * @example
19
+ * // Never constructed directly — obtained via driver.list():
20
+ * const list = cache.list<Event>("recent-events");
21
+ * await list.push(event);
22
+ */
23
+ var MemoryCacheList = class {
24
+ constructor(driver, key) {
25
+ this.driver = driver;
26
+ this.key = key;
27
+ }
28
+ /**
29
+ * Read the backing array from the driver. Returns an empty array on miss.
30
+ */
31
+ async read() {
32
+ const current = await this.driver.get(this.key);
33
+ return Array.isArray(current) ? [...current] : [];
34
+ }
35
+ /**
36
+ * Persist the backing array. Removes the entry when empty to keep the
37
+ * store clean.
38
+ */
39
+ async write(items) {
40
+ if (items.length === 0) {
41
+ await this.driver.remove(this.key);
42
+ return;
43
+ }
44
+ await this.driver.set(this.key, items);
45
+ }
46
+ /**
47
+ * {@inheritdoc}
48
+ */
49
+ async push(...items) {
50
+ const current = await this.read();
51
+ current.push(...items);
52
+ await this.write(current);
53
+ return current.length;
54
+ }
55
+ /**
56
+ * {@inheritdoc}
57
+ */
58
+ async unshift(...items) {
59
+ const current = await this.read();
60
+ current.unshift(...items);
61
+ await this.write(current);
62
+ return current.length;
63
+ }
64
+ /**
65
+ * {@inheritdoc}
66
+ */
67
+ async pop() {
68
+ const current = await this.read();
69
+ if (current.length === 0) return null;
70
+ const value = current.pop();
71
+ await this.write(current);
72
+ return value;
73
+ }
74
+ /**
75
+ * {@inheritdoc}
76
+ */
77
+ async shift() {
78
+ const current = await this.read();
79
+ if (current.length === 0) return null;
80
+ const value = current.shift();
81
+ await this.write(current);
82
+ return value;
83
+ }
84
+ /**
85
+ * {@inheritdoc}
86
+ */
87
+ async slice(start, end) {
88
+ return (await this.read()).slice(start, end);
89
+ }
90
+ /**
91
+ * {@inheritdoc}
92
+ */
93
+ async all() {
94
+ return this.read();
95
+ }
96
+ /**
97
+ * {@inheritdoc}
98
+ */
99
+ async length() {
100
+ return (await this.read()).length;
101
+ }
102
+ /**
103
+ * {@inheritdoc}
104
+ */
105
+ async trim(start, end) {
106
+ const trimmed = (await this.read()).slice(start, end + 1);
107
+ await this.write(trimmed);
108
+ }
109
+ /**
110
+ * {@inheritdoc}
111
+ */
112
+ async clear() {
113
+ await this.driver.remove(this.key);
114
+ }
115
+ };
116
+
117
+ //#endregion
118
+ export { MemoryCacheList };
119
+ //# sourceMappingURL=memory-cache-list.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-cache-list.mjs","names":[],"sources":["../../../../../../@warlock.js/cache/src/list/memory-cache-list.ts"],"sourcesContent":["import type { CacheDriver, CacheKey, CacheListAccessor } from \"../types\";\n\n/**\n * Generic array-backed {@link CacheListAccessor}.\n *\n * Stores the full list as a single cache entry and performs read-mutate-write\n * for every operation. Correct for any driver, but O(n) per op. The Redis\n * driver overrides `list()` to return a native-command accessor instead.\n *\n * **Role.** Fallback list accessor bound to a driver + key. Every mutation\n * fetches the array, transforms it in memory, and writes it back.\n *\n * **Responsibility.**\n * - Owns: translating list operations into array mutations + driver writes.\n * - Does NOT own: concurrency control (callers should wrap in a distributed\n * lock when multi-process writers are possible), TTL preservation across\n * ops (writes use driver defaults), or tagging of list entries.\n *\n * @example\n * // Never constructed directly — obtained via driver.list():\n * const list = cache.list<Event>(\"recent-events\");\n * await list.push(event);\n */\nexport class MemoryCacheList<T> implements CacheListAccessor<T> {\n public constructor(\n private readonly driver: CacheDriver<any, any>,\n private readonly key: CacheKey,\n ) {}\n\n /**\n * Read the backing array from the driver. Returns an empty array on miss.\n */\n private async read(): Promise<T[]> {\n const current = (await this.driver.get(this.key)) as T[] | null;\n return Array.isArray(current) ? [...current] : [];\n }\n\n /**\n * Persist the backing array. Removes the entry when empty to keep the\n * store clean.\n */\n private async write(items: T[]): Promise<void> {\n if (items.length === 0) {\n await this.driver.remove(this.key);\n return;\n }\n\n await this.driver.set(this.key, items);\n }\n\n /**\n * {@inheritdoc}\n */\n public async push(...items: T[]): Promise<number> {\n const current = await this.read();\n current.push(...items);\n await this.write(current);\n\n return current.length;\n }\n\n /**\n * {@inheritdoc}\n */\n public async unshift(...items: T[]): Promise<number> {\n const current = await this.read();\n current.unshift(...items);\n await this.write(current);\n\n return current.length;\n }\n\n /**\n * {@inheritdoc}\n */\n public async pop(): Promise<T | null> {\n const current = await this.read();\n\n if (current.length === 0) {\n return null;\n }\n\n const value = current.pop() as T;\n await this.write(current);\n\n return value;\n }\n\n /**\n * {@inheritdoc}\n */\n public async shift(): Promise<T | null> {\n const current = await this.read();\n\n if (current.length === 0) {\n return null;\n }\n\n const value = current.shift() as T;\n await this.write(current);\n\n return value;\n }\n\n /**\n * {@inheritdoc}\n */\n public async slice(start?: number, end?: number): Promise<T[]> {\n const current = await this.read();\n return current.slice(start, end);\n }\n\n /**\n * {@inheritdoc}\n */\n public async all(): Promise<T[]> {\n return this.read();\n }\n\n /**\n * {@inheritdoc}\n */\n public async length(): Promise<number> {\n const current = await this.read();\n return current.length;\n }\n\n /**\n * {@inheritdoc}\n */\n public async trim(start: number, end: number): Promise<void> {\n const current = await this.read();\n const trimmed = current.slice(start, end + 1);\n await this.write(trimmed);\n }\n\n /**\n * {@inheritdoc}\n */\n public async clear(): Promise<void> {\n await this.driver.remove(this.key);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA,IAAa,kBAAb,MAAgE;CAC9D,AAAO,YACL,AAAiB,QACjB,AAAiB,KACjB;EAFiB;EACA;CAChB;;;;CAKH,MAAc,OAAqB;EACjC,MAAM,UAAW,MAAM,KAAK,OAAO,IAAI,KAAK,GAAG;EAC/C,OAAO,MAAM,QAAQ,OAAO,IAAI,CAAC,GAAG,OAAO,IAAI,CAAC;CAClD;;;;;CAMA,MAAc,MAAM,OAA2B;EAC7C,IAAI,MAAM,WAAW,GAAG;GACtB,MAAM,KAAK,OAAO,OAAO,KAAK,GAAG;GACjC;EACF;EAEA,MAAM,KAAK,OAAO,IAAI,KAAK,KAAK,KAAK;CACvC;;;;CAKA,MAAa,KAAK,GAAG,OAA6B;EAChD,MAAM,UAAU,MAAM,KAAK,KAAK;EAChC,QAAQ,KAAK,GAAG,KAAK;EACrB,MAAM,KAAK,MAAM,OAAO;EAExB,OAAO,QAAQ;CACjB;;;;CAKA,MAAa,QAAQ,GAAG,OAA6B;EACnD,MAAM,UAAU,MAAM,KAAK,KAAK;EAChC,QAAQ,QAAQ,GAAG,KAAK;EACxB,MAAM,KAAK,MAAM,OAAO;EAExB,OAAO,QAAQ;CACjB;;;;CAKA,MAAa,MAAyB;EACpC,MAAM,UAAU,MAAM,KAAK,KAAK;EAEhC,IAAI,QAAQ,WAAW,GACrB,OAAO;EAGT,MAAM,QAAQ,QAAQ,IAAI;EAC1B,MAAM,KAAK,MAAM,OAAO;EAExB,OAAO;CACT;;;;CAKA,MAAa,QAA2B;EACtC,MAAM,UAAU,MAAM,KAAK,KAAK;EAEhC,IAAI,QAAQ,WAAW,GACrB,OAAO;EAGT,MAAM,QAAQ,QAAQ,MAAM;EAC5B,MAAM,KAAK,MAAM,OAAO;EAExB,OAAO;CACT;;;;CAKA,MAAa,MAAM,OAAgB,KAA4B;EAE7D,QAAO,MADe,KAAK,KAAK,GACjB,MAAM,OAAO,GAAG;CACjC;;;;CAKA,MAAa,MAAoB;EAC/B,OAAO,KAAK,KAAK;CACnB;;;;CAKA,MAAa,SAA0B;EAErC,QAAO,MADe,KAAK,KAAK,GACjB;CACjB;;;;CAKA,MAAa,KAAK,OAAe,KAA4B;EAE3D,MAAM,WAAU,MADM,KAAK,KAAK,GACR,MAAM,OAAO,MAAM,CAAC;EAC5C,MAAM,KAAK,MAAM,OAAO;CAC1B;;;;CAKA,MAAa,QAAuB;EAClC,MAAM,KAAK,OAAO,OAAO,KAAK,GAAG;CACnC;AACF"}