@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,486 @@
1
+ import { CacheMetricsCollector } from "./metrics.mjs";
2
+ import { CacheConfigurationError, CacheDriverNotInitializedError } from "./types.mjs";
3
+ import { ScopedCache } from "./scoped-cache.mjs";
4
+
5
+ //#region ../../@warlock.js/cache/src/cache-manager.ts
6
+ var CacheManager = class {
7
+ constructor() {
8
+ this.loadedDrivers = {};
9
+ this.configurations = {
10
+ drivers: {},
11
+ options: {}
12
+ };
13
+ this.globalEventListeners = /* @__PURE__ */ new Map();
14
+ this.name = "cacheManager";
15
+ }
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ get client() {
20
+ return this.currentDriver?.client;
21
+ }
22
+ /**
23
+ * Set the cache configurations
24
+ */
25
+ setCacheConfigurations(configurations) {
26
+ this.configurations.default = configurations.default;
27
+ this.configurations.drivers = configurations.drivers;
28
+ this.configurations.options = configurations.options;
29
+ this.configurations.logging = configurations.logging;
30
+ }
31
+ /**
32
+ * Set logging state
33
+ */
34
+ setLoggingState(loggingState) {
35
+ this.ensureDriverInitialized();
36
+ this.currentDriver.setLoggingState(loggingState);
37
+ }
38
+ /**
39
+ * Switch the manager to a registered driver, optionally injecting runtime
40
+ * options that merge over the static config.
41
+ *
42
+ * The string form looks the driver up in `setCacheConfigurations({ drivers })`,
43
+ * loads it (or returns the cached instance), and sets it as `currentDriver`.
44
+ * The instance form takes a pre-built driver and bypasses the registry; the
45
+ * `runtimeOptions` argument is silently ignored in that case because the
46
+ * instance was constructed externally.
47
+ *
48
+ * Runtime options merge over `config.options[name]` per-key — runtime wins.
49
+ * Use this for constructor-only knobs that can't live in static config
50
+ * (e.g. `pg`'s `client: pg.Pool`).
51
+ *
52
+ * @example
53
+ * const pool = new Pool({ connectionString });
54
+ * await cache.use("pg", { client: pool });
55
+ */
56
+ async use(driver, runtimeOptions) {
57
+ if (typeof driver === "string") {
58
+ const driverInstance = await this.load(driver, runtimeOptions);
59
+ if (!driverInstance) throw new CacheConfigurationError(`Cache driver ${driver} is not found, please declare it in the cache drivers in the configurations list.`);
60
+ driver = driverInstance;
61
+ }
62
+ this.attachGlobalListeners(driver);
63
+ if (this.configurations.logging !== void 0) driver.setLoggingState(this.configurations.logging);
64
+ this.currentDriver = driver;
65
+ return this;
66
+ }
67
+ /**
68
+ * Ensure driver is initialized before operations
69
+ */
70
+ ensureDriverInitialized() {
71
+ if (!this.currentDriver) throw new CacheDriverNotInitializedError();
72
+ }
73
+ /**
74
+ * Return the running metrics snapshot — counters, hit-rate, latency
75
+ * percentiles, per-driver breakdowns. Lazy-attaches the collector on
76
+ * first call so apps that never read metrics pay zero cost.
77
+ *
78
+ * @example
79
+ * const m = cache.metrics();
80
+ * console.log(`hit rate: ${(m.hitRate * 100).toFixed(1)}%`);
81
+ * console.log(`p95: ${m.latencyMs.p95.toFixed(2)}ms`);
82
+ */
83
+ metrics() {
84
+ return this.ensureMetricsCollector().snapshot();
85
+ }
86
+ /**
87
+ * Wipe every counter + latency sample and reset `startedAt` to now.
88
+ * The collector itself stays subscribed to events.
89
+ */
90
+ resetMetrics() {
91
+ this.ensureMetricsCollector().reset();
92
+ }
93
+ /**
94
+ * Lazy-construct the metrics collector and wire it to the global event
95
+ * bus. Subsequent calls return the same instance — survives `cache.use()`
96
+ * driver switches because handlers attach via `on()` and re-bind to every
97
+ * loaded driver.
98
+ */
99
+ ensureMetricsCollector() {
100
+ if (this.metricsCollector) return this.metricsCollector;
101
+ const collector = new CacheMetricsCollector();
102
+ this.on("hit", (data) => collector.recordEvent("hit", data));
103
+ this.on("miss", (data) => collector.recordEvent("miss", data));
104
+ this.on("set", (data) => collector.recordEvent("set", data));
105
+ this.on("removed", (data) => collector.recordEvent("removed", data));
106
+ this.on("error", (data) => collector.recordEvent("error", data));
107
+ this.metricsCollector = collector;
108
+ return collector;
109
+ }
110
+ /**
111
+ * Time the body, record the elapsed milliseconds against the metrics
112
+ * collector for the given driver (defaults to the current driver's name).
113
+ * Pass-through if the collector hasn't been instantiated yet — apps that
114
+ * don't read metrics never pay for sample collection.
115
+ */
116
+ async timed(body, driverName) {
117
+ if (!this.metricsCollector) return body();
118
+ const start = performance.now();
119
+ try {
120
+ return await body();
121
+ } finally {
122
+ const elapsed = performance.now() - start;
123
+ const name = driverName ?? this.currentDriver?.name ?? "unknown";
124
+ this.metricsCollector.recordLatency(name, elapsed);
125
+ }
126
+ }
127
+ /**
128
+ * {@inheritdoc}
129
+ */
130
+ async get(key) {
131
+ this.ensureDriverInitialized();
132
+ return this.timed(() => this.currentDriver.get(key));
133
+ }
134
+ /**
135
+ * Set a value in the cache.
136
+ *
137
+ * Accepts a positional TTL (number of seconds or duration string like `"1h"`)
138
+ * or a rich {@link CacheSetOptions} object supporting `ttl`, `expiresAt`,
139
+ * `tags`, `onConflict`, `namespace`, and per-call `driver` overrides.
140
+ */
141
+ async set(key, value, ttlOrOptions) {
142
+ this.ensureDriverInitialized();
143
+ const driverOverride = ttlOrOptions && typeof ttlOrOptions === "object" && "driver" in ttlOrOptions ? ttlOrOptions.driver : void 0;
144
+ if (driverOverride) {
145
+ const driver = await this.load(driverOverride);
146
+ return this.timed(() => driver.set(key, value, ttlOrOptions), driver.name);
147
+ }
148
+ return this.timed(() => this.currentDriver.set(key, value, ttlOrOptions));
149
+ }
150
+ /**
151
+ * {@inheritdoc}
152
+ */
153
+ async remove(key) {
154
+ this.ensureDriverInitialized();
155
+ return this.timed(() => this.currentDriver.remove(key));
156
+ }
157
+ /**
158
+ * {@inheritdoc}
159
+ */
160
+ async removeNamespace(namespace) {
161
+ this.ensureDriverInitialized();
162
+ return this.currentDriver.removeNamespace(namespace);
163
+ }
164
+ /**
165
+ * {@inheritdoc}
166
+ */
167
+ async flush() {
168
+ this.ensureDriverInitialized();
169
+ return this.currentDriver.flush();
170
+ }
171
+ /**
172
+ * {@inheritdoc}
173
+ */
174
+ async connect() {
175
+ this.ensureDriverInitialized();
176
+ return this.currentDriver.connect();
177
+ }
178
+ /**
179
+ * {@inheritdoc}
180
+ */
181
+ parseKey(key) {
182
+ this.ensureDriverInitialized();
183
+ return this.currentDriver.parseKey(key);
184
+ }
185
+ /**
186
+ * {@inheritdoc}
187
+ */
188
+ get options() {
189
+ this.ensureDriverInitialized();
190
+ return this.currentDriver.options;
191
+ }
192
+ /**
193
+ * {@inheritdoc}
194
+ */
195
+ setOptions(options) {
196
+ this.ensureDriverInitialized();
197
+ return this.currentDriver.setOptions(options || {});
198
+ }
199
+ /**
200
+ * Return the loaded driver instance for `driverName`, loading it on first
201
+ * call. Optional `runtimeOptions` follow the same merge-over-config rules
202
+ * as {@link load}; passing options after the driver has already been
203
+ * loaded throws to avoid silent swallowing.
204
+ */
205
+ async driver(driverName, runtimeOptions) {
206
+ if (this.loadedDrivers[driverName]) {
207
+ this.assertNoConflictingReload(driverName, runtimeOptions);
208
+ return this.loadedDrivers[driverName];
209
+ }
210
+ return this.load(driverName, runtimeOptions);
211
+ }
212
+ /**
213
+ * Initialize the cache manager and pick the default driver
214
+ */
215
+ async init() {
216
+ const defaultCacheDriverName = this.configurations.default;
217
+ if (!defaultCacheDriverName) return;
218
+ const driver = await this.driver(defaultCacheDriverName);
219
+ await this.use(driver);
220
+ }
221
+ /**
222
+ * Load and connect the registered driver named `driver`. First-call wins —
223
+ * subsequent calls without `runtimeOptions` return the cached instance, and
224
+ * subsequent calls *with* `runtimeOptions` throw {@link CacheConfigurationError}
225
+ * to avoid silently dropping the new options.
226
+ *
227
+ * `runtimeOptions` merge over `config.options[driver]` per-key (runtime wins),
228
+ * letting consumers split static knobs (table, ttl, globalPrefix) from
229
+ * constructor-only ones (pg's `client`, custom adapters, etc.).
230
+ *
231
+ * @example
232
+ * const pool = new Pool({ connectionString });
233
+ * const pg = await cache.load("pg", { client: pool });
234
+ */
235
+ async load(driver, runtimeOptions) {
236
+ if (this.loadedDrivers[driver]) {
237
+ this.assertNoConflictingReload(driver, runtimeOptions);
238
+ return this.loadedDrivers[driver];
239
+ }
240
+ const Driver = this.configurations.drivers[driver];
241
+ if (!Driver) throw new CacheConfigurationError(`Cache driver ${driver} is not found, please declare it in the cache drivers in the configurations list.`);
242
+ const driverInstance = new Driver();
243
+ const configOptions = this.configurations.options[driver] || {};
244
+ driverInstance.setOptions({
245
+ ...configOptions,
246
+ ...runtimeOptions ?? {}
247
+ });
248
+ await driverInstance.connect();
249
+ this.attachGlobalListeners(driverInstance);
250
+ this.loadedDrivers[driver] = driverInstance;
251
+ return driverInstance;
252
+ }
253
+ /**
254
+ * Guard against silently dropping runtime options on a re-load. Once a
255
+ * driver has been instantiated, its options are frozen — calling `load` /
256
+ * `driver` / `use` again with a non-empty `runtimeOptions` would otherwise
257
+ * appear to work but actually use the original options. We throw instead
258
+ * so the misuse surfaces at the call site.
259
+ */
260
+ assertNoConflictingReload(driverName, runtimeOptions) {
261
+ if (runtimeOptions === void 0) return;
262
+ if (Object.keys(runtimeOptions).length === 0) return;
263
+ throw new CacheConfigurationError(`Cache driver '${driverName}' is already loaded; runtime options on subsequent calls are ignored — register a second driver name if you need a different configuration.`);
264
+ }
265
+ /**
266
+ * Register and bind a driver
267
+ */
268
+ registerDriver(driverName, driverClass) {
269
+ this.configurations.drivers[driverName] = driverClass;
270
+ }
271
+ /**
272
+ * Disconnect the cache manager
273
+ */
274
+ async disconnect() {
275
+ if (this.currentDriver) await this.currentDriver.disconnect();
276
+ }
277
+ /**
278
+ * {@inheritdoc}
279
+ */
280
+ async has(key) {
281
+ this.ensureDriverInitialized();
282
+ return this.currentDriver.has(key);
283
+ }
284
+ /**
285
+ * {@inheritdoc}
286
+ */
287
+ async remember(key, ttlOrOptions, callback) {
288
+ this.ensureDriverInitialized();
289
+ const driverOverride = ttlOrOptions && typeof ttlOrOptions === "object" && "driver" in ttlOrOptions ? ttlOrOptions.driver : void 0;
290
+ if (driverOverride) return (await this.load(driverOverride)).remember(key, ttlOrOptions, callback);
291
+ return this.currentDriver.remember(key, ttlOrOptions, callback);
292
+ }
293
+ /**
294
+ * Stale-while-revalidate. Returns cached when fresh, returns the stale
295
+ * value plus a background refresh when within `freshTtl..staleTtl`,
296
+ * blocks like a normal miss past `staleTtl`. Honors per-call `driver`
297
+ * override the same way `remember()` does.
298
+ *
299
+ * @example
300
+ * const product = await cache.swr(
301
+ * "product.42",
302
+ * { freshTtl: "1m", staleTtl: "1h" },
303
+ * () => db.products.find(42),
304
+ * );
305
+ */
306
+ async swr(key, options, callback) {
307
+ this.ensureDriverInitialized();
308
+ const driverOverride = options.driver;
309
+ if (driverOverride) return (await this.load(driverOverride)).swr(key, options, callback);
310
+ return this.currentDriver.swr(key, options, callback);
311
+ }
312
+ /**
313
+ * {@inheritdoc}
314
+ */
315
+ async pull(key) {
316
+ this.ensureDriverInitialized();
317
+ return this.currentDriver.pull(key);
318
+ }
319
+ /**
320
+ * {@inheritdoc}
321
+ */
322
+ async forever(key, value) {
323
+ this.ensureDriverInitialized();
324
+ return this.currentDriver.forever(key, value);
325
+ }
326
+ /**
327
+ * {@inheritdoc}
328
+ */
329
+ async increment(key, value) {
330
+ this.ensureDriverInitialized();
331
+ return this.currentDriver.increment(key, value);
332
+ }
333
+ /**
334
+ * {@inheritdoc}
335
+ */
336
+ async decrement(key, value) {
337
+ this.ensureDriverInitialized();
338
+ return this.currentDriver.decrement(key, value);
339
+ }
340
+ /**
341
+ * {@inheritdoc}
342
+ */
343
+ async many(keys) {
344
+ this.ensureDriverInitialized();
345
+ return this.currentDriver.many(keys);
346
+ }
347
+ /**
348
+ * {@inheritdoc}
349
+ */
350
+ async setMany(items, ttl) {
351
+ this.ensureDriverInitialized();
352
+ return this.currentDriver.setMany(items, ttl);
353
+ }
354
+ /**
355
+ * Register a global event listener (applies to all drivers)
356
+ */
357
+ on(event, handler) {
358
+ if (!this.globalEventListeners.has(event)) this.globalEventListeners.set(event, /* @__PURE__ */ new Set());
359
+ this.globalEventListeners.get(event).add(handler);
360
+ if (this.currentDriver) this.currentDriver.on(event, handler);
361
+ for (const driver of Object.values(this.loadedDrivers)) driver.on(event, handler);
362
+ return this;
363
+ }
364
+ /**
365
+ * Remove a global event listener
366
+ */
367
+ off(event, handler) {
368
+ const handlers = this.globalEventListeners.get(event);
369
+ if (handlers) handlers.delete(handler);
370
+ if (this.currentDriver) this.currentDriver.off(event, handler);
371
+ for (const driver of Object.values(this.loadedDrivers)) driver.off(event, handler);
372
+ return this;
373
+ }
374
+ /**
375
+ * Register a one-time global event listener
376
+ */
377
+ once(event, handler) {
378
+ const onceHandler = async (data) => {
379
+ await handler(data);
380
+ this.off(event, onceHandler);
381
+ };
382
+ return this.on(event, onceHandler);
383
+ }
384
+ /**
385
+ * Attach global listeners to a driver
386
+ */
387
+ attachGlobalListeners(driver) {
388
+ for (const [event, handlers] of this.globalEventListeners) for (const handler of handlers) driver.on(event, handler);
389
+ }
390
+ /**
391
+ * Set if not exists (atomic operation)
392
+ * Returns true if key was set, false if key already existed
393
+ * Note: Only supported by drivers that implement setNX (e.g., Redis)
394
+ */
395
+ async setNX(key, value, ttl) {
396
+ this.ensureDriverInitialized();
397
+ if (!this.currentDriver.setNX) throw new Error(`setNX is not supported by the current cache driver: ${this.currentDriver.name}`);
398
+ return this.currentDriver.setNX(key, value, ttl);
399
+ }
400
+ /**
401
+ * Create a tagged cache instance for the given tags
402
+ */
403
+ tags(tags) {
404
+ this.ensureDriverInitialized();
405
+ return this.currentDriver.tags(tags);
406
+ }
407
+ /**
408
+ * Atomically read, transform, and write a cached value. Delegates to the current driver.
409
+ */
410
+ async update(key, fn, options) {
411
+ this.ensureDriverInitialized();
412
+ return this.currentDriver.update(key, fn, options);
413
+ }
414
+ /**
415
+ * Shallow-merge a partial object into a cached value.
416
+ */
417
+ async merge(key, partial, options) {
418
+ this.ensureDriverInitialized();
419
+ return this.currentDriver.merge(key, partial, options);
420
+ }
421
+ /**
422
+ * Obtain a list accessor bound to the current driver.
423
+ */
424
+ list(key) {
425
+ this.ensureDriverInitialized();
426
+ return this.currentDriver.list(key);
427
+ }
428
+ /**
429
+ * Acquire a distributed lock, run `fn`, and auto-release. Returns a
430
+ * {@link LockOutcome} discriminated union so callers can distinguish
431
+ * "ran and got this value" from "skipped because someone else holds it".
432
+ *
433
+ * Honors the `driver` option for per-call driver override, same as `set`
434
+ * and `remember`.
435
+ *
436
+ * @example
437
+ * const outcome = await cache.lock("lock.import", "5m", async () => {
438
+ * await runImport();
439
+ * return "done";
440
+ * });
441
+ * if (!outcome.acquired) {
442
+ * console.log("another worker is already importing");
443
+ * }
444
+ */
445
+ async lock(key, ttlOrOptions, fn) {
446
+ this.ensureDriverInitialized();
447
+ const driverOverride = ttlOrOptions && typeof ttlOrOptions === "object" && "driver" in ttlOrOptions ? ttlOrOptions.driver : void 0;
448
+ return (driverOverride ? await this.load(driverOverride) : this.currentDriver).lock(key, ttlOrOptions, fn);
449
+ }
450
+ /**
451
+ * Similarity retrieval. Delegates to the current driver's `similar()` impl.
452
+ *
453
+ * Drivers that lack a similarity index throw {@link CacheUnsupportedError}.
454
+ *
455
+ * @example
456
+ * const hits = await cache.similar(await embed(query), { topK: 5, threshold: 0.7 });
457
+ */
458
+ /**
459
+ * Create a scoped view over the cache. Every key written through the
460
+ * returned scope is automatically prefixed with `prefix`; optional defaults
461
+ * (`ttl`, `tags`) flow through every write inside the scope.
462
+ *
463
+ * Per-call options always win over scope defaults. Scope tags merge
464
+ * additively with per-call tags. Nested scopes inherit from the parent.
465
+ *
466
+ * @example
467
+ * const chat = cache.namespace("chats.10", { ttl: "30d" });
468
+ * await chat.set("messages.1", msg); // → "chats.10.messages.1", 30d
469
+ * await chat.set("draft", d, { ttl: "1h" }); // per-call ttl wins
470
+ * await chat.namespace("typing", { ttl: "5s" }).set("user.42", true);
471
+ * await chat.clear(); // wipe the whole scope
472
+ */
473
+ namespace(prefix, options) {
474
+ this.ensureDriverInitialized();
475
+ return new ScopedCache(this, prefix, options);
476
+ }
477
+ async similar(vector, options) {
478
+ this.ensureDriverInitialized();
479
+ return this.currentDriver.similar(vector, options);
480
+ }
481
+ };
482
+ const cache = new CacheManager();
483
+
484
+ //#endregion
485
+ export { CacheManager, cache };
486
+ //# sourceMappingURL=cache-manager.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-manager.mjs","names":[],"sources":["../../../../../@warlock.js/cache/src/cache-manager.ts"],"sourcesContent":["import { CacheMetricsCollector } from \"./metrics\";\r\nimport { ScopedCache } from \"./scoped-cache\";\r\nimport type {\r\n CacheConfigurations,\r\n CacheDriver,\r\n CacheEventHandler,\r\n CacheEventType,\r\n CacheKey,\r\n CacheListAccessor,\r\n CacheMetricsSnapshot,\r\n CacheNamespaceOptions,\r\n CacheSetOptions,\r\n CacheSimilarHit,\r\n CacheSimilarOptions,\r\n CacheSwrOptions,\r\n CacheTtl,\r\n DriverClass,\r\n LockOptions,\r\n LockOutcome,\r\n RememberOptions,\r\n ScopedCacheContract,\r\n TaggedCacheDriver,\r\n} from \"./types\";\r\nimport { CacheConfigurationError, CacheDriverNotInitializedError } from \"./types\";\r\n\r\nexport class CacheManager implements CacheDriver<any, any> {\r\n /**\r\n * Cache Driver\r\n */\r\n public currentDriver?: CacheDriver<any, any>;\r\n\r\n /**\r\n * Loaded drivers\r\n */\r\n public loadedDrivers: Record<string, CacheDriver<any, any>> = {};\r\n\r\n /**\r\n * Configurations list\r\n */\r\n protected configurations: CacheConfigurations = {\r\n drivers: {},\r\n options: {},\r\n };\r\n\r\n /**\r\n * Global event listeners\r\n */\r\n protected globalEventListeners: Map<CacheEventType, Set<CacheEventHandler>> = new Map();\r\n\r\n /**\r\n * Metrics collector — lazy on first {@link metrics} call so apps that\r\n * never read metrics pay zero cost. Once instantiated, it stays\r\n * subscribed to events for the manager's lifetime.\r\n */\r\n protected metricsCollector?: CacheMetricsCollector;\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"cacheManager\";\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public get client() {\r\n return this.currentDriver?.client;\r\n }\r\n\r\n /**\r\n * Set the cache configurations\r\n */\r\n public setCacheConfigurations(configurations: CacheConfigurations) {\r\n this.configurations.default = configurations.default;\r\n this.configurations.drivers = configurations.drivers;\r\n this.configurations.options = configurations.options;\r\n this.configurations.logging = configurations.logging;\r\n }\r\n\r\n /**\r\n * Set logging state\r\n */\r\n public setLoggingState(loggingState: boolean) {\r\n this.ensureDriverInitialized();\r\n\r\n this.currentDriver!.setLoggingState(loggingState);\r\n }\r\n\r\n /**\r\n * Switch the manager to a registered driver, optionally injecting runtime\r\n * options that merge over the static config.\r\n *\r\n * The string form looks the driver up in `setCacheConfigurations({ drivers })`,\r\n * loads it (or returns the cached instance), and sets it as `currentDriver`.\r\n * The instance form takes a pre-built driver and bypasses the registry; the\r\n * `runtimeOptions` argument is silently ignored in that case because the\r\n * instance was constructed externally.\r\n *\r\n * Runtime options merge over `config.options[name]` per-key — runtime wins.\r\n * Use this for constructor-only knobs that can't live in static config\r\n * (e.g. `pg`'s `client: pg.Pool`).\r\n *\r\n * @example\r\n * const pool = new Pool({ connectionString });\r\n * await cache.use(\"pg\", { client: pool });\r\n */\r\n public async use(\r\n driver: string | CacheDriver<any, any>,\r\n runtimeOptions?: Record<string, any>,\r\n ) {\r\n if (typeof driver === \"string\") {\r\n const driverInstance = await this.load(driver, runtimeOptions);\r\n\r\n if (!driverInstance) {\r\n throw new CacheConfigurationError(\r\n `Cache driver ${driver} is not found, please declare it in the cache drivers in the configurations list.`,\r\n );\r\n }\r\n\r\n driver = driverInstance;\r\n }\r\n\r\n this.attachGlobalListeners(driver);\r\n\r\n if (this.configurations.logging !== undefined) {\r\n driver.setLoggingState(this.configurations.logging);\r\n }\r\n\r\n this.currentDriver = driver;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Ensure driver is initialized before operations\r\n */\r\n protected ensureDriverInitialized(): void {\r\n if (!this.currentDriver) {\r\n throw new CacheDriverNotInitializedError();\r\n }\r\n }\r\n\r\n /**\r\n * Return the running metrics snapshot — counters, hit-rate, latency\r\n * percentiles, per-driver breakdowns. Lazy-attaches the collector on\r\n * first call so apps that never read metrics pay zero cost.\r\n *\r\n * @example\r\n * const m = cache.metrics();\r\n * console.log(`hit rate: ${(m.hitRate * 100).toFixed(1)}%`);\r\n * console.log(`p95: ${m.latencyMs.p95.toFixed(2)}ms`);\r\n */\r\n public metrics(): CacheMetricsSnapshot {\r\n return this.ensureMetricsCollector().snapshot();\r\n }\r\n\r\n /**\r\n * Wipe every counter + latency sample and reset `startedAt` to now.\r\n * The collector itself stays subscribed to events.\r\n */\r\n public resetMetrics(): void {\r\n this.ensureMetricsCollector().reset();\r\n }\r\n\r\n /**\r\n * Lazy-construct the metrics collector and wire it to the global event\r\n * bus. Subsequent calls return the same instance — survives `cache.use()`\r\n * driver switches because handlers attach via `on()` and re-bind to every\r\n * loaded driver.\r\n */\r\n protected ensureMetricsCollector(): CacheMetricsCollector {\r\n if (this.metricsCollector) {\r\n return this.metricsCollector;\r\n }\r\n\r\n const collector = new CacheMetricsCollector();\r\n\r\n this.on(\"hit\", (data) => collector.recordEvent(\"hit\", data));\r\n this.on(\"miss\", (data) => collector.recordEvent(\"miss\", data));\r\n this.on(\"set\", (data) => collector.recordEvent(\"set\", data));\r\n this.on(\"removed\", (data) => collector.recordEvent(\"removed\", data));\r\n this.on(\"error\", (data) => collector.recordEvent(\"error\", data));\r\n\r\n this.metricsCollector = collector;\r\n\r\n return collector;\r\n }\r\n\r\n /**\r\n * Time the body, record the elapsed milliseconds against the metrics\r\n * collector for the given driver (defaults to the current driver's name).\r\n * Pass-through if the collector hasn't been instantiated yet — apps that\r\n * don't read metrics never pay for sample collection.\r\n */\r\n protected async timed<T>(\r\n body: () => Promise<T>,\r\n driverName?: string,\r\n ): Promise<T> {\r\n if (!this.metricsCollector) {\r\n return body();\r\n }\r\n\r\n const start = performance.now();\r\n\r\n try {\r\n return await body();\r\n } finally {\r\n const elapsed = performance.now() - start;\r\n const name = driverName ?? this.currentDriver?.name ?? \"unknown\";\r\n this.metricsCollector.recordLatency(name, elapsed);\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async get<T = any>(key: CacheKey): Promise<T | null> {\r\n this.ensureDriverInitialized();\r\n return this.timed(() => this.currentDriver!.get<T>(key));\r\n }\r\n\r\n /**\r\n * Set a value in the cache.\r\n *\r\n * Accepts a positional TTL (number of seconds or duration string like `\"1h\"`)\r\n * or a rich {@link CacheSetOptions} object supporting `ttl`, `expiresAt`,\r\n * `tags`, `onConflict`, `namespace`, and per-call `driver` overrides.\r\n */\r\n public async set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions) {\r\n this.ensureDriverInitialized();\r\n\r\n const driverOverride =\r\n ttlOrOptions && typeof ttlOrOptions === \"object\" && \"driver\" in ttlOrOptions\r\n ? ttlOrOptions.driver\r\n : undefined;\r\n\r\n if (driverOverride) {\r\n const driver = await this.load(driverOverride);\r\n return this.timed(() => driver.set(key, value, ttlOrOptions), driver.name);\r\n }\r\n\r\n return this.timed(() => this.currentDriver!.set(key, value, ttlOrOptions));\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remove(key: CacheKey) {\r\n this.ensureDriverInitialized();\r\n return this.timed(() => this.currentDriver!.remove(key));\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async removeNamespace(namespace: string) {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.removeNamespace(namespace);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async flush() {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.flush();\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async connect() {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.connect();\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public parseKey(key: CacheKey) {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.parseKey(key);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public get options() {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.options;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public setOptions(options: Record<string, any>) {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.setOptions(options || {});\r\n }\r\n\r\n /**\r\n * Return the loaded driver instance for `driverName`, loading it on first\r\n * call. Optional `runtimeOptions` follow the same merge-over-config rules\r\n * as {@link load}; passing options after the driver has already been\r\n * loaded throws to avoid silent swallowing.\r\n */\r\n public async driver(driverName: string, runtimeOptions?: Record<string, any>) {\r\n if (this.loadedDrivers[driverName]) {\r\n this.assertNoConflictingReload(driverName, runtimeOptions);\r\n\r\n return this.loadedDrivers[driverName];\r\n }\r\n\r\n return this.load(driverName, runtimeOptions);\r\n }\r\n\r\n /**\r\n * Initialize the cache manager and pick the default driver\r\n */\r\n public async init() {\r\n const defaultCacheDriverName = this.configurations.default;\r\n\r\n if (!defaultCacheDriverName) {\r\n return;\r\n }\r\n\r\n const driver = await this.driver(defaultCacheDriverName);\r\n\r\n await this.use(driver);\r\n }\r\n\r\n /**\r\n * Load and connect the registered driver named `driver`. First-call wins —\r\n * subsequent calls without `runtimeOptions` return the cached instance, and\r\n * subsequent calls *with* `runtimeOptions` throw {@link CacheConfigurationError}\r\n * to avoid silently dropping the new options.\r\n *\r\n * `runtimeOptions` merge over `config.options[driver]` per-key (runtime wins),\r\n * letting consumers split static knobs (table, ttl, globalPrefix) from\r\n * constructor-only ones (pg's `client`, custom adapters, etc.).\r\n *\r\n * @example\r\n * const pool = new Pool({ connectionString });\r\n * const pg = await cache.load(\"pg\", { client: pool });\r\n */\r\n public async load(driver: string, runtimeOptions?: Record<string, any>) {\r\n if (this.loadedDrivers[driver]) {\r\n this.assertNoConflictingReload(driver, runtimeOptions);\r\n\r\n return this.loadedDrivers[driver];\r\n }\r\n\r\n const Driver = this.configurations.drivers[\r\n driver as keyof typeof this.configurations.drivers\r\n ] as DriverClass | undefined;\r\n\r\n if (!Driver) {\r\n throw new CacheConfigurationError(\r\n `Cache driver ${driver} is not found, please declare it in the cache drivers in the configurations list.`,\r\n );\r\n }\r\n\r\n const driverInstance = new Driver();\r\n const configOptions =\r\n this.configurations.options[driver as keyof typeof this.configurations.options] || {};\r\n\r\n driverInstance.setOptions({ ...configOptions, ...(runtimeOptions ?? {}) });\r\n\r\n await driverInstance.connect();\r\n\r\n this.attachGlobalListeners(driverInstance);\r\n\r\n this.loadedDrivers[driver] = driverInstance;\r\n\r\n return driverInstance as CacheDriver<any, any>;\r\n }\r\n\r\n /**\r\n * Guard against silently dropping runtime options on a re-load. Once a\r\n * driver has been instantiated, its options are frozen — calling `load` /\r\n * `driver` / `use` again with a non-empty `runtimeOptions` would otherwise\r\n * appear to work but actually use the original options. We throw instead\r\n * so the misuse surfaces at the call site.\r\n */\r\n protected assertNoConflictingReload(\r\n driverName: string,\r\n runtimeOptions: Record<string, any> | undefined,\r\n ): void {\r\n if (runtimeOptions === undefined) {\r\n return;\r\n }\r\n\r\n if (Object.keys(runtimeOptions).length === 0) {\r\n return;\r\n }\r\n\r\n throw new CacheConfigurationError(\r\n `Cache driver '${driverName}' is already loaded; runtime options on subsequent calls are ignored — register a second driver name if you need a different configuration.`,\r\n );\r\n }\r\n\r\n /**\r\n * Register and bind a driver\r\n */\r\n public registerDriver(driverName: string, driverClass: DriverClass) {\r\n (this.configurations.drivers as Record<string, DriverClass>)[driverName] = driverClass;\r\n }\r\n\r\n /**\r\n * Disconnect the cache manager\r\n */\r\n public async disconnect() {\r\n if (this.currentDriver) {\r\n await this.currentDriver.disconnect();\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async has(key: CacheKey): Promise<boolean> {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.has(key);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remember<T = any>(\r\n key: CacheKey,\r\n ttlOrOptions: CacheTtl | RememberOptions,\r\n callback: () => Promise<T>,\r\n ): Promise<T> {\r\n this.ensureDriverInitialized();\r\n\r\n const driverOverride =\r\n ttlOrOptions && typeof ttlOrOptions === \"object\" && \"driver\" in ttlOrOptions\r\n ? ttlOrOptions.driver\r\n : undefined;\r\n\r\n if (driverOverride) {\r\n const driver = await this.load(driverOverride);\r\n return driver.remember(key, ttlOrOptions, callback);\r\n }\r\n\r\n return this.currentDriver!.remember(key, ttlOrOptions, callback);\r\n }\r\n\r\n /**\r\n * Stale-while-revalidate. Returns cached when fresh, returns the stale\r\n * value plus a background refresh when within `freshTtl..staleTtl`,\r\n * blocks like a normal miss past `staleTtl`. Honors per-call `driver`\r\n * override the same way `remember()` does.\r\n *\r\n * @example\r\n * const product = await cache.swr(\r\n * \"product.42\",\r\n * { freshTtl: \"1m\", staleTtl: \"1h\" },\r\n * () => db.products.find(42),\r\n * );\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 this.ensureDriverInitialized();\r\n\r\n const driverOverride = options.driver;\r\n\r\n if (driverOverride) {\r\n const driver = await this.load(driverOverride);\r\n\r\n return driver.swr<T>(key, options, callback);\r\n }\r\n\r\n return this.currentDriver!.swr<T>(key, options, callback);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async pull(key: CacheKey): Promise<any | null> {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.pull(key);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async forever(key: CacheKey, value: any): Promise<any> {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.forever(key, value);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async increment(key: CacheKey, value?: number): Promise<number> {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.increment(key, value);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async decrement(key: CacheKey, value?: number): Promise<number> {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.decrement(key, value);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async many(keys: CacheKey[]): Promise<any[]> {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.many(keys);\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 this.ensureDriverInitialized();\r\n return this.currentDriver!.setMany(items, ttl);\r\n }\r\n\r\n /**\r\n * Register a global event listener (applies to all drivers)\r\n */\r\n public on(event: CacheEventType, handler: CacheEventHandler): this {\r\n if (!this.globalEventListeners.has(event)) {\r\n this.globalEventListeners.set(event, new Set());\r\n }\r\n this.globalEventListeners.get(event)!.add(handler);\r\n\r\n // Also attach to current driver if exists\r\n if (this.currentDriver) {\r\n this.currentDriver.on(event, handler);\r\n }\r\n\r\n // Attach to all loaded drivers\r\n for (const driver of Object.values(this.loadedDrivers)) {\r\n driver.on(event, handler);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Remove a global event listener\r\n */\r\n public off(event: CacheEventType, handler: CacheEventHandler): this {\r\n const handlers = this.globalEventListeners.get(event);\r\n if (handlers) {\r\n handlers.delete(handler);\r\n }\r\n\r\n // Also remove from current driver\r\n if (this.currentDriver) {\r\n this.currentDriver.off(event, handler);\r\n }\r\n\r\n // Remove from all loaded drivers\r\n for (const driver of Object.values(this.loadedDrivers)) {\r\n driver.off(event, handler);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Register a one-time global 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 * Attach global listeners to a driver\r\n */\r\n protected attachGlobalListeners(driver: CacheDriver<any, any>) {\r\n for (const [event, handlers] of this.globalEventListeners) {\r\n for (const handler of handlers) {\r\n driver.on(event, handler);\r\n }\r\n }\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 * Note: Only supported by drivers that implement setNX (e.g., Redis)\r\n */\r\n public async setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\r\n this.ensureDriverInitialized();\r\n\r\n if (!this.currentDriver!.setNX) {\r\n throw new Error(\r\n `setNX is not supported by the current cache driver: ${this.currentDriver!.name}`,\r\n );\r\n }\r\n\r\n return this.currentDriver!.setNX(key, value, ttl);\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[]): TaggedCacheDriver {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.tags(tags);\r\n }\r\n\r\n /**\r\n * Atomically read, transform, and write a cached value. Delegates to the current driver.\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 this.ensureDriverInitialized();\r\n return this.currentDriver!.update<T>(key, fn, options);\r\n }\r\n\r\n /**\r\n * Shallow-merge a partial object into a cached value.\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 this.ensureDriverInitialized();\r\n return this.currentDriver!.merge<T>(key, partial, options);\r\n }\r\n\r\n /**\r\n * Obtain a list accessor bound to the current driver.\r\n */\r\n public list<T = any>(key: CacheKey): CacheListAccessor<T> {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.list<T>(key);\r\n }\r\n\r\n /**\r\n * Acquire a distributed lock, run `fn`, and auto-release. Returns a\r\n * {@link LockOutcome} discriminated union so callers can distinguish\r\n * \"ran and got this value\" from \"skipped because someone else holds it\".\r\n *\r\n * Honors the `driver` option for per-call driver override, same as `set`\r\n * and `remember`.\r\n *\r\n * @example\r\n * const outcome = await cache.lock(\"lock.import\", \"5m\", async () => {\r\n * await runImport();\r\n * return \"done\";\r\n * });\r\n * if (!outcome.acquired) {\r\n * console.log(\"another worker is already importing\");\r\n * }\r\n */\r\n public async lock<T>(\r\n key: CacheKey,\r\n ttlOrOptions: CacheTtl | LockOptions,\r\n fn: () => Promise<T>,\r\n ): Promise<LockOutcome<T>> {\r\n this.ensureDriverInitialized();\r\n\r\n const driverOverride =\r\n ttlOrOptions && typeof ttlOrOptions === \"object\" && \"driver\" in ttlOrOptions\r\n ? ttlOrOptions.driver\r\n : undefined;\r\n\r\n const driver = driverOverride\r\n ? await this.load(driverOverride)\r\n : this.currentDriver!;\r\n\r\n return driver.lock<T>(key, ttlOrOptions as CacheTtl | Omit<LockOptions, \"driver\">, fn);\r\n }\r\n\r\n /**\r\n * Similarity retrieval. Delegates to the current driver's `similar()` impl.\r\n *\r\n * Drivers that lack a similarity index throw {@link CacheUnsupportedError}.\r\n *\r\n * @example\r\n * const hits = await cache.similar(await embed(query), { topK: 5, threshold: 0.7 });\r\n */\r\n /**\r\n * Create a scoped view over the cache. Every key written through the\r\n * returned scope is automatically prefixed with `prefix`; optional defaults\r\n * (`ttl`, `tags`) flow through every write inside the scope.\r\n *\r\n * Per-call options always win over scope defaults. Scope tags merge\r\n * additively with per-call tags. Nested scopes inherit from the parent.\r\n *\r\n * @example\r\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\r\n * await chat.set(\"messages.1\", msg); // → \"chats.10.messages.1\", 30d\r\n * await chat.set(\"draft\", d, { ttl: \"1h\" }); // per-call ttl wins\r\n * await chat.namespace(\"typing\", { ttl: \"5s\" }).set(\"user.42\", true);\r\n * await chat.clear(); // wipe the whole scope\r\n */\r\n public namespace(prefix: string, options?: CacheNamespaceOptions): ScopedCacheContract {\r\n this.ensureDriverInitialized();\r\n return new ScopedCache(this, prefix, options);\r\n }\r\n\r\n public async similar<T = any>(\r\n vector: number[],\r\n options: CacheSimilarOptions,\r\n ): Promise<CacheSimilarHit<T>[]> {\r\n this.ensureDriverInitialized();\r\n return this.currentDriver!.similar<T>(vector, options);\r\n }\r\n}\r\n\r\nexport const cache = new CacheManager();\r\n"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ //#region ../../@warlock.js/cache/src/cached/auto-key.d.ts
2
+ /**
3
+ * Derive a cache key from a prefix and a set of function arguments.
4
+ *
5
+ * Rules (in order of precedence):
6
+ * 1. No args → prefix alone.
7
+ * 2. All primitives (`string`, `number`, `boolean`) or `null` / `undefined` /
8
+ * `bigint` → joined onto the prefix with dots.
9
+ * 3. Any non-primitive arg present → the full args array is `JSON.stringify`-ed
10
+ * and appended to the prefix.
11
+ * 4. Serialization throws (circular refs, `BigInt` nested in an object) → we
12
+ * re-throw as `CacheConfigurationError` so the caller sees a cache-scoped
13
+ * error rather than a cryptic `TypeError`.
14
+ *
15
+ * @example
16
+ * deriveAutoKey("user", [42]); // "user.42"
17
+ * deriveAutoKey("orders", [42, "abc"]); // "orders.42.abc"
18
+ * deriveAutoKey("featured", []); // "featured"
19
+ * deriveAutoKey("search", [{ q: "hello" }]); // "search.[{\"q\":\"hello\"}]"
20
+ * deriveAutoKey("user", [null, undefined]); // "user.null.undefined"
21
+ */
22
+ declare function deriveAutoKey(prefix: string, args: readonly unknown[]): string;
23
+ //#endregion
24
+ export { deriveAutoKey };
25
+ //# sourceMappingURL=auto-key.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-key.d.mts","names":[],"sources":["../../../../../../@warlock.js/cache/src/cached/auto-key.ts"],"mappings":";;AAsBA;;;;AAAsE;;;;;;;;;;;;;;;iBAAtD,aAAA,CAAc,MAAA,UAAgB,IAAwB"}
@@ -0,0 +1,55 @@
1
+ import { CacheConfigurationError } from "../types.mjs";
2
+
3
+ //#region ../../@warlock.js/cache/src/cached/auto-key.ts
4
+ /**
5
+ * Derive a cache key from a prefix and a set of function arguments.
6
+ *
7
+ * Rules (in order of precedence):
8
+ * 1. No args → prefix alone.
9
+ * 2. All primitives (`string`, `number`, `boolean`) or `null` / `undefined` /
10
+ * `bigint` → joined onto the prefix with dots.
11
+ * 3. Any non-primitive arg present → the full args array is `JSON.stringify`-ed
12
+ * and appended to the prefix.
13
+ * 4. Serialization throws (circular refs, `BigInt` nested in an object) → we
14
+ * re-throw as `CacheConfigurationError` so the caller sees a cache-scoped
15
+ * error rather than a cryptic `TypeError`.
16
+ *
17
+ * @example
18
+ * deriveAutoKey("user", [42]); // "user.42"
19
+ * deriveAutoKey("orders", [42, "abc"]); // "orders.42.abc"
20
+ * deriveAutoKey("featured", []); // "featured"
21
+ * deriveAutoKey("search", [{ q: "hello" }]); // "search.[{\"q\":\"hello\"}]"
22
+ * deriveAutoKey("user", [null, undefined]); // "user.null.undefined"
23
+ */
24
+ function deriveAutoKey(prefix, args) {
25
+ if (args.length === 0) return prefix;
26
+ if (args.every(isPrimitiveOrNullish)) return prefix + "." + args.map(serializePrimitive).join(".");
27
+ try {
28
+ return prefix + "." + JSON.stringify(args);
29
+ } catch (error) {
30
+ throw new CacheConfigurationError(`cached(): could not derive an auto-key from args for prefix "${prefix}". The args include a value that is not JSON-serializable (circular reference, BigInt nested inside an object, or similar). Use the options form with a custom key function. Original error: ${error.message}`);
31
+ }
32
+ }
33
+ /**
34
+ * Primitives and nullish values can be concatenated directly onto a key without
35
+ * JSON serialization. Adding `bigint` here avoids the `JSON.stringify` throw on
36
+ * top-level bigint args.
37
+ */
38
+ function isPrimitiveOrNullish(value) {
39
+ if (value === null || value === void 0) return true;
40
+ const type = typeof value;
41
+ return type === "string" || type === "number" || type === "boolean" || type === "bigint";
42
+ }
43
+ /**
44
+ * Serialize a single primitive or nullish value to its string key-segment form.
45
+ */
46
+ function serializePrimitive(value) {
47
+ if (value === null) return "null";
48
+ if (value === void 0) return "undefined";
49
+ if (typeof value === "bigint") return value.toString();
50
+ return String(value);
51
+ }
52
+
53
+ //#endregion
54
+ export { deriveAutoKey };
55
+ //# sourceMappingURL=auto-key.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-key.mjs","names":[],"sources":["../../../../../../@warlock.js/cache/src/cached/auto-key.ts"],"sourcesContent":["import { CacheConfigurationError } from \"../types\";\n\n/**\n * Derive a cache key from a prefix and a set of function arguments.\n *\n * Rules (in order of precedence):\n * 1. No args → prefix alone.\n * 2. All primitives (`string`, `number`, `boolean`) or `null` / `undefined` /\n * `bigint` → joined onto the prefix with dots.\n * 3. Any non-primitive arg present → the full args array is `JSON.stringify`-ed\n * and appended to the prefix.\n * 4. Serialization throws (circular refs, `BigInt` nested in an object) → we\n * re-throw as `CacheConfigurationError` so the caller sees a cache-scoped\n * error rather than a cryptic `TypeError`.\n *\n * @example\n * deriveAutoKey(\"user\", [42]); // \"user.42\"\n * deriveAutoKey(\"orders\", [42, \"abc\"]); // \"orders.42.abc\"\n * deriveAutoKey(\"featured\", []); // \"featured\"\n * deriveAutoKey(\"search\", [{ q: \"hello\" }]); // \"search.[{\\\"q\\\":\\\"hello\\\"}]\"\n * deriveAutoKey(\"user\", [null, undefined]); // \"user.null.undefined\"\n */\nexport function deriveAutoKey(prefix: string, args: readonly unknown[]): string {\n if (args.length === 0) {\n return prefix;\n }\n\n if (args.every(isPrimitiveOrNullish)) {\n return prefix + \".\" + args.map(serializePrimitive).join(\".\");\n }\n\n try {\n return prefix + \".\" + JSON.stringify(args);\n } catch (error) {\n throw new CacheConfigurationError(\n `cached(): could not derive an auto-key from args for prefix \"${prefix}\". ` +\n `The args include a value that is not JSON-serializable (circular reference, ` +\n `BigInt nested inside an object, or similar). Use the options form with a custom ` +\n `key function. Original error: ${(error as Error).message}`,\n );\n }\n}\n\n/**\n * Primitives and nullish values can be concatenated directly onto a key without\n * JSON serialization. Adding `bigint` here avoids the `JSON.stringify` throw on\n * top-level bigint args.\n */\nfunction isPrimitiveOrNullish(value: unknown): boolean {\n if (value === null || value === undefined) {\n return true;\n }\n\n const type = typeof value;\n return type === \"string\" || type === \"number\" || type === \"boolean\" || type === \"bigint\";\n}\n\n/**\n * Serialize a single primitive or nullish value to its string key-segment form.\n */\nfunction serializePrimitive(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n if (typeof value === \"bigint\") return value.toString();\n return String(value);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,cAAc,QAAgB,MAAkC;CAC9E,IAAI,KAAK,WAAW,GAClB,OAAO;CAGT,IAAI,KAAK,MAAM,oBAAoB,GACjC,OAAO,SAAS,MAAM,KAAK,IAAI,kBAAkB,EAAE,KAAK,GAAG;CAG7D,IAAI;EACF,OAAO,SAAS,MAAM,KAAK,UAAU,IAAI;CAC3C,SAAS,OAAO;EACd,MAAM,IAAI,wBACR,gEAAgE,OAAO,+LAGnC,MAAgB,SACtD;CACF;AACF;;;;;;AAOA,SAAS,qBAAqB,OAAyB;CACrD,IAAI,UAAU,QAAQ,UAAU,QAC9B,OAAO;CAGT,MAAM,OAAO,OAAO;CACpB,OAAO,SAAS,YAAY,SAAS,YAAY,SAAS,aAAa,SAAS;AAClF;;;;AAKA,SAAS,mBAAmB,OAAwB;CAClD,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI,UAAU,QAAW,OAAO;CAChC,IAAI,OAAO,UAAU,UAAU,OAAO,MAAM,SAAS;CACrD,OAAO,OAAO,KAAK;AACrB"}