@warlock.js/cache 5.30.0 → 5.31.0

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 (77) hide show
  1. package/CHANGELOG.md +20 -8
  2. package/README.md +12 -21
  3. package/cjs/index.cjs +46 -27
  4. package/cjs/index.cjs.map +1 -1
  5. package/esm/cache-manager.d.mts +3 -1
  6. package/esm/cache-manager.d.mts.map +1 -1
  7. package/esm/cache-manager.mjs +2 -2
  8. package/esm/cache-manager.mjs.map +1 -1
  9. package/esm/drivers/base-cache-driver.d.mts +3 -1
  10. package/esm/drivers/base-cache-driver.d.mts.map +1 -1
  11. package/esm/drivers/base-cache-driver.mjs +4 -3
  12. package/esm/drivers/base-cache-driver.mjs.map +1 -1
  13. package/esm/drivers/file-cache-driver.d.mts.map +1 -1
  14. package/esm/drivers/file-cache-driver.mjs.map +1 -1
  15. package/esm/drivers/lru-memory-cache-driver.d.mts +3 -1
  16. package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -1
  17. package/esm/drivers/lru-memory-cache-driver.mjs +6 -4
  18. package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -1
  19. package/esm/drivers/memory-cache-driver.d.mts +3 -1
  20. package/esm/drivers/memory-cache-driver.d.mts.map +1 -1
  21. package/esm/drivers/memory-cache-driver.mjs +9 -3
  22. package/esm/drivers/memory-cache-driver.mjs.map +1 -1
  23. package/esm/drivers/mock-cache-driver.d.mts +3 -1
  24. package/esm/drivers/mock-cache-driver.d.mts.map +1 -1
  25. package/esm/drivers/mock-cache-driver.mjs +11 -4
  26. package/esm/drivers/mock-cache-driver.mjs.map +1 -1
  27. package/esm/drivers/pg-cache-driver.d.mts +3 -1
  28. package/esm/drivers/pg-cache-driver.d.mts.map +1 -1
  29. package/esm/drivers/pg-cache-driver.mjs +6 -3
  30. package/esm/drivers/pg-cache-driver.mjs.map +1 -1
  31. package/esm/drivers/redis-cache-driver.d.mts +4 -2
  32. package/esm/drivers/redis-cache-driver.d.mts.map +1 -1
  33. package/esm/drivers/redis-cache-driver.mjs +4 -3
  34. package/esm/drivers/redis-cache-driver.mjs.map +1 -1
  35. package/esm/list/memory-cache-list.mjs.map +1 -1
  36. package/esm/metrics.d.mts.map +1 -1
  37. package/esm/metrics.mjs.map +1 -1
  38. package/esm/scoped-cache.d.mts +3 -1
  39. package/esm/scoped-cache.d.mts.map +1 -1
  40. package/esm/scoped-cache.mjs +2 -2
  41. package/esm/scoped-cache.mjs.map +1 -1
  42. package/esm/tagged-cache.d.mts +3 -1
  43. package/esm/tagged-cache.d.mts.map +1 -1
  44. package/esm/tagged-cache.mjs +2 -2
  45. package/esm/tagged-cache.mjs.map +1 -1
  46. package/esm/tagged-scoped-cache.d.mts +3 -1
  47. package/esm/tagged-scoped-cache.d.mts.map +1 -1
  48. package/esm/tagged-scoped-cache.mjs +2 -2
  49. package/esm/tagged-scoped-cache.mjs.map +1 -1
  50. package/esm/types.d.mts +16 -5
  51. package/esm/types.d.mts.map +1 -1
  52. package/esm/types.mjs.map +1 -1
  53. package/esm/utils.d.mts.map +1 -1
  54. package/esm/utils.mjs.map +1 -1
  55. package/llms-full.txt +229 -224
  56. package/llms.txt +1 -1
  57. package/package.json +3 -3
  58. package/skills/apply-cache-patterns/SKILL.md +3 -2
  59. package/skills/cache-basics/SKILL.md +24 -24
  60. package/skills/configure-pg-cache/SKILL.md +2 -2
  61. package/skills/configure-set-options/SKILL.md +14 -14
  62. package/skills/handle-cache-errors/SKILL.md +8 -8
  63. package/skills/index.md +4 -1
  64. package/skills/observe-cache/SKILL.md +8 -8
  65. package/skills/pick-cache-driver/SKILL.md +24 -23
  66. package/skills/test-cache-code/SKILL.md +15 -10
  67. package/skills/use-cache-atomic/SKILL.md +17 -11
  68. package/skills/use-cache-bulk/SKILL.md +8 -5
  69. package/skills/use-cache-list/SKILL.md +13 -13
  70. package/skills/use-cache-lock/SKILL.md +11 -17
  71. package/skills/use-cache-namespace/SKILL.md +11 -13
  72. package/skills/use-cache-similarity/SKILL.md +9 -9
  73. package/skills/use-cache-tags/SKILL.md +13 -13
  74. package/skills/use-cache-update-merge/SKILL.md +10 -10
  75. package/skills/use-cache-utils/SKILL.md +17 -17
  76. package/skills/use-cached-hof/SKILL.md +8 -7
  77. package/skills/use-swr/SKILL.md +14 -18
@@ -213,7 +213,9 @@ declare class CacheManager implements CacheDriver<any, any> {
213
213
  /**
214
214
  * {@inheritdoc}
215
215
  */
216
- increment(key: CacheKey, value?: number): Promise<number>;
216
+ increment(key: CacheKey, value?: number, options?: {
217
+ ttl?: CacheTtl;
218
+ }): Promise<number>;
217
219
  /**
218
220
  * {@inheritdoc}
219
221
  */
@@ -1 +1 @@
1
- {"version":3,"file":"cache-manager.d.mts","names":[],"sources":["../../../../../../../packages/cache/src/cache-manager.ts"],"mappings":"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"}
1
+ {"version":3,"file":"cache-manager.d.mts","names":[],"sources":["../../../../../../../packages/cache/src/cache-manager.ts"],"mappings":"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"}
@@ -344,9 +344,9 @@ var CacheManager = class {
344
344
  /**
345
345
  * {@inheritdoc}
346
346
  */
347
- async increment(key, value) {
347
+ async increment(key, value, options) {
348
348
  this.ensureDriverInitialized();
349
- return this.currentDriver.increment(key, value);
349
+ return this.currentDriver.increment(key, value, options);
350
350
  }
351
351
  /**
352
352
  * {@inheritdoc}
@@ -1 +1 @@
1
- {"version":3,"file":"cache-manager.mjs","names":[],"sources":["../../../../../../../packages/cache/src/cache-manager.ts"],"sourcesContent":["import { CacheMetricsCollector } from \"./metrics\";\nimport { ScopedCache } from \"./scoped-cache\";\nimport type {\n CacheConfigurations,\n CacheDriver,\n CacheEventHandler,\n CacheEventType,\n CacheKey,\n CacheListAccessor,\n CacheMetricsSnapshot,\n CacheNamespaceOptions,\n CacheSetOptions,\n CacheSimilarHit,\n CacheSimilarOptions,\n CacheSwrOptions,\n CacheTtl,\n DriverClass,\n LockOptions,\n LockOutcome,\n RememberOptions,\n ScopedCacheContract,\n TaggedCacheDriver,\n} from \"./types\";\nimport { CacheConfigurationError, CacheDriverNotInitializedError } from \"./types\";\n\nexport class CacheManager implements CacheDriver<any, any> {\n /**\n * Cache Driver\n */\n public currentDriver?: CacheDriver<any, any>;\n\n /**\n * Loaded drivers\n */\n public loadedDrivers: Record<string, CacheDriver<any, any>> = {};\n\n /**\n * In-flight `load()` promises, so concurrent callers share one `connect()`.\n */\n protected loadingDrivers = new Map<string, Promise<CacheDriver<any, any>>>();\n\n /**\n * Configurations list\n */\n protected configurations: CacheConfigurations = {\n drivers: {},\n options: {},\n };\n\n /**\n * Global event listeners\n */\n protected globalEventListeners: Map<CacheEventType, Set<CacheEventHandler>> = new Map();\n\n /**\n * Metrics collector — lazy on first {@link metrics} call so apps that\n * never read metrics pay zero cost. Once instantiated, it stays\n * subscribed to events for the manager's lifetime.\n */\n protected metricsCollector?: CacheMetricsCollector;\n\n /**\n * {@inheritdoc}\n */\n public name = \"cacheManager\";\n\n /**\n * {@inheritdoc}\n */\n public get client() {\n return this.currentDriver?.client;\n }\n\n /**\n * Set the cache configurations\n */\n public setCacheConfigurations(configurations: CacheConfigurations) {\n this.configurations.default = configurations.default;\n this.configurations.drivers = configurations.drivers;\n this.configurations.options = configurations.options;\n this.configurations.logging = configurations.logging;\n }\n\n /**\n * Set logging state\n */\n public setLoggingState(loggingState: boolean) {\n this.ensureDriverInitialized();\n\n this.currentDriver!.setLoggingState(loggingState);\n }\n\n /**\n * Switch the manager to a registered driver, optionally injecting runtime\n * options that merge over the static config.\n *\n * The string form looks the driver up in `setCacheConfigurations({ drivers })`,\n * loads it (or returns the cached instance), and sets it as `currentDriver`.\n * The instance form takes a pre-built driver and bypasses the registry; the\n * `runtimeOptions` argument is silently ignored in that case because the\n * instance was constructed externally.\n *\n * Runtime options merge over `config.options[name]` per-key — runtime wins.\n * Use this for constructor-only knobs that can't live in static config\n * (e.g. `pg`'s `client: pg.Pool`).\n *\n * @example\n * const pool = new Pool({ connectionString });\n * await cache.use(\"pg\", { client: pool });\n */\n public async use(\n driver: string | CacheDriver<any, any>,\n runtimeOptions?: Record<string, any>,\n ) {\n if (typeof driver === \"string\") {\n const driverInstance = await this.load(driver, runtimeOptions);\n\n if (!driverInstance) {\n throw new CacheConfigurationError(\n `Cache driver ${driver} is not found, please declare it in the cache drivers in the configurations list.`,\n );\n }\n\n driver = driverInstance;\n }\n\n this.attachGlobalListeners(driver);\n\n if (this.configurations.logging !== undefined) {\n driver.setLoggingState(this.configurations.logging);\n }\n\n this.currentDriver = driver;\n\n return this;\n }\n\n /**\n * Ensure driver is initialized before operations\n */\n protected ensureDriverInitialized(): void {\n if (!this.currentDriver) {\n throw new CacheDriverNotInitializedError();\n }\n }\n\n /**\n * Return the running metrics snapshot — counters, hit-rate, latency\n * percentiles, per-driver breakdowns. Lazy-attaches the collector on\n * first call so apps that never read metrics pay zero cost.\n *\n * @example\n * const m = cache.metrics();\n * console.log(`hit rate: ${(m.hitRate * 100).toFixed(1)}%`);\n * console.log(`p95: ${m.latencyMs.p95.toFixed(2)}ms`);\n */\n public metrics(): CacheMetricsSnapshot {\n return this.ensureMetricsCollector().snapshot();\n }\n\n /**\n * Wipe every counter + latency sample and reset `startedAt` to now.\n * The collector itself stays subscribed to events.\n */\n public resetMetrics(): void {\n this.ensureMetricsCollector().reset();\n }\n\n /**\n * Lazy-construct the metrics collector and wire it to the global event\n * bus. Subsequent calls return the same instance — survives `cache.use()`\n * driver switches because handlers attach via `on()` and re-bind to every\n * loaded driver.\n */\n protected ensureMetricsCollector(): CacheMetricsCollector {\n if (this.metricsCollector) {\n return this.metricsCollector;\n }\n\n const collector = new CacheMetricsCollector();\n\n this.on(\"hit\", (data) => collector.recordEvent(\"hit\", data));\n this.on(\"miss\", (data) => collector.recordEvent(\"miss\", data));\n this.on(\"set\", (data) => collector.recordEvent(\"set\", data));\n this.on(\"removed\", (data) => collector.recordEvent(\"removed\", data));\n this.on(\"error\", (data) => collector.recordEvent(\"error\", data));\n\n this.metricsCollector = collector;\n\n return collector;\n }\n\n /**\n * Time the body, record the elapsed milliseconds against the metrics\n * collector for the given driver (defaults to the current driver's name).\n * Pass-through if the collector hasn't been instantiated yet — apps that\n * don't read metrics never pay for sample collection.\n */\n protected async timed<T>(\n body: () => Promise<T>,\n driverName?: string,\n ): Promise<T> {\n if (!this.metricsCollector) {\n return body();\n }\n\n const start = performance.now();\n\n try {\n return await body();\n } finally {\n const elapsed = performance.now() - start;\n const name = driverName ?? this.currentDriver?.name ?? \"unknown\";\n this.metricsCollector.recordLatency(name, elapsed);\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async get<T = any>(key: CacheKey): Promise<T | null> {\n this.ensureDriverInitialized();\n return this.timed(() => this.currentDriver!.get<T>(key));\n }\n\n /**\n * Set a value in the cache.\n *\n * Accepts a positional TTL (number of seconds or duration string like `\"1h\"`)\n * or a rich {@link CacheSetOptions} object supporting `ttl`, `expiresAt`,\n * `tags`, `onConflict`, `namespace`, and per-call `driver` overrides.\n */\n public async set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions) {\n this.ensureDriverInitialized();\n\n const driverOverride =\n ttlOrOptions && typeof ttlOrOptions === \"object\" && \"driver\" in ttlOrOptions\n ? ttlOrOptions.driver\n : undefined;\n\n if (driverOverride) {\n const driver = await this.load(driverOverride);\n return this.timed(() => driver.set(key, value, ttlOrOptions), driver.name);\n }\n\n return this.timed(() => this.currentDriver!.set(key, value, ttlOrOptions));\n }\n\n /**\n * {@inheritdoc}\n */\n public async remove(key: CacheKey) {\n this.ensureDriverInitialized();\n return this.timed(() => this.currentDriver!.remove(key));\n }\n\n /**\n * {@inheritdoc}\n */\n public async removeNamespace(namespace: string) {\n this.ensureDriverInitialized();\n return this.currentDriver!.removeNamespace(namespace);\n }\n\n /**\n * {@inheritdoc}\n */\n public async flush() {\n this.ensureDriverInitialized();\n return this.currentDriver!.flush();\n }\n\n /**\n * {@inheritdoc}\n */\n public async connect() {\n this.ensureDriverInitialized();\n return this.currentDriver!.connect();\n }\n\n /**\n * {@inheritdoc}\n */\n public parseKey(key: CacheKey) {\n this.ensureDriverInitialized();\n return this.currentDriver!.parseKey(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public get options() {\n this.ensureDriverInitialized();\n return this.currentDriver!.options;\n }\n\n /**\n * {@inheritdoc}\n */\n public setOptions(options: Record<string, any>) {\n this.ensureDriverInitialized();\n return this.currentDriver!.setOptions(options || {});\n }\n\n /**\n * Return the loaded driver instance for `driverName`, loading it on first\n * call. Optional `runtimeOptions` follow the same merge-over-config rules\n * as {@link load}; passing options after the driver has already been\n * loaded throws to avoid silent swallowing.\n */\n public async driver(driverName: string, runtimeOptions?: Record<string, any>) {\n if (this.loadedDrivers[driverName]) {\n this.assertNoConflictingReload(driverName, runtimeOptions);\n\n return this.loadedDrivers[driverName];\n }\n\n return this.load(driverName, runtimeOptions);\n }\n\n /**\n * Initialize the cache manager and pick the default driver\n */\n public async init() {\n const defaultCacheDriverName = this.configurations.default;\n\n if (!defaultCacheDriverName) {\n return;\n }\n\n const driver = await this.driver(defaultCacheDriverName);\n\n await this.use(driver);\n }\n\n /**\n * Load and connect the registered driver named `driver`. First-call wins —\n * subsequent calls without `runtimeOptions` return the cached instance, and\n * subsequent calls *with* `runtimeOptions` throw {@link CacheConfigurationError}\n * to avoid silently dropping the new options.\n *\n * `runtimeOptions` merge over `config.options[driver]` per-key (runtime wins),\n * letting consumers split static knobs (table, ttl, globalPrefix) from\n * constructor-only ones (pg's `client`, custom adapters, etc.).\n *\n * @example\n * const pool = new Pool({ connectionString });\n * const pg = await cache.load(\"pg\", { client: pool });\n */\n public async load(driver: string, runtimeOptions?: Record<string, any>) {\n if (this.loadedDrivers[driver]) {\n this.assertNoConflictingReload(driver, runtimeOptions);\n\n return this.loadedDrivers[driver];\n }\n\n const inFlight = this.loadingDrivers.get(driver);\n\n if (inFlight) {\n this.assertNoConflictingReload(driver, runtimeOptions);\n\n return inFlight;\n }\n\n const Driver = this.configurations.drivers[\n driver as keyof typeof this.configurations.drivers\n ] as DriverClass | undefined;\n\n if (!Driver) {\n throw new CacheConfigurationError(\n `Cache driver ${driver} is not found, please declare it in the cache drivers in the configurations list.`,\n );\n }\n\n const driverInstance = new Driver();\n const configOptions =\n this.configurations.options[driver as keyof typeof this.configurations.options] || {};\n\n driverInstance.setOptions({ ...configOptions, ...(runtimeOptions ?? {}) });\n\n const promise = (async () => {\n try {\n await driverInstance.connect();\n\n this.attachGlobalListeners(driverInstance);\n\n this.loadedDrivers[driver] = driverInstance;\n\n return driverInstance as CacheDriver<any, any>;\n } finally {\n this.loadingDrivers.delete(driver);\n }\n })();\n\n this.loadingDrivers.set(driver, promise);\n\n return promise;\n }\n\n /**\n * Guard against silently dropping runtime options on a re-load. Once a\n * driver has been instantiated, its options are frozen — calling `load` /\n * `driver` / `use` again with a non-empty `runtimeOptions` would otherwise\n * appear to work but actually use the original options. We throw instead\n * so the misuse surfaces at the call site.\n */\n protected assertNoConflictingReload(\n driverName: string,\n runtimeOptions: Record<string, any> | undefined,\n ): void {\n if (runtimeOptions === undefined) {\n return;\n }\n\n if (Object.keys(runtimeOptions).length === 0) {\n return;\n }\n\n throw new CacheConfigurationError(\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.`,\n );\n }\n\n /**\n * Register and bind a driver\n */\n public registerDriver(driverName: string, driverClass: DriverClass) {\n (this.configurations.drivers as Record<string, DriverClass>)[driverName] = driverClass;\n }\n\n /**\n * Disconnect the cache manager\n */\n public async disconnect() {\n const drivers = new Set<any>(Object.values(this.loadedDrivers));\n\n if (this.currentDriver) {\n drivers.add(this.currentDriver);\n }\n\n await Promise.allSettled([...drivers].map(driver => driver.disconnect()));\n\n for (const name of Object.keys(this.loadedDrivers)) {\n delete this.loadedDrivers[name];\n }\n\n this.loadingDrivers.clear();\n }\n\n /**\n * {@inheritdoc}\n */\n public async has(key: CacheKey): Promise<boolean> {\n this.ensureDriverInitialized();\n return this.currentDriver!.has(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public async remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n this.ensureDriverInitialized();\n\n const driverOverride =\n ttlOrOptions && typeof ttlOrOptions === \"object\" && \"driver\" in ttlOrOptions\n ? ttlOrOptions.driver\n : undefined;\n\n if (driverOverride) {\n const driver = await this.load(driverOverride);\n return driver.remember(key, ttlOrOptions, callback);\n }\n\n return this.currentDriver!.remember(key, ttlOrOptions, callback);\n }\n\n /**\n * Stale-while-revalidate. Returns cached when fresh, returns the stale\n * value plus a background refresh when within `freshTtl..staleTtl`,\n * blocks like a normal miss past `staleTtl`. Honors per-call `driver`\n * override the same way `remember()` does.\n *\n * @example\n * const product = await cache.swr(\n * \"product.42\",\n * { freshTtl: \"1m\", staleTtl: \"1h\" },\n * () => db.products.find(42),\n * );\n */\n public async swr<T = any>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n this.ensureDriverInitialized();\n\n const driverOverride = options.driver;\n\n if (driverOverride) {\n const driver = await this.load(driverOverride);\n\n return driver.swr<T>(key, options, callback);\n }\n\n return this.currentDriver!.swr<T>(key, options, callback);\n }\n\n /**\n * {@inheritdoc}\n */\n public async pull(key: CacheKey): Promise<any | null> {\n this.ensureDriverInitialized();\n return this.currentDriver!.pull(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public async forever(key: CacheKey, value: any): Promise<any> {\n this.ensureDriverInitialized();\n return this.currentDriver!.forever(key, value);\n }\n\n /**\n * {@inheritdoc}\n */\n public async increment(key: CacheKey, value?: number): Promise<number> {\n this.ensureDriverInitialized();\n return this.currentDriver!.increment(key, value);\n }\n\n /**\n * {@inheritdoc}\n */\n public async decrement(key: CacheKey, value?: number): Promise<number> {\n this.ensureDriverInitialized();\n return this.currentDriver!.decrement(key, value);\n }\n\n /**\n * {@inheritdoc}\n */\n public async many(keys: CacheKey[]): Promise<any[]> {\n this.ensureDriverInitialized();\n return this.currentDriver!.many(keys);\n }\n\n /**\n * {@inheritdoc}\n */\n public async setMany(items: Record<string, any>, ttl?: number): Promise<void> {\n this.ensureDriverInitialized();\n return this.currentDriver!.setMany(items, ttl);\n }\n\n /**\n * Register a global event listener (applies to all drivers)\n */\n public on(event: CacheEventType, handler: CacheEventHandler): this {\n if (!this.globalEventListeners.has(event)) {\n this.globalEventListeners.set(event, new Set());\n }\n this.globalEventListeners.get(event)!.add(handler);\n\n // Also attach to current driver if exists\n if (this.currentDriver) {\n this.currentDriver.on(event, handler);\n }\n\n // Attach to all loaded drivers\n for (const driver of Object.values(this.loadedDrivers)) {\n driver.on(event, handler);\n }\n\n return this;\n }\n\n /**\n * Remove a global event listener\n */\n public off(event: CacheEventType, handler: CacheEventHandler): this {\n const handlers = this.globalEventListeners.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n\n // Also remove from current driver\n if (this.currentDriver) {\n this.currentDriver.off(event, handler);\n }\n\n // Remove from all loaded drivers\n for (const driver of Object.values(this.loadedDrivers)) {\n driver.off(event, handler);\n }\n\n return this;\n }\n\n /**\n * Register a one-time global event listener\n */\n public once(event: CacheEventType, handler: CacheEventHandler): this {\n const onceHandler: CacheEventHandler = async (data) => {\n await handler(data);\n this.off(event, onceHandler);\n };\n return this.on(event, onceHandler);\n }\n\n /**\n * Attach global listeners to a driver\n */\n protected attachGlobalListeners(driver: CacheDriver<any, any>) {\n for (const [event, handlers] of this.globalEventListeners) {\n for (const handler of handlers) {\n driver.on(event, handler);\n }\n }\n }\n\n /**\n * Set if not exists (atomic operation)\n * Returns true if key was set, false if key already existed\n * Note: Only supported by drivers that implement setNX (e.g., Redis)\n */\n public async setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\n this.ensureDriverInitialized();\n\n if (!this.currentDriver!.setNX) {\n throw new Error(\n `setNX is not supported by the current cache driver: ${this.currentDriver!.name}`,\n );\n }\n\n return this.currentDriver!.setNX(key, value, ttl);\n }\n\n /**\n * Create a tagged cache instance for the given tags\n */\n public tags(tags: string[]): TaggedCacheDriver {\n this.ensureDriverInitialized();\n return this.currentDriver!.tags(tags);\n }\n\n /**\n * Atomically read, transform, and write a cached value. Delegates to the current driver.\n */\n public async update<T = any>(\n key: CacheKey,\n fn: (current: T | null) => T | null | Promise<T | null>,\n options?: { ttl?: CacheTtl },\n ): Promise<T | null> {\n this.ensureDriverInitialized();\n return this.currentDriver!.update<T>(key, fn, options);\n }\n\n /**\n * Shallow-merge a partial object into a cached value.\n */\n public async merge<T extends Record<string, any> = Record<string, any>>(\n key: CacheKey,\n partial: Partial<T>,\n options?: { ttl?: CacheTtl },\n ): Promise<T> {\n this.ensureDriverInitialized();\n return this.currentDriver!.merge<T>(key, partial, options);\n }\n\n /**\n * Obtain a list accessor bound to the current driver.\n */\n public list<T = any>(key: CacheKey): CacheListAccessor<T> {\n this.ensureDriverInitialized();\n return this.currentDriver!.list<T>(key);\n }\n\n /**\n * Acquire a distributed lock, run `fn`, and auto-release. Returns a\n * {@link LockOutcome} discriminated union so callers can distinguish\n * \"ran and got this value\" from \"skipped because someone else holds it\".\n *\n * Honors the `driver` option for per-call driver override, same as `set`\n * and `remember`.\n *\n * @example\n * const outcome = await cache.lock(\"lock.import\", \"5m\", async () => {\n * await runImport();\n * return \"done\";\n * });\n * if (!outcome.acquired) {\n * console.log(\"another worker is already importing\");\n * }\n */\n public async lock<T>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | LockOptions,\n fn: () => Promise<T>,\n ): Promise<LockOutcome<T>> {\n this.ensureDriverInitialized();\n\n const driverOverride =\n ttlOrOptions && typeof ttlOrOptions === \"object\" && \"driver\" in ttlOrOptions\n ? ttlOrOptions.driver\n : undefined;\n\n const driver = driverOverride\n ? await this.load(driverOverride)\n : this.currentDriver!;\n\n return driver.lock<T>(key, ttlOrOptions as CacheTtl | Omit<LockOptions, \"driver\">, fn);\n }\n\n /**\n * Similarity retrieval. Delegates to the current driver's `similar()` impl.\n *\n * Drivers that lack a similarity index throw {@link CacheUnsupportedError}.\n *\n * @example\n * const hits = await cache.similar(await embed(query), { topK: 5, threshold: 0.7 });\n */\n /**\n * Create a scoped view over the cache. Every key written through the\n * returned scope is automatically prefixed with `prefix`; optional defaults\n * (`ttl`, `tags`) flow through every write inside the scope.\n *\n * Per-call options always win over scope defaults. Scope tags merge\n * additively with per-call tags. Nested scopes inherit from the parent.\n *\n * @example\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\n * await chat.set(\"messages.1\", msg); // → \"chats.10.messages.1\", 30d\n * await chat.set(\"draft\", d, { ttl: \"1h\" }); // per-call ttl wins\n * await chat.namespace(\"typing\", { ttl: \"5s\" }).set(\"user.42\", true);\n * await chat.clear(); // wipe the whole scope\n */\n public namespace(prefix: string, options?: CacheNamespaceOptions): ScopedCacheContract {\n this.ensureDriverInitialized();\n return new ScopedCache(this, prefix, options);\n }\n\n public async similar<T = any>(\n vector: number[],\n options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]> {\n this.ensureDriverInitialized();\n return this.currentDriver!.similar<T>(vector, options);\n }\n}\n\nexport const cache = new CacheManager();\n"],"mappings":"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"}
1
+ {"version":3,"file":"cache-manager.mjs","names":[],"sources":["../../../../../../../packages/cache/src/cache-manager.ts"],"sourcesContent":["import { CacheMetricsCollector } from \"./metrics\";\nimport { ScopedCache } from \"./scoped-cache\";\nimport type {\n CacheConfigurations,\n CacheDriver,\n CacheEventHandler,\n CacheEventType,\n CacheKey,\n CacheListAccessor,\n CacheMetricsSnapshot,\n CacheNamespaceOptions,\n CacheSetOptions,\n CacheSimilarHit,\n CacheSimilarOptions,\n CacheSwrOptions,\n CacheTtl,\n DriverClass,\n LockOptions,\n LockOutcome,\n RememberOptions,\n ScopedCacheContract,\n TaggedCacheDriver,\n} from \"./types\";\nimport { CacheConfigurationError, CacheDriverNotInitializedError } from \"./types\";\n\nexport class CacheManager implements CacheDriver<any, any> {\n /**\n * Cache Driver\n */\n public currentDriver?: CacheDriver<any, any>;\n\n /**\n * Loaded drivers\n */\n public loadedDrivers: Record<string, CacheDriver<any, any>> = {};\n\n /**\n * In-flight `load()` promises, so concurrent callers share one `connect()`.\n */\n protected loadingDrivers = new Map<string, Promise<CacheDriver<any, any>>>();\n\n /**\n * Configurations list\n */\n protected configurations: CacheConfigurations = {\n drivers: {},\n options: {},\n };\n\n /**\n * Global event listeners\n */\n protected globalEventListeners: Map<CacheEventType, Set<CacheEventHandler>> = new Map();\n\n /**\n * Metrics collector — lazy on first {@link metrics} call so apps that\n * never read metrics pay zero cost. Once instantiated, it stays\n * subscribed to events for the manager's lifetime.\n */\n protected metricsCollector?: CacheMetricsCollector;\n\n /**\n * {@inheritdoc}\n */\n public name = \"cacheManager\";\n\n /**\n * {@inheritdoc}\n */\n public get client() {\n return this.currentDriver?.client;\n }\n\n /**\n * Set the cache configurations\n */\n public setCacheConfigurations(configurations: CacheConfigurations) {\n this.configurations.default = configurations.default;\n this.configurations.drivers = configurations.drivers;\n this.configurations.options = configurations.options;\n this.configurations.logging = configurations.logging;\n }\n\n /**\n * Set logging state\n */\n public setLoggingState(loggingState: boolean) {\n this.ensureDriverInitialized();\n\n this.currentDriver!.setLoggingState(loggingState);\n }\n\n /**\n * Switch the manager to a registered driver, optionally injecting runtime\n * options that merge over the static config.\n *\n * The string form looks the driver up in `setCacheConfigurations({ drivers })`,\n * loads it (or returns the cached instance), and sets it as `currentDriver`.\n * The instance form takes a pre-built driver and bypasses the registry; the\n * `runtimeOptions` argument is silently ignored in that case because the\n * instance was constructed externally.\n *\n * Runtime options merge over `config.options[name]` per-key — runtime wins.\n * Use this for constructor-only knobs that can't live in static config\n * (e.g. `pg`'s `client: pg.Pool`).\n *\n * @example\n * const pool = new Pool({ connectionString });\n * await cache.use(\"pg\", { client: pool });\n */\n public async use(driver: string | CacheDriver<any, any>, runtimeOptions?: Record<string, any>) {\n if (typeof driver === \"string\") {\n const driverInstance = await this.load(driver, runtimeOptions);\n\n if (!driverInstance) {\n throw new CacheConfigurationError(\n `Cache driver ${driver} is not found, please declare it in the cache drivers in the configurations list.`,\n );\n }\n\n driver = driverInstance;\n }\n\n this.attachGlobalListeners(driver);\n\n if (this.configurations.logging !== undefined) {\n driver.setLoggingState(this.configurations.logging);\n }\n\n this.currentDriver = driver;\n\n return this;\n }\n\n /**\n * Ensure driver is initialized before operations\n */\n protected ensureDriverInitialized(): void {\n if (!this.currentDriver) {\n throw new CacheDriverNotInitializedError();\n }\n }\n\n /**\n * Return the running metrics snapshot — counters, hit-rate, latency\n * percentiles, per-driver breakdowns. Lazy-attaches the collector on\n * first call so apps that never read metrics pay zero cost.\n *\n * @example\n * const m = cache.metrics();\n * console.log(`hit rate: ${(m.hitRate * 100).toFixed(1)}%`);\n * console.log(`p95: ${m.latencyMs.p95.toFixed(2)}ms`);\n */\n public metrics(): CacheMetricsSnapshot {\n return this.ensureMetricsCollector().snapshot();\n }\n\n /**\n * Wipe every counter + latency sample and reset `startedAt` to now.\n * The collector itself stays subscribed to events.\n */\n public resetMetrics(): void {\n this.ensureMetricsCollector().reset();\n }\n\n /**\n * Lazy-construct the metrics collector and wire it to the global event\n * bus. Subsequent calls return the same instance — survives `cache.use()`\n * driver switches because handlers attach via `on()` and re-bind to every\n * loaded driver.\n */\n protected ensureMetricsCollector(): CacheMetricsCollector {\n if (this.metricsCollector) {\n return this.metricsCollector;\n }\n\n const collector = new CacheMetricsCollector();\n\n this.on(\"hit\", (data) => collector.recordEvent(\"hit\", data));\n this.on(\"miss\", (data) => collector.recordEvent(\"miss\", data));\n this.on(\"set\", (data) => collector.recordEvent(\"set\", data));\n this.on(\"removed\", (data) => collector.recordEvent(\"removed\", data));\n this.on(\"error\", (data) => collector.recordEvent(\"error\", data));\n\n this.metricsCollector = collector;\n\n return collector;\n }\n\n /**\n * Time the body, record the elapsed milliseconds against the metrics\n * collector for the given driver (defaults to the current driver's name).\n * Pass-through if the collector hasn't been instantiated yet — apps that\n * don't read metrics never pay for sample collection.\n */\n protected async timed<T>(body: () => Promise<T>, driverName?: string): Promise<T> {\n if (!this.metricsCollector) {\n return body();\n }\n\n const start = performance.now();\n\n try {\n return await body();\n } finally {\n const elapsed = performance.now() - start;\n const name = driverName ?? this.currentDriver?.name ?? \"unknown\";\n this.metricsCollector.recordLatency(name, elapsed);\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async get<T = any>(key: CacheKey): Promise<T | null> {\n this.ensureDriverInitialized();\n return this.timed(() => this.currentDriver!.get<T>(key));\n }\n\n /**\n * Set a value in the cache.\n *\n * Accepts a positional TTL (number of seconds or duration string like `\"1h\"`)\n * or a rich {@link CacheSetOptions} object supporting `ttl`, `expiresAt`,\n * `tags`, `onConflict`, `namespace`, and per-call `driver` overrides.\n */\n public async set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions) {\n this.ensureDriverInitialized();\n\n const driverOverride =\n ttlOrOptions && typeof ttlOrOptions === \"object\" && \"driver\" in ttlOrOptions\n ? ttlOrOptions.driver\n : undefined;\n\n if (driverOverride) {\n const driver = await this.load(driverOverride);\n return this.timed(() => driver.set(key, value, ttlOrOptions), driver.name);\n }\n\n return this.timed(() => this.currentDriver!.set(key, value, ttlOrOptions));\n }\n\n /**\n * {@inheritdoc}\n */\n public async remove(key: CacheKey) {\n this.ensureDriverInitialized();\n return this.timed(() => this.currentDriver!.remove(key));\n }\n\n /**\n * {@inheritdoc}\n */\n public async removeNamespace(namespace: string) {\n this.ensureDriverInitialized();\n return this.currentDriver!.removeNamespace(namespace);\n }\n\n /**\n * {@inheritdoc}\n */\n public async flush() {\n this.ensureDriverInitialized();\n return this.currentDriver!.flush();\n }\n\n /**\n * {@inheritdoc}\n */\n public async connect() {\n this.ensureDriverInitialized();\n return this.currentDriver!.connect();\n }\n\n /**\n * {@inheritdoc}\n */\n public parseKey(key: CacheKey) {\n this.ensureDriverInitialized();\n return this.currentDriver!.parseKey(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public get options() {\n this.ensureDriverInitialized();\n return this.currentDriver!.options;\n }\n\n /**\n * {@inheritdoc}\n */\n public setOptions(options: Record<string, any>) {\n this.ensureDriverInitialized();\n return this.currentDriver!.setOptions(options || {});\n }\n\n /**\n * Return the loaded driver instance for `driverName`, loading it on first\n * call. Optional `runtimeOptions` follow the same merge-over-config rules\n * as {@link load}; passing options after the driver has already been\n * loaded throws to avoid silent swallowing.\n */\n public async driver(driverName: string, runtimeOptions?: Record<string, any>) {\n if (this.loadedDrivers[driverName]) {\n this.assertNoConflictingReload(driverName, runtimeOptions);\n\n return this.loadedDrivers[driverName];\n }\n\n return this.load(driverName, runtimeOptions);\n }\n\n /**\n * Initialize the cache manager and pick the default driver\n */\n public async init() {\n const defaultCacheDriverName = this.configurations.default;\n\n if (!defaultCacheDriverName) {\n return;\n }\n\n const driver = await this.driver(defaultCacheDriverName);\n\n await this.use(driver);\n }\n\n /**\n * Load and connect the registered driver named `driver`. First-call wins —\n * subsequent calls without `runtimeOptions` return the cached instance, and\n * subsequent calls *with* `runtimeOptions` throw {@link CacheConfigurationError}\n * to avoid silently dropping the new options.\n *\n * `runtimeOptions` merge over `config.options[driver]` per-key (runtime wins),\n * letting consumers split static knobs (table, ttl, globalPrefix) from\n * constructor-only ones (pg's `client`, custom adapters, etc.).\n *\n * @example\n * const pool = new Pool({ connectionString });\n * const pg = await cache.load(\"pg\", { client: pool });\n */\n public async load(driver: string, runtimeOptions?: Record<string, any>) {\n if (this.loadedDrivers[driver]) {\n this.assertNoConflictingReload(driver, runtimeOptions);\n\n return this.loadedDrivers[driver];\n }\n\n const inFlight = this.loadingDrivers.get(driver);\n\n if (inFlight) {\n this.assertNoConflictingReload(driver, runtimeOptions);\n\n return inFlight;\n }\n\n const Driver = this.configurations.drivers[\n driver as keyof typeof this.configurations.drivers\n ] as DriverClass | undefined;\n\n if (!Driver) {\n throw new CacheConfigurationError(\n `Cache driver ${driver} is not found, please declare it in the cache drivers in the configurations list.`,\n );\n }\n\n const driverInstance = new Driver();\n const configOptions =\n this.configurations.options[driver as keyof typeof this.configurations.options] || {};\n\n driverInstance.setOptions({ ...configOptions, ...(runtimeOptions ?? {}) });\n\n const promise = (async () => {\n try {\n await driverInstance.connect();\n\n this.attachGlobalListeners(driverInstance);\n\n this.loadedDrivers[driver] = driverInstance;\n\n return driverInstance as CacheDriver<any, any>;\n } finally {\n this.loadingDrivers.delete(driver);\n }\n })();\n\n this.loadingDrivers.set(driver, promise);\n\n return promise;\n }\n\n /**\n * Guard against silently dropping runtime options on a re-load. Once a\n * driver has been instantiated, its options are frozen — calling `load` /\n * `driver` / `use` again with a non-empty `runtimeOptions` would otherwise\n * appear to work but actually use the original options. We throw instead\n * so the misuse surfaces at the call site.\n */\n protected assertNoConflictingReload(\n driverName: string,\n runtimeOptions: Record<string, any> | undefined,\n ): void {\n if (runtimeOptions === undefined) {\n return;\n }\n\n if (Object.keys(runtimeOptions).length === 0) {\n return;\n }\n\n throw new CacheConfigurationError(\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.`,\n );\n }\n\n /**\n * Register and bind a driver\n */\n public registerDriver(driverName: string, driverClass: DriverClass) {\n (this.configurations.drivers as Record<string, DriverClass>)[driverName] = driverClass;\n }\n\n /**\n * Disconnect the cache manager\n */\n public async disconnect() {\n const drivers = new Set<any>(Object.values(this.loadedDrivers));\n\n if (this.currentDriver) {\n drivers.add(this.currentDriver);\n }\n\n await Promise.allSettled([...drivers].map((driver) => driver.disconnect()));\n\n for (const name of Object.keys(this.loadedDrivers)) {\n delete this.loadedDrivers[name];\n }\n\n this.loadingDrivers.clear();\n }\n\n /**\n * {@inheritdoc}\n */\n public async has(key: CacheKey): Promise<boolean> {\n this.ensureDriverInitialized();\n return this.currentDriver!.has(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public async remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n this.ensureDriverInitialized();\n\n const driverOverride =\n ttlOrOptions && typeof ttlOrOptions === \"object\" && \"driver\" in ttlOrOptions\n ? ttlOrOptions.driver\n : undefined;\n\n if (driverOverride) {\n const driver = await this.load(driverOverride);\n return driver.remember(key, ttlOrOptions, callback);\n }\n\n return this.currentDriver!.remember(key, ttlOrOptions, callback);\n }\n\n /**\n * Stale-while-revalidate. Returns cached when fresh, returns the stale\n * value plus a background refresh when within `freshTtl..staleTtl`,\n * blocks like a normal miss past `staleTtl`. Honors per-call `driver`\n * override the same way `remember()` does.\n *\n * @example\n * const product = await cache.swr(\n * \"product.42\",\n * { freshTtl: \"1m\", staleTtl: \"1h\" },\n * () => db.products.find(42),\n * );\n */\n public async swr<T = any>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n this.ensureDriverInitialized();\n\n const driverOverride = options.driver;\n\n if (driverOverride) {\n const driver = await this.load(driverOverride);\n\n return driver.swr<T>(key, options, callback);\n }\n\n return this.currentDriver!.swr<T>(key, options, callback);\n }\n\n /**\n * {@inheritdoc}\n */\n public async pull(key: CacheKey): Promise<any | null> {\n this.ensureDriverInitialized();\n return this.currentDriver!.pull(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public async forever(key: CacheKey, value: any): Promise<any> {\n this.ensureDriverInitialized();\n return this.currentDriver!.forever(key, value);\n }\n\n /**\n * {@inheritdoc}\n */\n public async increment(\n key: CacheKey,\n value?: number,\n options?: { ttl?: CacheTtl },\n ): Promise<number> {\n this.ensureDriverInitialized();\n return this.currentDriver!.increment(key, value, options);\n }\n\n /**\n * {@inheritdoc}\n */\n public async decrement(key: CacheKey, value?: number): Promise<number> {\n this.ensureDriverInitialized();\n return this.currentDriver!.decrement(key, value);\n }\n\n /**\n * {@inheritdoc}\n */\n public async many(keys: CacheKey[]): Promise<any[]> {\n this.ensureDriverInitialized();\n return this.currentDriver!.many(keys);\n }\n\n /**\n * {@inheritdoc}\n */\n public async setMany(items: Record<string, any>, ttl?: number): Promise<void> {\n this.ensureDriverInitialized();\n return this.currentDriver!.setMany(items, ttl);\n }\n\n /**\n * Register a global event listener (applies to all drivers)\n */\n public on(event: CacheEventType, handler: CacheEventHandler): this {\n if (!this.globalEventListeners.has(event)) {\n this.globalEventListeners.set(event, new Set());\n }\n this.globalEventListeners.get(event)!.add(handler);\n\n // Also attach to current driver if exists\n if (this.currentDriver) {\n this.currentDriver.on(event, handler);\n }\n\n // Attach to all loaded drivers\n for (const driver of Object.values(this.loadedDrivers)) {\n driver.on(event, handler);\n }\n\n return this;\n }\n\n /**\n * Remove a global event listener\n */\n public off(event: CacheEventType, handler: CacheEventHandler): this {\n const handlers = this.globalEventListeners.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n\n // Also remove from current driver\n if (this.currentDriver) {\n this.currentDriver.off(event, handler);\n }\n\n // Remove from all loaded drivers\n for (const driver of Object.values(this.loadedDrivers)) {\n driver.off(event, handler);\n }\n\n return this;\n }\n\n /**\n * Register a one-time global event listener\n */\n public once(event: CacheEventType, handler: CacheEventHandler): this {\n const onceHandler: CacheEventHandler = async (data) => {\n await handler(data);\n this.off(event, onceHandler);\n };\n return this.on(event, onceHandler);\n }\n\n /**\n * Attach global listeners to a driver\n */\n protected attachGlobalListeners(driver: CacheDriver<any, any>) {\n for (const [event, handlers] of this.globalEventListeners) {\n for (const handler of handlers) {\n driver.on(event, handler);\n }\n }\n }\n\n /**\n * Set if not exists (atomic operation)\n * Returns true if key was set, false if key already existed\n * Note: Only supported by drivers that implement setNX (e.g., Redis)\n */\n public async setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\n this.ensureDriverInitialized();\n\n if (!this.currentDriver!.setNX) {\n throw new Error(\n `setNX is not supported by the current cache driver: ${this.currentDriver!.name}`,\n );\n }\n\n return this.currentDriver!.setNX(key, value, ttl);\n }\n\n /**\n * Create a tagged cache instance for the given tags\n */\n public tags(tags: string[]): TaggedCacheDriver {\n this.ensureDriverInitialized();\n return this.currentDriver!.tags(tags);\n }\n\n /**\n * Atomically read, transform, and write a cached value. Delegates to the current driver.\n */\n public async update<T = any>(\n key: CacheKey,\n fn: (current: T | null) => T | null | Promise<T | null>,\n options?: { ttl?: CacheTtl },\n ): Promise<T | null> {\n this.ensureDriverInitialized();\n return this.currentDriver!.update<T>(key, fn, options);\n }\n\n /**\n * Shallow-merge a partial object into a cached value.\n */\n public async merge<T extends Record<string, any> = Record<string, any>>(\n key: CacheKey,\n partial: Partial<T>,\n options?: { ttl?: CacheTtl },\n ): Promise<T> {\n this.ensureDriverInitialized();\n return this.currentDriver!.merge<T>(key, partial, options);\n }\n\n /**\n * Obtain a list accessor bound to the current driver.\n */\n public list<T = any>(key: CacheKey): CacheListAccessor<T> {\n this.ensureDriverInitialized();\n return this.currentDriver!.list<T>(key);\n }\n\n /**\n * Acquire a distributed lock, run `fn`, and auto-release. Returns a\n * {@link LockOutcome} discriminated union so callers can distinguish\n * \"ran and got this value\" from \"skipped because someone else holds it\".\n *\n * Honors the `driver` option for per-call driver override, same as `set`\n * and `remember`.\n *\n * @example\n * const outcome = await cache.lock(\"lock.import\", \"5m\", async () => {\n * await runImport();\n * return \"done\";\n * });\n * if (!outcome.acquired) {\n * console.log(\"another worker is already importing\");\n * }\n */\n public async lock<T>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | LockOptions,\n fn: () => Promise<T>,\n ): Promise<LockOutcome<T>> {\n this.ensureDriverInitialized();\n\n const driverOverride =\n ttlOrOptions && typeof ttlOrOptions === \"object\" && \"driver\" in ttlOrOptions\n ? ttlOrOptions.driver\n : undefined;\n\n const driver = driverOverride ? await this.load(driverOverride) : this.currentDriver!;\n\n return driver.lock<T>(key, ttlOrOptions as CacheTtl | Omit<LockOptions, \"driver\">, fn);\n }\n\n /**\n * Similarity retrieval. Delegates to the current driver's `similar()` impl.\n *\n * Drivers that lack a similarity index throw {@link CacheUnsupportedError}.\n *\n * @example\n * const hits = await cache.similar(await embed(query), { topK: 5, threshold: 0.7 });\n */\n /**\n * Create a scoped view over the cache. Every key written through the\n * returned scope is automatically prefixed with `prefix`; optional defaults\n * (`ttl`, `tags`) flow through every write inside the scope.\n *\n * Per-call options always win over scope defaults. Scope tags merge\n * additively with per-call tags. Nested scopes inherit from the parent.\n *\n * @example\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\n * await chat.set(\"messages.1\", msg); // → \"chats.10.messages.1\", 30d\n * await chat.set(\"draft\", d, { ttl: \"1h\" }); // per-call ttl wins\n * await chat.namespace(\"typing\", { ttl: \"5s\" }).set(\"user.42\", true);\n * await chat.clear(); // wipe the whole scope\n */\n public namespace(prefix: string, options?: CacheNamespaceOptions): ScopedCacheContract {\n this.ensureDriverInitialized();\n return new ScopedCache(this, prefix, options);\n }\n\n public async similar<T = any>(\n vector: number[],\n options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]> {\n this.ensureDriverInitialized();\n return this.currentDriver!.similar<T>(vector, options);\n }\n}\n\nexport const cache = new CacheManager();\n"],"mappings":"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"}
@@ -292,7 +292,9 @@ declare abstract class BaseCacheDriver<ClientType, Options extends Record<string
292
292
  * The remaining TTL is rounded down, so repeated increments can never push
293
293
  * a window's deadline later.
294
294
  */
295
- increment(key: CacheKey, value?: number): Promise<number>;
295
+ increment(key: CacheKey, value?: number, options?: {
296
+ ttl?: CacheTtl;
297
+ }): Promise<number>;
296
298
  /**
297
299
  * {@inheritdoc}
298
300
  */
@@ -1 +1 @@
1
- {"version":3,"file":"base-cache-driver.d.mts","names":[],"sources":["../../../../../../../../packages/cache/src/drivers/base-cache-driver.ts"],"mappings":"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"}
1
+ {"version":3,"file":"base-cache-driver.d.mts","names":[],"sources":["../../../../../../../../packages/cache/src/drivers/base-cache-driver.ts"],"mappings":"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"}
@@ -408,7 +408,8 @@ var BaseCacheDriver = class {
408
408
  * The remaining TTL is rounded down, so repeated increments can never push
409
409
  * a window's deadline later.
410
410
  */
411
- async increment(key, value = 1) {
411
+ async increment(key, value = 1, options = {}) {
412
+ const ttl = options.ttl === void 0 ? this.ttl : parseTtl(options.ttl);
412
413
  const parsedKey = this.parseKey(key);
413
414
  return this.runSerialized(parsedKey, async () => {
414
415
  const entry = await this.getEntry(key);
@@ -419,8 +420,8 @@ var BaseCacheDriver = class {
419
420
  const base = current === null || current === void 0 ? 0 : current;
420
421
  if (typeof base !== "number") throw new Error(`Cannot increment non-numeric value for key: ${maskEmailsInText(parsedKey)}`);
421
422
  const newValue = base + value;
422
- if (!live) await this.set(key, newValue);
423
- else if (expiresAt == null || expiresAt === Infinity) await this.set(key, newValue, { ttl: Infinity });
423
+ if (!live) await this.set(key, newValue, { ttl });
424
+ else if (expiresAt == null || expiresAt === Infinity) await this.set(key, newValue, { ttl: options.ttl === void 0 ? Infinity : ttl });
424
425
  else await this.set(key, newValue, { ttl: Math.max(1, Math.floor((expiresAt - now) / 1e3)) });
425
426
  return newValue;
426
427
  });
@@ -1 +1 @@
1
- {"version":3,"file":"base-cache-driver.mjs","names":[],"sources":["../../../../../../../../packages/cache/src/drivers/base-cache-driver.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { log, maskEmailsInText } from \"@warlock.js/logger\";\nimport { MemoryCacheList } from \"../list/memory-cache-list\";\nimport { TaggedCache } from \"../tagged-cache\";\nimport type {\n CacheConflictPolicy,\n CacheData,\n CacheDriver,\n CacheEventData,\n CacheEventHandler,\n CacheEventType,\n CacheKey,\n CacheListAccessor,\n CacheOperationType,\n CacheSetOptions,\n CacheSetResult,\n CacheSimilarHit,\n CacheSimilarOptions,\n CacheSwrOptions,\n CacheTtl,\n LockOptions,\n LockOutcome,\n RememberOptions,\n} from \"../types\";\nimport { CacheConfigurationError, CacheUnsupportedError } from \"../types\";\nimport {\n normalizeToOptions,\n parseCacheKey,\n parseTtl,\n resolveTtl,\n safeErrorInfo,\n} from \"../utils\";\n\n/**\n * Normalized form of the 3rd `set` argument.\n *\n * All drivers operate on this shape internally regardless of whether the caller\n * passed a positional TTL, a duration string, or a rich options object.\n */\nexport type NormalizedSetOptions = {\n /**\n * Final TTL in seconds — already merged with the driver-level default\n * (`this.options.ttl`). `Infinity` means \"no expiration\".\n *\n * Always populated. Drivers do NOT need to fall back to `this.ttl`\n * themselves — `resolveSetOptions` does the merge centrally so that\n * every driver respects the configured default without ceremony.\n */\n ttl: number;\n /**\n * Inline tag list, or undefined when none were provided.\n */\n tags?: string[];\n /**\n * Conflict policy. Defaults to `\"upsert\"`.\n */\n onConflict: CacheConflictPolicy;\n /**\n * Optional embedding vector for similarity retrieval. Drivers that do not\n * support similarity must throw {@link CacheUnsupportedError} when this is\n * present (rather than silently dropping it).\n */\n vector?: number[];\n /**\n * Optional freshness deadline as a millisecond timestamp. Set by `swr()`\n * to mark when the entry stops being \"fresh\" and becomes\n * \"stale-but-revalidatable.\" Drivers route this through\n * `prepareDataForStorage` so it persists in the wrapper.\n */\n staleAt?: number;\n};\n\nconst messages = {\n clearing: \"Clearing namespace\",\n cleared: \"Namespace cleared\",\n fetching: \"Fetching key\",\n fetched: \"Key fetched\",\n caching: \"Caching key\",\n cached: \"Key cached\",\n flushing: \"Flushing cache\",\n flushed: \"Cache flushed\",\n removing: \"Removing key\",\n removed: \"Key removed\",\n expired: \"Key expired\",\n notFound: \"Key not found\",\n connecting: \"Connecting to the cache engine.\",\n connected: \"Connected to the cache engine.\",\n disconnecting: \"Disconnecting from the cache engine.\",\n disconnected: \"Disconnected from the cache engine.\",\n connectionFailed: \"Failed to connect to the cache engine.\",\n error: \"Error occurred\",\n};\n\nexport abstract class BaseCacheDriver<\n ClientType,\n Options extends Record<string, any>,\n> implements CacheDriver<ClientType, Options> {\n /**\n * CLient driver\n */\n protected clientDriver!: ClientType;\n\n /**\n * Determine whether to log or not\n */\n protected shouldLog: boolean = true;\n\n /**\n * {@inheritdoc}\n */\n public get client() {\n return (this.clientDriver || this) as unknown as ClientType;\n }\n\n /**\n * Set logging state\n */\n public setLoggingState(shouldLog: boolean) {\n this.shouldLog = shouldLog;\n\n return this;\n }\n\n /**\n * Set client driver\n */\n public set client(client: ClientType) {\n this.clientDriver = client;\n }\n\n /**\n * Get the cache driver name\n */\n public abstract name: string;\n\n /**\n * Options list\n */\n public options!: Options;\n\n /**\n * Event listeners storage\n */\n protected eventListeners: Map<CacheEventType, Set<CacheEventHandler>> =\n new Map();\n\n /**\n * {@inheritdoc}\n */\n public parseKey(key: CacheKey) {\n return parseCacheKey(key, this.options);\n }\n\n /**\n * {@inheritdoc}\n */\n public setOptions(options: Options) {\n this.options = options || {};\n return this;\n }\n\n /**\n * Register an event listener\n */\n public on(event: CacheEventType, handler: CacheEventHandler): this {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n return this;\n }\n\n /**\n * Remove an event listener\n */\n public off(event: CacheEventType, handler: CacheEventHandler): this {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n return this;\n }\n\n /**\n * Register a one-time event listener\n */\n public once(event: CacheEventType, handler: CacheEventHandler): this {\n const onceHandler: CacheEventHandler = async (data) => {\n await handler(data);\n this.off(event, onceHandler);\n };\n return this.on(event, onceHandler);\n }\n\n /**\n * Emit an event to all registered listeners\n */\n protected async emit(\n event: CacheEventType,\n data: Partial<CacheEventData> = {},\n ): Promise<void> {\n const handlers = this.eventListeners.get(event);\n if (!handlers || handlers.size === 0) return;\n\n const eventData: CacheEventData = {\n driver: this.name,\n ...data,\n };\n\n // Execute all handlers\n const promises: Promise<void>[] = [];\n for (const handler of handlers) {\n try {\n const result = handler(eventData);\n if (result instanceof Promise) {\n promises.push(result);\n }\n } catch (error) {\n this.logError(`Error in event handler for '${event}'`, error);\n }\n }\n\n // Wait for all async handlers\n if (promises.length > 0) {\n await Promise.allSettled(promises);\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public abstract removeNamespace(namespace: string): Promise<any>;\n\n /**\n * {@inheritdoc}\n */\n public abstract set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any>;\n\n /**\n * Normalize the 3rd argument of a `set` call into a single shape every driver\n * can act on. Handles TTL parsing (number | string | Infinity), `expiresAt` →\n * relative TTL conversion, and mutual-exclusion validation.\n *\n * @throws {CacheConfigurationError} when `ttl` and `expiresAt` are passed together\n * or an unparseable duration string is supplied.\n */\n protected resolveSetOptions(\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): NormalizedSetOptions {\n const options = normalizeToOptions(ttlOrOptions);\n\n return {\n ttl: resolveTtl(options.ttl, options.expiresAt, this.ttl),\n tags: options.tags,\n onConflict: options.onConflict ?? \"upsert\",\n vector: options.vector,\n staleAt: options.staleAt,\n };\n }\n\n /**\n * Resolve the union of cache keys associated with any of the given tags.\n * Used by `similar()` to narrow the candidate pool before similarity ranking.\n *\n * Returns `null` when no tags are passed (callers should treat that as \"no filter\").\n * Keys come back parsed (prefixed) — the index stores them un-prefixed, but\n * callers match them against their own parsed storage keys.\n */\n protected async getKeysForTags(\n tags: string[] | undefined,\n ): Promise<Set<string> | null> {\n if (!tags || tags.length === 0) {\n return null;\n }\n\n const allKeys = new Set<string>();\n for (const tag of tags) {\n const keys = await this.tagMembers(`cache:tags:${tag}`);\n for (const k of keys) {\n allKeys.add(this.parseKey(k));\n }\n }\n\n return allKeys;\n }\n\n // ============================================================\n // Tag index primitives\n // ============================================================\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: the index is an ordinary entry holding a string\n * array, read-modified-written under {@link runSerialized} — correct within\n * THIS process only. Drivers with a native primitive override it (redis SET,\n * pg atomic upsert, the in-process drivers' {@link InMemoryTagIndex}).\n */\n public async tagAdd(tagKey: CacheKey, members: string[]): Promise<void> {\n if (members.length === 0) {\n return;\n }\n\n await this.runSerialized(this.parseKey(tagKey), async () => {\n const current = await this.readTagArray(tagKey);\n const merged = new Set(current);\n\n for (const member of members) {\n merged.add(member);\n }\n\n if (merged.size !== current.length) {\n await this.set(tagKey, [...merged], Infinity);\n }\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagMembers(tagKey: CacheKey): Promise<string[]> {\n return this.readTagArray(tagKey);\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: serialized read-filter-write (process-local); an\n * emptied index is removed.\n */\n public async tagRemove(tagKey: CacheKey, members: string[]): Promise<void> {\n if (members.length === 0) {\n return;\n }\n\n await this.runSerialized(this.parseKey(tagKey), async () => {\n const current = await this.readTagArray(tagKey);\n const dropped = new Set(members);\n const kept = current.filter((member) => !dropped.has(member));\n\n if (kept.length === current.length) {\n return;\n }\n\n if (kept.length === 0) {\n await this.remove(tagKey);\n } else {\n await this.set(tagKey, kept, Infinity);\n }\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagDelete(tagKey: CacheKey): Promise<void> {\n await this.runSerialized(this.parseKey(tagKey), () => this.remove(tagKey));\n }\n\n /**\n * The index stored as a plain entry, or `[]`. Non-string members (a corrupted\n * or foreign value) are ignored rather than trusted.\n */\n protected async readTagArray(tagKey: CacheKey): Promise<string[]> {\n const value = await this.get(tagKey);\n\n if (!Array.isArray(value)) {\n return [];\n }\n\n return value.filter((member): member is string => typeof member === \"string\");\n }\n\n /**\n * Apply tag relationships after a successful write. Called by drivers once\n * the value is in storage.\n *\n * Pass the caller's key, not the parsed one: the tag index stores keys\n * un-prefixed so `invalidate()` can delete them through `remove(key)`.\n */\n protected async applyTags(key: CacheKey, tags: string[]): Promise<void> {\n if (tags.length === 0) {\n return;\n }\n\n const tagged = this.tags(tags);\n await (tagged as TaggedCache).storeTagRelationship(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public abstract get(key: CacheKey): Promise<any>;\n\n /**\n * {@inheritdoc}\n */\n public abstract remove(key: CacheKey): Promise<void>;\n\n /**\n * {@inheritdoc}\n */\n public abstract flush(): Promise<void>;\n\n /**\n * {@inheritdoc}\n */\n public async has(key: CacheKey): Promise<boolean> {\n const value = await this.get(key);\n // Event is emitted by get() method\n return value !== null;\n }\n\n /**\n * In-flight `remember()` computations, one per parsed key (stampede guard).\n */\n protected rememberInFlight: Map<string, Promise<any>> = new Map();\n\n /**\n * In-flight `swr()` cold-miss fetches, one per parsed key.\n */\n protected swrFetchInFlight: Map<string, Promise<any>> = new Map();\n\n /**\n * In-flight `swr()` stale-window background refreshes, one per parsed key.\n */\n protected swrRefreshes: Map<string, Promise<void>> = new Map();\n\n /**\n * Per-key serialization chains for the in-process read-modify-write\n * operations (`update`, and the default `increment` / `pull`). Kept apart\n * from the stampede maps so a `remember()` can never break an update chain,\n * and never receives an unrelated promise.\n */\n protected updateChains: Map<string, Promise<any>> = new Map();\n\n /**\n * Run `task` once per key at a time: concurrent callers for the same\n * `parsedKey` share the in-flight promise registered in `inFlight`.\n */\n protected singleFlight<T>(\n inFlight: Map<string, Promise<any>>,\n parsedKey: string,\n task: () => Promise<T>,\n ): Promise<T> {\n const existing = inFlight.get(parsedKey);\n\n if (existing) {\n return existing as Promise<T>;\n }\n\n const promise: Promise<T> = task().finally(() => {\n if (inFlight.get(parsedKey) === promise) {\n inFlight.delete(parsedKey);\n }\n });\n\n inFlight.set(parsedKey, promise);\n\n return promise;\n }\n\n /**\n * Chain `task` after every earlier serialized task for the same key, so\n * read-modify-write callers in THIS process run one at a time. This is\n * process-local: it does not coordinate with other servers.\n */\n protected runSerialized<T>(parsedKey: string, task: () => Promise<T>): Promise<T> {\n const previous = this.updateChains.get(parsedKey) ?? Promise.resolve();\n\n const next = previous.catch(() => undefined).then(task);\n\n this.updateChains.set(parsedKey, next);\n\n // Clean up the slot once this link finishes, but only if nobody chained a\n // follow-up onto it meanwhile. The trailing catch keeps a rejected link\n // from surfacing as an unhandled rejection here (the caller still sees it).\n next\n .finally(() => {\n if (this.updateChains.get(parsedKey) === next) {\n this.updateChains.delete(parsedKey);\n }\n })\n .catch(() => undefined);\n\n return next;\n }\n\n /**\n * {@inheritdoc}\n */\n public async remember(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<any>,\n ): Promise<any> {\n const parsedKey = this.parseKey(key);\n\n // The options-form lets callers forward tags / driver-override through to\n // the cache-miss write. Normalize both shapes into a single CacheSetOptions\n // blob so there's one path from here on.\n const setOptions = this.normalizeRememberOptions(ttlOrOptions);\n\n // Only a real miss (`null`) recomputes: cached falsy values such as\n // `0`, `false` and `\"\"` are returned as-is.\n const cachedValue = await this.get(key);\n\n if (cachedValue !== null && cachedValue !== undefined) {\n return cachedValue;\n }\n\n return this.singleFlight(this.rememberInFlight, parsedKey, async () => {\n const result = await callback();\n\n await this.set(key, result, setOptions);\n\n return result;\n });\n }\n\n /**\n * Resolve the TTL-or-options arg of `remember` into a `CacheSetOptions` object\n * that can be passed straight to `set()`. Keeps the implementation unbranched.\n */\n protected normalizeRememberOptions(\n ttlOrOptions: CacheTtl | RememberOptions,\n ): CacheSetOptions {\n if (typeof ttlOrOptions === \"number\" || typeof ttlOrOptions === \"string\") {\n return { ttl: ttlOrOptions };\n }\n\n return {\n ttl: ttlOrOptions.ttl,\n tags: ttlOrOptions.tags,\n };\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: read raw entry, branch on freshness/staleness,\n * trigger background refresh in the stale window, fall through to\n * `callback` on miss/expiry. Concurrent cold-miss callers share one fetch\n * ({@link swrFetchInFlight}); concurrent stale-window callers share one\n * background refresh ({@link swrRefreshes}).\n *\n * Drivers without a real {@link getEntry} override degrade gracefully —\n * the synthetic entry has no `staleAt`, which the freshness check treats\n * as \"always fresh,\" so SWR behaves like a TTL-only cached read on those\n * drivers (no background refresh, but no double-fetch either).\n */\n public async swr<T = any>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n const parsedKey = this.parseKey(key);\n const freshSeconds = parseTtl(options.freshTtl);\n const staleSeconds = parseTtl(options.staleTtl);\n\n if (staleSeconds <= freshSeconds) {\n throw new Error(\n `cache.swr: 'staleTtl' (${staleSeconds}s) must be greater than 'freshTtl' (${freshSeconds}s).`,\n );\n }\n\n const entry = await this.getEntry(key);\n const now = Date.now();\n\n const isExpired = entry?.expiresAt !== undefined && entry.expiresAt <= now;\n\n if (!entry || isExpired) {\n return this.singleFlight(this.swrFetchInFlight, parsedKey, () =>\n this.swrFetchAndStore<T>(key, options, callback, freshSeconds, staleSeconds),\n );\n }\n\n const isFresh = entry.staleAt === undefined || entry.staleAt > now;\n\n if (isFresh) {\n return entry.data as T;\n }\n\n this.scheduleSwrRefresh<T>(\n parsedKey,\n key,\n options,\n callback,\n freshSeconds,\n staleSeconds,\n );\n\n return entry.data as T;\n }\n\n /**\n * Read the raw {@link CacheData} wrapper for a key, including any\n * `expiresAt` / `staleAt` metadata. Default implementation falls back to\n * `get()` and synthesizes a metadata-less wrapper — drivers that store\n * the wrapper directly (memory, lru, file, redis, pg, mock) override\n * this to return real metadata so SWR can branch on freshness.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const value = await this.get(key);\n\n if (value === null) {\n return null;\n }\n\n return { data: value };\n }\n\n /**\n * Remaining lifetime of an existing entry, in seconds — used by TTL-preserving\n * writes such as `update()` / `merge()` when the caller passes no explicit\n * `ttl`.\n *\n * - `Infinity` — the entry exists with no expiry (preserve \"never expires\").\n * - positive number — seconds left before the entry expires.\n * - `undefined` — the key is missing or already past its deadline; the caller\n * should fall back to the driver default TTL.\n *\n * Default reads `expiresAt` from {@link getEntry}, which the metadata-aware\n * drivers (memory, lru, mock, pg) populate. Drivers that track TTL natively\n * and don't carry `expiresAt` in their payload (Redis) override this.\n */\n protected async getRemainingTtl(key: CacheKey): Promise<number | undefined> {\n const entry = await this.getEntry(key);\n\n if (!entry) {\n return undefined;\n }\n\n if (!entry.expiresAt || entry.expiresAt === Infinity) {\n return Infinity;\n }\n\n const remainingSeconds = Math.ceil((entry.expiresAt - Date.now()) / 1000);\n\n return remainingSeconds > 0 ? remainingSeconds : undefined;\n }\n\n /**\n * Block-and-fetch path of `swr()`: invoked on miss or past-`staleTtl`\n * expiry. Writes through `set()` with the SWR options translated into\n * standard `CacheSetOptions` (ttl = staleTtl, staleAt = now + freshTtl).\n */\n protected async swrFetchAndStore<T>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n freshSeconds: number,\n staleSeconds: number,\n ): Promise<T> {\n const result = await callback();\n\n await this.set(key, result, {\n ttl: staleSeconds,\n staleAt: Date.now() + freshSeconds * 1000,\n tags: options.tags,\n });\n\n return result;\n }\n\n /**\n * Stale-window background refresh. Registers a single in-flight promise\n * per parsed key so concurrent SWR callers share one refresh. Failed\n * refreshes preserve the stale entry, log via `logError`, and emit on\n * `error` — the stale-returning caller never sees the failure.\n */\n protected scheduleSwrRefresh<T>(\n parsedKey: string,\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n freshSeconds: number,\n staleSeconds: number,\n ): void {\n if (this.swrRefreshes.has(parsedKey)) {\n return;\n }\n\n let refresh!: Promise<void>;\n refresh = (async () => {\n try {\n const result = await callback();\n\n await this.set(key, result, {\n ttl: staleSeconds,\n staleAt: Date.now() + freshSeconds * 1000,\n tags: options.tags,\n });\n } catch (error) {\n this.logError(`SWR background refresh failed for ${parsedKey}`, error);\n await this.emit(\"error\", { key: parsedKey, error });\n } finally {\n if (this.swrRefreshes.get(parsedKey) === refresh) {\n this.swrRefreshes.delete(parsedKey);\n }\n }\n })();\n\n this.swrRefreshes.set(parsedKey, refresh);\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: get-then-remove, serialized per key in THIS\n * process. Drivers with a native primitive override it to be atomic across\n * servers (redis `GETDEL`, pg `DELETE … RETURNING`) or process-atomic\n * (memory, lru, mock).\n */\n public async pull(key: CacheKey): Promise<any | null> {\n return this.runSerialized(this.parseKey(key), async () => {\n // Events are emitted by get() and remove()\n const value = await this.get(key);\n\n if (value !== null) {\n await this.remove(key);\n }\n\n return value;\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async forever(key: CacheKey, value: any): Promise<any> {\n // Event is emitted by set() method\n return this.set(key, value, Infinity);\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: read-add-write serialized per key in THIS process,\n * keeping the entry's deadline (a missing key starts from 0 with the driver\n * default TTL). Not atomic across servers: drivers with a native primitive\n * override it (redis `INCRBY`, pg single-statement upsert).\n *\n * The value and its deadline come from ONE read. Reading them separately\n * let a counter that expired in between be rewritten from its stale value\n * with no expiry at all (a login-throttle counter that then never resets).\n * The remaining TTL is rounded down, so repeated increments can never push\n * a window's deadline later.\n */\n public async increment(key: CacheKey, value: number = 1): Promise<number> {\n const parsedKey = this.parseKey(key);\n\n return this.runSerialized(parsedKey, async () => {\n const entry = await this.getEntry(key);\n const now = Date.now();\n const expiresAt = entry?.expiresAt;\n const live = entry ? expiresAt == null || expiresAt > now : false;\n const current = entry && live ? entry.data : null;\n const base = current === null || current === undefined ? 0 : current;\n\n if (typeof base !== \"number\") {\n throw new Error(`Cannot increment non-numeric value for key: ${maskEmailsInText(parsedKey)}`);\n }\n\n const newValue = base + value;\n\n if (!live) {\n // missing, or expired since it was read: a fresh counter\n await this.set(key, newValue);\n } else if (expiresAt == null || expiresAt === Infinity) {\n await this.set(key, newValue, { ttl: Infinity });\n } else {\n await this.set(key, newValue, {\n ttl: Math.max(1, Math.floor((expiresAt - now) / 1000)),\n });\n }\n\n return newValue;\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async decrement(key: CacheKey, value: number = 1): Promise<number> {\n return this.increment(key, -value);\n }\n\n /**\n * {@inheritdoc}\n */\n public async many(keys: CacheKey[]): Promise<any[]> {\n return Promise.all(keys.map((key) => this.get(key)));\n }\n\n /**\n * {@inheritdoc}\n */\n public async setMany(\n items: Record<string, any>,\n ttl?: number,\n ): Promise<void> {\n await Promise.all(\n Object.entries(items).map(([key, value]) => this.set(key, value, ttl)),\n );\n }\n\n /**\n * Log the operation\n */\n protected log(operation: CacheOperationType, key?: string | any) {\n if (!this.shouldLog) return;\n\n if (typeof key === \"string\") {\n // this will be likely used with file cache driver as it will convert the dot to slash\n // to make it consistent and not to confuse developers we will output the key by making sure it's a dot\n // An email inside a key (an app's `user.<email>` key, say) is personal\n // data in a log line; key-based redaction cannot reach it, so mask it.\n key = maskEmailsInText(key.replace(/\\//g, \".\"));\n }\n\n if (operation === \"connectionFailed\") {\n log.fatal(`cache.${this.name}`, operation, key);\n }\n\n if (operation == \"notFound\" || operation == \"expired\") {\n return log.info(\n \"cache.\" + this.name,\n operation,\n (key ? key + \" \" : \"\") + messages[operation],\n );\n }\n\n if (operation.endsWith(\"ed\")) {\n return log.success(\n \"cache.\" + this.name,\n operation,\n (key ? key + \" \" : \"\") + messages[operation],\n );\n }\n\n log.info(\n \"cache.\" + this.name,\n operation,\n (key ? key + \" \" : \"\") + messages[operation],\n );\n }\n\n /**\n * Log error message\n *\n * Never logs the raw `error` object — driver connection errors (Redis,\n * Postgres, ...) can carry the connection string, including the password,\n * in the message/cause. Only a redacted `{ message, code }` shape is\n * passed to the structured logger.\n */\n protected logError(message: string, error?: any) {\n // Error lines name the key (\"SWR background refresh failed for\n // user.alice@example.com\"), and they are written even with logging off,\n // so addresses are masked here as in `log()`.\n const maskedMessage = maskEmailsInText(message);\n\n if (error) {\n const info = safeErrorInfo(error);\n\n log.error(\"cache.\" + this.name, \"error\", maskedMessage, {\n ...info,\n message: maskEmailsInText(info.message),\n });\n } else {\n log.error(\"cache.\" + this.name, \"error\", maskedMessage);\n }\n }\n\n /**\n * Get the default TTL in seconds. Parses human-readable strings (`\"1h\"`, `\"30m\"`)\n * from driver options if present; falls back to `Infinity` when no default is set.\n */\n public get ttl() {\n if (this.options.ttl === undefined) {\n return Infinity;\n }\n\n return parseTtl(this.options.ttl);\n }\n\n /**\n * Get time to live value in milliseconds\n */\n public getExpiresAt(ttl: number = this.ttl) {\n if (ttl) {\n return new Date().getTime() + ttl * 1000;\n }\n }\n\n /**\n * Wrap a value with TTL and optional freshness metadata for backend\n * storage. `staleAt` persists alongside `expiresAt` when supplied — used\n * by the SWR flow to mark when the entry stops being fresh.\n */\n protected prepareDataForStorage(data: any, ttl?: number, staleAt?: number) {\n const preparedData: CacheData = {\n data,\n };\n\n if (ttl) {\n preparedData.ttl = ttl;\n preparedData.expiresAt = this.getExpiresAt(ttl);\n }\n\n if (staleAt !== undefined) {\n preparedData.staleAt = staleAt;\n }\n\n return preparedData;\n }\n\n /**\n * Parse fetched data from cache\n */\n protected async parseCachedData(key: string, data: CacheData) {\n this.log(\"fetched\", key);\n\n if (data.expiresAt && data.expiresAt < Date.now()) {\n this.remove(key);\n return null;\n }\n\n const value = data.data;\n\n // Skip cloning for primitives (immutable types)\n if (value === null || value === undefined) {\n return value;\n }\n\n const type = typeof value;\n if (type === \"string\" || type === \"number\" || type === \"boolean\") {\n return value;\n }\n\n // Deep clone objects/arrays to prevent cache mutation\n try {\n return structuredClone(value);\n } catch (error) {\n this.logError(\n `Failed to clone cached value for ${key}, typeof value: ${typeof value}`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async connect() {\n this.log(\"connecting\");\n this.log(\"connected\");\n await this.emit(\"connected\");\n }\n\n /**\n * {@inheritdoc}\n */\n public async disconnect() {\n this.log(\"disconnected\");\n await this.emit(\"disconnected\");\n }\n\n /**\n * Create a tagged cache instance for the given tags\n */\n public tags(tags: string[]): any {\n return new TaggedCache(tags, this);\n }\n\n /**\n * Emit `invalidated` on behalf of a {@link TaggedCache} created by this\n * driver, once per `invalidate()` call. Listeners cannot fail the\n * invalidation: `emit` isolates them.\n *\n * @internal\n */\n public async emitTagsInvalidated(data: {\n tags: string[];\n keys: string[];\n durationMs: number;\n }): Promise<void> {\n await this.emit(\"invalidated\", data);\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: read → transform → write, serialized per key in\n * THIS process only. Drivers that can offer cross-server atomicity (redis,\n * pg: compare-and-set with retries) override it.\n */\n public async update<T = any>(\n key: CacheKey,\n fn: (current: T | null) => T | null | Promise<T | null>,\n options: { ttl?: CacheTtl } = {},\n ): Promise<T | null> {\n // Chain each update onto the previous one for the same key so concurrent\n // callers are serialized end-to-end, not merely awakened together.\n return this.runSerialized(this.parseKey(key), async () => {\n const current = (await this.get(key)) as T | null;\n const result = await fn(current);\n\n if (result === null) {\n await this.remove(key);\n return null;\n }\n\n if (options.ttl !== undefined) {\n await this.set(key, result, { ttl: options.ttl });\n\n return result;\n }\n\n // No explicit TTL → preserve the existing entry's remaining lifetime\n // rather than resetting it to the driver default.\n const remainingTtl = await this.getRemainingTtl(key);\n\n if (remainingTtl !== undefined) {\n await this.set(key, result, { ttl: remainingTtl });\n } else {\n await this.set(key, result);\n }\n\n return result;\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async merge<T extends Record<string, any> = Record<string, any>>(\n key: CacheKey,\n partial: Partial<T>,\n options: { ttl?: CacheTtl } = {},\n ): Promise<T> {\n const result = await this.update<T>(\n key,\n (current) => {\n const base = (current ?? {}) as T;\n return { ...base, ...partial } as T;\n },\n options,\n );\n\n return result as T;\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: read-mutate-write array backed by the underlying\n * cache entry. Concrete drivers (e.g. Redis) override with native commands.\n */\n public list<T = any>(key: CacheKey): CacheListAccessor<T> {\n return new MemoryCacheList<T>(this, key);\n }\n\n /**\n * {@inheritdoc}\n *\n * Built on top of `set({ onConflict: \"create\" })` — Redis-native `SET … NX EX`\n * under the hood on Redis, emulated via key-existence check on other drivers.\n * The lock value is the resolved `owner` (defaults to `pid.<process.pid>`).\n *\n * Always releases in `finally`, even if `fn` throws — the thrown error\n * propagates to the caller unchanged, and a failed release is logged rather\n * than replacing `fn`'s result or error. The ttl must be finite and\n * positive; there is no renewal, so it must exceed the worst-case duration.\n */\n public async lock<T>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\n fn: () => Promise<T>,\n ): Promise<LockOutcome<T>> {\n const { ttl, owner } = this.normalizeLockOptions(ttlOrOptions);\n\n // A lock with no expiry stays held forever if its holder crashes, on every\n // server. Refuse it up front (parseTtl also rejects undefined / NaN).\n const ttlSeconds = parseTtl(ttl);\n\n if (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0) {\n throw new CacheConfigurationError(\n `cache.lock() requires a finite, positive ttl (got ${String(ttl)}). The ttl must exceed the worst-case duration of the locked work.`,\n );\n }\n // The stored value is `<owner>#<token>`: the token makes it unique per\n // acquisition so release can compare-and-delete instead of deleting blindly.\n const lockValue = `${owner ?? `pid.${process.pid}`}#${randomUUID()}`;\n\n const setResult = (await this.set(key, lockValue, {\n onConflict: \"create\",\n ttl,\n })) as CacheSetResult | unknown;\n\n // `onConflict` drivers return CacheSetResult. Drivers that no-op on set\n // (e.g. the null driver) may return anything else — treat that as\n // \"acquired\" since there's nothing to collide with.\n const wasSet =\n typeof setResult === \"object\" &&\n setResult !== null &&\n \"wasSet\" in setResult\n ? (setResult as CacheSetResult).wasSet\n : true;\n\n if (!wasSet) {\n return { acquired: false };\n }\n\n try {\n const value = await fn();\n return { acquired: true, value };\n } finally {\n // Only delete our own lock — if the TTL expired and a successor\n // acquired the key, its value differs and it is left alone. A release\n // failure (e.g. a Redis blip) is logged, never allowed to replace fn's\n // outcome: the lock still expires by its TTL.\n try {\n await this.deleteIfEquals(key, lockValue);\n } catch (error) {\n this.logError(`lock release failed for ${this.parseKey(key)}`, error);\n }\n }\n }\n\n /**\n * Compare-and-delete: remove `key` only when its current value equals\n * `expected`. Returns whether a delete happened.\n *\n * This default is get-then-remove and is NOT atomic across processes; drivers\n * with a real primitive (memory, redis, pg) override it.\n */\n protected async deleteIfEquals(key: CacheKey, expected: unknown): Promise<boolean> {\n if ((await this.get(key)) !== expected) {\n return false;\n }\n\n await this.remove(key);\n\n return true;\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation throws {@link CacheUnsupportedError}. Drivers that\n * support similarity retrieval (memory family, `pg`, `redis` w/ RediSearch)\n * override this with a real impl.\n */\n public async similar<T = any>(\n _vector: number[],\n _options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]> {\n throw new CacheUnsupportedError(\n `'${this.name}' driver does not support similarity retrieval. Use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).`,\n );\n }\n\n /**\n * Resolve the TTL-or-options arg of `lock` into a uniform shape.\n */\n protected normalizeLockOptions(\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\n ): {\n ttl: CacheTtl;\n owner?: string;\n } {\n if (typeof ttlOrOptions === \"number\" || typeof ttlOrOptions === \"string\") {\n return { ttl: ttlOrOptions };\n }\n\n return { ttl: ttlOrOptions.ttl, owner: ttlOrOptions.owner };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base-cache-driver.mjs","names":[],"sources":["../../../../../../../../packages/cache/src/drivers/base-cache-driver.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { log, maskEmailsInText } from \"@warlock.js/logger\";\nimport { MemoryCacheList } from \"../list/memory-cache-list\";\nimport { TaggedCache } from \"../tagged-cache\";\nimport type {\n CacheConflictPolicy,\n CacheData,\n CacheDriver,\n CacheEventData,\n CacheEventHandler,\n CacheEventType,\n CacheKey,\n CacheListAccessor,\n CacheOperationType,\n CacheSetOptions,\n CacheSetResult,\n CacheSimilarHit,\n CacheSimilarOptions,\n CacheSwrOptions,\n CacheTtl,\n LockOptions,\n LockOutcome,\n RememberOptions,\n} from \"../types\";\nimport { CacheConfigurationError, CacheUnsupportedError } from \"../types\";\nimport { normalizeToOptions, parseCacheKey, parseTtl, resolveTtl, safeErrorInfo } from \"../utils\";\n\n/**\n * Normalized form of the 3rd `set` argument.\n *\n * All drivers operate on this shape internally regardless of whether the caller\n * passed a positional TTL, a duration string, or a rich options object.\n */\nexport type NormalizedSetOptions = {\n /**\n * Final TTL in seconds — already merged with the driver-level default\n * (`this.options.ttl`). `Infinity` means \"no expiration\".\n *\n * Always populated. Drivers do NOT need to fall back to `this.ttl`\n * themselves — `resolveSetOptions` does the merge centrally so that\n * every driver respects the configured default without ceremony.\n */\n ttl: number;\n /**\n * Inline tag list, or undefined when none were provided.\n */\n tags?: string[];\n /**\n * Conflict policy. Defaults to `\"upsert\"`.\n */\n onConflict: CacheConflictPolicy;\n /**\n * Optional embedding vector for similarity retrieval. Drivers that do not\n * support similarity must throw {@link CacheUnsupportedError} when this is\n * present (rather than silently dropping it).\n */\n vector?: number[];\n /**\n * Optional freshness deadline as a millisecond timestamp. Set by `swr()`\n * to mark when the entry stops being \"fresh\" and becomes\n * \"stale-but-revalidatable.\" Drivers route this through\n * `prepareDataForStorage` so it persists in the wrapper.\n */\n staleAt?: number;\n};\n\nconst messages = {\n clearing: \"Clearing namespace\",\n cleared: \"Namespace cleared\",\n fetching: \"Fetching key\",\n fetched: \"Key fetched\",\n caching: \"Caching key\",\n cached: \"Key cached\",\n flushing: \"Flushing cache\",\n flushed: \"Cache flushed\",\n removing: \"Removing key\",\n removed: \"Key removed\",\n expired: \"Key expired\",\n notFound: \"Key not found\",\n connecting: \"Connecting to the cache engine.\",\n connected: \"Connected to the cache engine.\",\n disconnecting: \"Disconnecting from the cache engine.\",\n disconnected: \"Disconnected from the cache engine.\",\n connectionFailed: \"Failed to connect to the cache engine.\",\n error: \"Error occurred\",\n};\n\nexport abstract class BaseCacheDriver<\n ClientType,\n Options extends Record<string, any>,\n> implements CacheDriver<ClientType, Options> {\n /**\n * CLient driver\n */\n protected clientDriver!: ClientType;\n\n /**\n * Determine whether to log or not\n */\n protected shouldLog: boolean = true;\n\n /**\n * {@inheritdoc}\n */\n public get client() {\n return (this.clientDriver || this) as unknown as ClientType;\n }\n\n /**\n * Set logging state\n */\n public setLoggingState(shouldLog: boolean) {\n this.shouldLog = shouldLog;\n\n return this;\n }\n\n /**\n * Set client driver\n */\n public set client(client: ClientType) {\n this.clientDriver = client;\n }\n\n /**\n * Get the cache driver name\n */\n public abstract name: string;\n\n /**\n * Options list\n */\n public options!: Options;\n\n /**\n * Event listeners storage\n */\n protected eventListeners: Map<CacheEventType, Set<CacheEventHandler>> = new Map();\n\n /**\n * {@inheritdoc}\n */\n public parseKey(key: CacheKey) {\n return parseCacheKey(key, this.options);\n }\n\n /**\n * {@inheritdoc}\n */\n public setOptions(options: Options) {\n this.options = options || {};\n return this;\n }\n\n /**\n * Register an event listener\n */\n public on(event: CacheEventType, handler: CacheEventHandler): this {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(handler);\n return this;\n }\n\n /**\n * Remove an event listener\n */\n public off(event: CacheEventType, handler: CacheEventHandler): this {\n const handlers = this.eventListeners.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n return this;\n }\n\n /**\n * Register a one-time event listener\n */\n public once(event: CacheEventType, handler: CacheEventHandler): this {\n const onceHandler: CacheEventHandler = async (data) => {\n await handler(data);\n this.off(event, onceHandler);\n };\n return this.on(event, onceHandler);\n }\n\n /**\n * Emit an event to all registered listeners\n */\n protected async emit(event: CacheEventType, data: Partial<CacheEventData> = {}): Promise<void> {\n const handlers = this.eventListeners.get(event);\n if (!handlers || handlers.size === 0) return;\n\n const eventData: CacheEventData = {\n driver: this.name,\n ...data,\n };\n\n // Execute all handlers\n const promises: Promise<void>[] = [];\n for (const handler of handlers) {\n try {\n const result = handler(eventData);\n if (result instanceof Promise) {\n promises.push(result);\n }\n } catch (error) {\n this.logError(`Error in event handler for '${event}'`, error);\n }\n }\n\n // Wait for all async handlers\n if (promises.length > 0) {\n await Promise.allSettled(promises);\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public abstract removeNamespace(namespace: string): Promise<any>;\n\n /**\n * {@inheritdoc}\n */\n public abstract set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any>;\n\n /**\n * Normalize the 3rd argument of a `set` call into a single shape every driver\n * can act on. Handles TTL parsing (number | string | Infinity), `expiresAt` →\n * relative TTL conversion, and mutual-exclusion validation.\n *\n * @throws {CacheConfigurationError} when `ttl` and `expiresAt` are passed together\n * or an unparseable duration string is supplied.\n */\n protected resolveSetOptions(ttlOrOptions?: CacheTtl | CacheSetOptions): NormalizedSetOptions {\n const options = normalizeToOptions(ttlOrOptions);\n\n return {\n ttl: resolveTtl(options.ttl, options.expiresAt, this.ttl),\n tags: options.tags,\n onConflict: options.onConflict ?? \"upsert\",\n vector: options.vector,\n staleAt: options.staleAt,\n };\n }\n\n /**\n * Resolve the union of cache keys associated with any of the given tags.\n * Used by `similar()` to narrow the candidate pool before similarity ranking.\n *\n * Returns `null` when no tags are passed (callers should treat that as \"no filter\").\n * Keys come back parsed (prefixed) — the index stores them un-prefixed, but\n * callers match them against their own parsed storage keys.\n */\n protected async getKeysForTags(tags: string[] | undefined): Promise<Set<string> | null> {\n if (!tags || tags.length === 0) {\n return null;\n }\n\n const allKeys = new Set<string>();\n for (const tag of tags) {\n const keys = await this.tagMembers(`cache:tags:${tag}`);\n for (const k of keys) {\n allKeys.add(this.parseKey(k));\n }\n }\n\n return allKeys;\n }\n\n // ============================================================\n // Tag index primitives\n // ============================================================\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: the index is an ordinary entry holding a string\n * array, read-modified-written under {@link runSerialized} — correct within\n * THIS process only. Drivers with a native primitive override it (redis SET,\n * pg atomic upsert, the in-process drivers' {@link InMemoryTagIndex}).\n */\n public async tagAdd(tagKey: CacheKey, members: string[]): Promise<void> {\n if (members.length === 0) {\n return;\n }\n\n await this.runSerialized(this.parseKey(tagKey), async () => {\n const current = await this.readTagArray(tagKey);\n const merged = new Set(current);\n\n for (const member of members) {\n merged.add(member);\n }\n\n if (merged.size !== current.length) {\n await this.set(tagKey, [...merged], Infinity);\n }\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagMembers(tagKey: CacheKey): Promise<string[]> {\n return this.readTagArray(tagKey);\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: serialized read-filter-write (process-local); an\n * emptied index is removed.\n */\n public async tagRemove(tagKey: CacheKey, members: string[]): Promise<void> {\n if (members.length === 0) {\n return;\n }\n\n await this.runSerialized(this.parseKey(tagKey), async () => {\n const current = await this.readTagArray(tagKey);\n const dropped = new Set(members);\n const kept = current.filter((member) => !dropped.has(member));\n\n if (kept.length === current.length) {\n return;\n }\n\n if (kept.length === 0) {\n await this.remove(tagKey);\n } else {\n await this.set(tagKey, kept, Infinity);\n }\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagDelete(tagKey: CacheKey): Promise<void> {\n await this.runSerialized(this.parseKey(tagKey), () => this.remove(tagKey));\n }\n\n /**\n * The index stored as a plain entry, or `[]`. Non-string members (a corrupted\n * or foreign value) are ignored rather than trusted.\n */\n protected async readTagArray(tagKey: CacheKey): Promise<string[]> {\n const value = await this.get(tagKey);\n\n if (!Array.isArray(value)) {\n return [];\n }\n\n return value.filter((member): member is string => typeof member === \"string\");\n }\n\n /**\n * Apply tag relationships after a successful write. Called by drivers once\n * the value is in storage.\n *\n * Pass the caller's key, not the parsed one: the tag index stores keys\n * un-prefixed so `invalidate()` can delete them through `remove(key)`.\n */\n protected async applyTags(key: CacheKey, tags: string[]): Promise<void> {\n if (tags.length === 0) {\n return;\n }\n\n const tagged = this.tags(tags);\n await (tagged as TaggedCache).storeTagRelationship(key);\n }\n\n /**\n * {@inheritdoc}\n */\n public abstract get(key: CacheKey): Promise<any>;\n\n /**\n * {@inheritdoc}\n */\n public abstract remove(key: CacheKey): Promise<void>;\n\n /**\n * {@inheritdoc}\n */\n public abstract flush(): Promise<void>;\n\n /**\n * {@inheritdoc}\n */\n public async has(key: CacheKey): Promise<boolean> {\n const value = await this.get(key);\n // Event is emitted by get() method\n return value !== null;\n }\n\n /**\n * In-flight `remember()` computations, one per parsed key (stampede guard).\n */\n protected rememberInFlight: Map<string, Promise<any>> = new Map();\n\n /**\n * In-flight `swr()` cold-miss fetches, one per parsed key.\n */\n protected swrFetchInFlight: Map<string, Promise<any>> = new Map();\n\n /**\n * In-flight `swr()` stale-window background refreshes, one per parsed key.\n */\n protected swrRefreshes: Map<string, Promise<void>> = new Map();\n\n /**\n * Per-key serialization chains for the in-process read-modify-write\n * operations (`update`, and the default `increment` / `pull`). Kept apart\n * from the stampede maps so a `remember()` can never break an update chain,\n * and never receives an unrelated promise.\n */\n protected updateChains: Map<string, Promise<any>> = new Map();\n\n /**\n * Run `task` once per key at a time: concurrent callers for the same\n * `parsedKey` share the in-flight promise registered in `inFlight`.\n */\n protected singleFlight<T>(\n inFlight: Map<string, Promise<any>>,\n parsedKey: string,\n task: () => Promise<T>,\n ): Promise<T> {\n const existing = inFlight.get(parsedKey);\n\n if (existing) {\n return existing as Promise<T>;\n }\n\n const promise: Promise<T> = task().finally(() => {\n if (inFlight.get(parsedKey) === promise) {\n inFlight.delete(parsedKey);\n }\n });\n\n inFlight.set(parsedKey, promise);\n\n return promise;\n }\n\n /**\n * Chain `task` after every earlier serialized task for the same key, so\n * read-modify-write callers in THIS process run one at a time. This is\n * process-local: it does not coordinate with other servers.\n */\n protected runSerialized<T>(parsedKey: string, task: () => Promise<T>): Promise<T> {\n const previous = this.updateChains.get(parsedKey) ?? Promise.resolve();\n\n const next = previous.catch(() => undefined).then(task);\n\n this.updateChains.set(parsedKey, next);\n\n // Clean up the slot once this link finishes, but only if nobody chained a\n // follow-up onto it meanwhile. The trailing catch keeps a rejected link\n // from surfacing as an unhandled rejection here (the caller still sees it).\n next\n .finally(() => {\n if (this.updateChains.get(parsedKey) === next) {\n this.updateChains.delete(parsedKey);\n }\n })\n .catch(() => undefined);\n\n return next;\n }\n\n /**\n * {@inheritdoc}\n */\n public async remember(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<any>,\n ): Promise<any> {\n const parsedKey = this.parseKey(key);\n\n // The options-form lets callers forward tags / driver-override through to\n // the cache-miss write. Normalize both shapes into a single CacheSetOptions\n // blob so there's one path from here on.\n const setOptions = this.normalizeRememberOptions(ttlOrOptions);\n\n // Only a real miss (`null`) recomputes: cached falsy values such as\n // `0`, `false` and `\"\"` are returned as-is.\n const cachedValue = await this.get(key);\n\n if (cachedValue !== null && cachedValue !== undefined) {\n return cachedValue;\n }\n\n return this.singleFlight(this.rememberInFlight, parsedKey, async () => {\n const result = await callback();\n\n await this.set(key, result, setOptions);\n\n return result;\n });\n }\n\n /**\n * Resolve the TTL-or-options arg of `remember` into a `CacheSetOptions` object\n * that can be passed straight to `set()`. Keeps the implementation unbranched.\n */\n protected normalizeRememberOptions(ttlOrOptions: CacheTtl | RememberOptions): CacheSetOptions {\n if (typeof ttlOrOptions === \"number\" || typeof ttlOrOptions === \"string\") {\n return { ttl: ttlOrOptions };\n }\n\n return {\n ttl: ttlOrOptions.ttl,\n tags: ttlOrOptions.tags,\n };\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: read raw entry, branch on freshness/staleness,\n * trigger background refresh in the stale window, fall through to\n * `callback` on miss/expiry. Concurrent cold-miss callers share one fetch\n * ({@link swrFetchInFlight}); concurrent stale-window callers share one\n * background refresh ({@link swrRefreshes}).\n *\n * Drivers without a real {@link getEntry} override degrade gracefully —\n * the synthetic entry has no `staleAt`, which the freshness check treats\n * as \"always fresh,\" so SWR behaves like a TTL-only cached read on those\n * drivers (no background refresh, but no double-fetch either).\n */\n public async swr<T = any>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n const parsedKey = this.parseKey(key);\n const freshSeconds = parseTtl(options.freshTtl);\n const staleSeconds = parseTtl(options.staleTtl);\n\n if (staleSeconds <= freshSeconds) {\n throw new Error(\n `cache.swr: 'staleTtl' (${staleSeconds}s) must be greater than 'freshTtl' (${freshSeconds}s).`,\n );\n }\n\n const entry = await this.getEntry(key);\n const now = Date.now();\n\n const isExpired = entry?.expiresAt !== undefined && entry.expiresAt <= now;\n\n if (!entry || isExpired) {\n return this.singleFlight(this.swrFetchInFlight, parsedKey, () =>\n this.swrFetchAndStore<T>(key, options, callback, freshSeconds, staleSeconds),\n );\n }\n\n const isFresh = entry.staleAt === undefined || entry.staleAt > now;\n\n if (isFresh) {\n return entry.data as T;\n }\n\n this.scheduleSwrRefresh<T>(parsedKey, key, options, callback, freshSeconds, staleSeconds);\n\n return entry.data as T;\n }\n\n /**\n * Read the raw {@link CacheData} wrapper for a key, including any\n * `expiresAt` / `staleAt` metadata. Default implementation falls back to\n * `get()` and synthesizes a metadata-less wrapper — drivers that store\n * the wrapper directly (memory, lru, file, redis, pg, mock) override\n * this to return real metadata so SWR can branch on freshness.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const value = await this.get(key);\n\n if (value === null) {\n return null;\n }\n\n return { data: value };\n }\n\n /**\n * Remaining lifetime of an existing entry, in seconds — used by TTL-preserving\n * writes such as `update()` / `merge()` when the caller passes no explicit\n * `ttl`.\n *\n * - `Infinity` — the entry exists with no expiry (preserve \"never expires\").\n * - positive number — seconds left before the entry expires.\n * - `undefined` — the key is missing or already past its deadline; the caller\n * should fall back to the driver default TTL.\n *\n * Default reads `expiresAt` from {@link getEntry}, which the metadata-aware\n * drivers (memory, lru, mock, pg) populate. Drivers that track TTL natively\n * and don't carry `expiresAt` in their payload (Redis) override this.\n */\n protected async getRemainingTtl(key: CacheKey): Promise<number | undefined> {\n const entry = await this.getEntry(key);\n\n if (!entry) {\n return undefined;\n }\n\n if (!entry.expiresAt || entry.expiresAt === Infinity) {\n return Infinity;\n }\n\n const remainingSeconds = Math.ceil((entry.expiresAt - Date.now()) / 1000);\n\n return remainingSeconds > 0 ? remainingSeconds : undefined;\n }\n\n /**\n * Block-and-fetch path of `swr()`: invoked on miss or past-`staleTtl`\n * expiry. Writes through `set()` with the SWR options translated into\n * standard `CacheSetOptions` (ttl = staleTtl, staleAt = now + freshTtl).\n */\n protected async swrFetchAndStore<T>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n freshSeconds: number,\n staleSeconds: number,\n ): Promise<T> {\n const result = await callback();\n\n await this.set(key, result, {\n ttl: staleSeconds,\n staleAt: Date.now() + freshSeconds * 1000,\n tags: options.tags,\n });\n\n return result;\n }\n\n /**\n * Stale-window background refresh. Registers a single in-flight promise\n * per parsed key so concurrent SWR callers share one refresh. Failed\n * refreshes preserve the stale entry, log via `logError`, and emit on\n * `error` — the stale-returning caller never sees the failure.\n */\n protected scheduleSwrRefresh<T>(\n parsedKey: string,\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n freshSeconds: number,\n staleSeconds: number,\n ): void {\n if (this.swrRefreshes.has(parsedKey)) {\n return;\n }\n\n let refresh!: Promise<void>;\n refresh = (async () => {\n try {\n const result = await callback();\n\n await this.set(key, result, {\n ttl: staleSeconds,\n staleAt: Date.now() + freshSeconds * 1000,\n tags: options.tags,\n });\n } catch (error) {\n this.logError(`SWR background refresh failed for ${parsedKey}`, error);\n await this.emit(\"error\", { key: parsedKey, error });\n } finally {\n if (this.swrRefreshes.get(parsedKey) === refresh) {\n this.swrRefreshes.delete(parsedKey);\n }\n }\n })();\n\n this.swrRefreshes.set(parsedKey, refresh);\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: get-then-remove, serialized per key in THIS\n * process. Drivers with a native primitive override it to be atomic across\n * servers (redis `GETDEL`, pg `DELETE … RETURNING`) or process-atomic\n * (memory, lru, mock).\n */\n public async pull(key: CacheKey): Promise<any | null> {\n return this.runSerialized(this.parseKey(key), async () => {\n // Events are emitted by get() and remove()\n const value = await this.get(key);\n\n if (value !== null) {\n await this.remove(key);\n }\n\n return value;\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async forever(key: CacheKey, value: any): Promise<any> {\n // Event is emitted by set() method\n return this.set(key, value, Infinity);\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: read-add-write serialized per key in THIS process,\n * keeping the entry's deadline (a missing key starts from 0 with the driver\n * default TTL). Not atomic across servers: drivers with a native primitive\n * override it (redis `INCRBY`, pg single-statement upsert).\n *\n * The value and its deadline come from ONE read. Reading them separately\n * let a counter that expired in between be rewritten from its stale value\n * with no expiry at all (a login-throttle counter that then never resets).\n * The remaining TTL is rounded down, so repeated increments can never push\n * a window's deadline later.\n */\n public async increment(\n key: CacheKey,\n value: number = 1,\n options: { ttl?: CacheTtl } = {},\n ): Promise<number> {\n const ttl = options.ttl === undefined ? this.ttl : parseTtl(options.ttl);\n const parsedKey = this.parseKey(key);\n\n return this.runSerialized(parsedKey, async () => {\n const entry = await this.getEntry(key);\n const now = Date.now();\n const expiresAt = entry?.expiresAt;\n const live = entry ? expiresAt == null || expiresAt > now : false;\n const current = entry && live ? entry.data : null;\n const base = current === null || current === undefined ? 0 : current;\n\n if (typeof base !== \"number\") {\n throw new Error(\n `Cannot increment non-numeric value for key: ${maskEmailsInText(parsedKey)}`,\n );\n }\n\n const newValue = base + value;\n\n if (!live) {\n // missing, or expired since it was read: a fresh counter\n await this.set(key, newValue, { ttl });\n } else if (expiresAt == null || expiresAt === Infinity) {\n await this.set(key, newValue, { ttl: options.ttl === undefined ? Infinity : ttl });\n } else {\n await this.set(key, newValue, {\n ttl: Math.max(1, Math.floor((expiresAt - now) / 1000)),\n });\n }\n\n return newValue;\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async decrement(key: CacheKey, value: number = 1): Promise<number> {\n return this.increment(key, -value);\n }\n\n /**\n * {@inheritdoc}\n */\n public async many(keys: CacheKey[]): Promise<any[]> {\n return Promise.all(keys.map((key) => this.get(key)));\n }\n\n /**\n * {@inheritdoc}\n */\n public async setMany(items: Record<string, any>, ttl?: number): Promise<void> {\n await Promise.all(Object.entries(items).map(([key, value]) => this.set(key, value, ttl)));\n }\n\n /**\n * Log the operation\n */\n protected log(operation: CacheOperationType, key?: string | any) {\n if (!this.shouldLog) return;\n\n if (typeof key === \"string\") {\n // this will be likely used with file cache driver as it will convert the dot to slash\n // to make it consistent and not to confuse developers we will output the key by making sure it's a dot\n // An email inside a key (an app's `user.<email>` key, say) is personal\n // data in a log line; key-based redaction cannot reach it, so mask it.\n key = maskEmailsInText(key.replace(/\\//g, \".\"));\n }\n\n if (operation === \"connectionFailed\") {\n log.fatal(`cache.${this.name}`, operation, key);\n }\n\n if (operation == \"notFound\" || operation == \"expired\") {\n return log.info(\n \"cache.\" + this.name,\n operation,\n (key ? key + \" \" : \"\") + messages[operation],\n );\n }\n\n if (operation.endsWith(\"ed\")) {\n return log.success(\n \"cache.\" + this.name,\n operation,\n (key ? key + \" \" : \"\") + messages[operation],\n );\n }\n\n log.info(\"cache.\" + this.name, operation, (key ? key + \" \" : \"\") + messages[operation]);\n }\n\n /**\n * Log error message\n *\n * Never logs the raw `error` object — driver connection errors (Redis,\n * Postgres, ...) can carry the connection string, including the password,\n * in the message/cause. Only a redacted `{ message, code }` shape is\n * passed to the structured logger.\n */\n protected logError(message: string, error?: any) {\n // Error lines name the key (\"SWR background refresh failed for\n // user.alice@example.com\"), and they are written even with logging off,\n // so addresses are masked here as in `log()`.\n const maskedMessage = maskEmailsInText(message);\n\n if (error) {\n const info = safeErrorInfo(error);\n\n log.error(\"cache.\" + this.name, \"error\", maskedMessage, {\n ...info,\n message: maskEmailsInText(info.message),\n });\n } else {\n log.error(\"cache.\" + this.name, \"error\", maskedMessage);\n }\n }\n\n /**\n * Get the default TTL in seconds. Parses human-readable strings (`\"1h\"`, `\"30m\"`)\n * from driver options if present; falls back to `Infinity` when no default is set.\n */\n public get ttl() {\n if (this.options.ttl === undefined) {\n return Infinity;\n }\n\n return parseTtl(this.options.ttl);\n }\n\n /**\n * Get time to live value in milliseconds\n */\n public getExpiresAt(ttl: number = this.ttl) {\n if (ttl) {\n return new Date().getTime() + ttl * 1000;\n }\n }\n\n /**\n * Wrap a value with TTL and optional freshness metadata for backend\n * storage. `staleAt` persists alongside `expiresAt` when supplied — used\n * by the SWR flow to mark when the entry stops being fresh.\n */\n protected prepareDataForStorage(data: any, ttl?: number, staleAt?: number) {\n const preparedData: CacheData = {\n data,\n };\n\n if (ttl) {\n preparedData.ttl = ttl;\n preparedData.expiresAt = this.getExpiresAt(ttl);\n }\n\n if (staleAt !== undefined) {\n preparedData.staleAt = staleAt;\n }\n\n return preparedData;\n }\n\n /**\n * Parse fetched data from cache\n */\n protected async parseCachedData(key: string, data: CacheData) {\n this.log(\"fetched\", key);\n\n if (data.expiresAt && data.expiresAt < Date.now()) {\n this.remove(key);\n return null;\n }\n\n const value = data.data;\n\n // Skip cloning for primitives (immutable types)\n if (value === null || value === undefined) {\n return value;\n }\n\n const type = typeof value;\n if (type === \"string\" || type === \"number\" || type === \"boolean\") {\n return value;\n }\n\n // Deep clone objects/arrays to prevent cache mutation\n try {\n return structuredClone(value);\n } catch (error) {\n this.logError(\n `Failed to clone cached value for ${key}, typeof value: ${typeof value}`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async connect() {\n this.log(\"connecting\");\n this.log(\"connected\");\n await this.emit(\"connected\");\n }\n\n /**\n * {@inheritdoc}\n */\n public async disconnect() {\n this.log(\"disconnected\");\n await this.emit(\"disconnected\");\n }\n\n /**\n * Create a tagged cache instance for the given tags\n */\n public tags(tags: string[]): any {\n return new TaggedCache(tags, this);\n }\n\n /**\n * Emit `invalidated` on behalf of a {@link TaggedCache} created by this\n * driver, once per `invalidate()` call. Listeners cannot fail the\n * invalidation: `emit` isolates them.\n *\n * @internal\n */\n public async emitTagsInvalidated(data: {\n tags: string[];\n keys: string[];\n durationMs: number;\n }): Promise<void> {\n await this.emit(\"invalidated\", data);\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: read → transform → write, serialized per key in\n * THIS process only. Drivers that can offer cross-server atomicity (redis,\n * pg: compare-and-set with retries) override it.\n */\n public async update<T = any>(\n key: CacheKey,\n fn: (current: T | null) => T | null | Promise<T | null>,\n options: { ttl?: CacheTtl } = {},\n ): Promise<T | null> {\n // Chain each update onto the previous one for the same key so concurrent\n // callers are serialized end-to-end, not merely awakened together.\n return this.runSerialized(this.parseKey(key), async () => {\n const current = (await this.get(key)) as T | null;\n const result = await fn(current);\n\n if (result === null) {\n await this.remove(key);\n return null;\n }\n\n if (options.ttl !== undefined) {\n await this.set(key, result, { ttl: options.ttl });\n\n return result;\n }\n\n // No explicit TTL → preserve the existing entry's remaining lifetime\n // rather than resetting it to the driver default.\n const remainingTtl = await this.getRemainingTtl(key);\n\n if (remainingTtl !== undefined) {\n await this.set(key, result, { ttl: remainingTtl });\n } else {\n await this.set(key, result);\n }\n\n return result;\n });\n }\n\n /**\n * {@inheritdoc}\n */\n public async merge<T extends Record<string, any> = Record<string, any>>(\n key: CacheKey,\n partial: Partial<T>,\n options: { ttl?: CacheTtl } = {},\n ): Promise<T> {\n const result = await this.update<T>(\n key,\n (current) => {\n const base = (current ?? {}) as T;\n return { ...base, ...partial } as T;\n },\n options,\n );\n\n return result as T;\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation: read-mutate-write array backed by the underlying\n * cache entry. Concrete drivers (e.g. Redis) override with native commands.\n */\n public list<T = any>(key: CacheKey): CacheListAccessor<T> {\n return new MemoryCacheList<T>(this, key);\n }\n\n /**\n * {@inheritdoc}\n *\n * Built on top of `set({ onConflict: \"create\" })` — Redis-native `SET … NX EX`\n * under the hood on Redis, emulated via key-existence check on other drivers.\n * The lock value is the resolved `owner` (defaults to `pid.<process.pid>`).\n *\n * Always releases in `finally`, even if `fn` throws — the thrown error\n * propagates to the caller unchanged, and a failed release is logged rather\n * than replacing `fn`'s result or error. The ttl must be finite and\n * positive; there is no renewal, so it must exceed the worst-case duration.\n */\n public async lock<T>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\n fn: () => Promise<T>,\n ): Promise<LockOutcome<T>> {\n const { ttl, owner } = this.normalizeLockOptions(ttlOrOptions);\n\n // A lock with no expiry stays held forever if its holder crashes, on every\n // server. Refuse it up front (parseTtl also rejects undefined / NaN).\n const ttlSeconds = parseTtl(ttl);\n\n if (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0) {\n throw new CacheConfigurationError(\n `cache.lock() requires a finite, positive ttl (got ${String(ttl)}). The ttl must exceed the worst-case duration of the locked work.`,\n );\n }\n // The stored value is `<owner>#<token>`: the token makes it unique per\n // acquisition so release can compare-and-delete instead of deleting blindly.\n const lockValue = `${owner ?? `pid.${process.pid}`}#${randomUUID()}`;\n\n const setResult = (await this.set(key, lockValue, {\n onConflict: \"create\",\n ttl,\n })) as CacheSetResult | unknown;\n\n // `onConflict` drivers return CacheSetResult. Drivers that no-op on set\n // (e.g. the null driver) may return anything else — treat that as\n // \"acquired\" since there's nothing to collide with.\n const wasSet =\n typeof setResult === \"object\" && setResult !== null && \"wasSet\" in setResult\n ? (setResult as CacheSetResult).wasSet\n : true;\n\n if (!wasSet) {\n return { acquired: false };\n }\n\n try {\n const value = await fn();\n return { acquired: true, value };\n } finally {\n // Only delete our own lock — if the TTL expired and a successor\n // acquired the key, its value differs and it is left alone. A release\n // failure (e.g. a Redis blip) is logged, never allowed to replace fn's\n // outcome: the lock still expires by its TTL.\n try {\n await this.deleteIfEquals(key, lockValue);\n } catch (error) {\n this.logError(`lock release failed for ${this.parseKey(key)}`, error);\n }\n }\n }\n\n /**\n * Compare-and-delete: remove `key` only when its current value equals\n * `expected`. Returns whether a delete happened.\n *\n * This default is get-then-remove and is NOT atomic across processes; drivers\n * with a real primitive (memory, redis, pg) override it.\n */\n protected async deleteIfEquals(key: CacheKey, expected: unknown): Promise<boolean> {\n if ((await this.get(key)) !== expected) {\n return false;\n }\n\n await this.remove(key);\n\n return true;\n }\n\n /**\n * {@inheritdoc}\n *\n * Default implementation throws {@link CacheUnsupportedError}. Drivers that\n * support similarity retrieval (memory family, `pg`, `redis` w/ RediSearch)\n * override this with a real impl.\n */\n public async similar<T = any>(\n _vector: number[],\n _options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]> {\n throw new CacheUnsupportedError(\n `'${this.name}' driver does not support similarity retrieval. Use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).`,\n );\n }\n\n /**\n * Resolve the TTL-or-options arg of `lock` into a uniform shape.\n */\n protected normalizeLockOptions(ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">): {\n ttl: CacheTtl;\n owner?: string;\n } {\n if (typeof ttlOrOptions === \"number\" || typeof ttlOrOptions === \"string\") {\n return { ttl: ttlOrOptions };\n }\n\n return { ttl: ttlOrOptions.ttl, owner: ttlOrOptions.owner };\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-cache-driver.d.mts","names":[],"sources":["../../../../../../../../packages/cache/src/drivers/file-cache-driver.ts"],"mappings":";;;;cAsBa,eAAA,SACH,eAAA,CAAgB,eAAA,EAAiB,gBAAA,aAC9B,WAAA,CAAY,eAAA,EAAiB,gBAAA;;AAF1C;;EAOS,IAAA;EANiB;;;EAWjB,UAAA,CAAW,OAAA,EAAS,gBAAA;EAAA;;;EAAA,IAahB,SAAA;EAqJiB;;;EAAA,IAtIjB,QAAA;EA+PY;;;;;;;;;;EAAA,UA3Ob,YAAA,CAAa,SAAA;EA3Df;;;;EAAA,UAyEE,gBAAA,CAAiB,SAAA;EAzEH;;;;;;;EAAA,UAuFd,gBAAA,CAAiB,OAAA;EA5ET;;;;;EAAA,UAwFR,aAAA,CAAc,OAAA;EA1BG;;;;;;;;;;;EAuDd,eAAA,CAAgB,SAAA,WAAiB,OAAA;EA6C7B;;;;;;;EAHJ,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EAkEA;;;;;;;;EAAA,UAHa,cAAA,CACd,QAAA,UACA,IAAA,EAAM,SAAA,GACL,OAAA;IAAU,QAAA;EAAA;EAsGA;;;;;;;;EAhDA,MAAA,IAAU,OAAA;EA6GL;;;EApGL,KAAA,IAAS,OAAA;EAsHF;;;;;EAAA,UA3GJ,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;;;;EA4BpC,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;;;;EAyCjB,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;;;;EAoBpB,KAAA,IAAK,OAAA;;;;EAkBL,OAAA,IAAO,OAAA;AAAA"}
1
+ {"version":3,"file":"file-cache-driver.d.mts","names":[],"sources":["../../../../../../../../packages/cache/src/drivers/file-cache-driver.ts"],"mappings":";;;;cAsBa,eAAA,SACH,eAAA,CAAgB,eAAA,EAAiB,gBAAA,aAC9B,WAAA,CAAY,eAAA,EAAiB,gBAAA;;AAF1C;;EAOS,IAAA;EANiB;;;EAWjB,UAAA,CAAW,OAAA,EAAS,gBAAA;EAAA;;;EAAA,IAahB,SAAA;EAyIiB;;;EAAA,IA1HjB,QAAA;EAsPY;;;;;;;;;;EAAA,UAlOb,YAAA,CAAa,SAAA;EA3Df;;;;EAAA,UAyEE,gBAAA,CAAiB,SAAA;EAzEH;;;;;;;EAAA,UAoFd,gBAAA,CAAiB,OAAA;EAzET;;;;;EAAA,UAkFR,aAAA,CAAc,OAAA;EApBG;;;;;;;;;;;EA2Cd,eAAA,CAAgB,SAAA,WAAiB,OAAA;EA6C7B;;;;;;;EAHJ,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EAmEA;;;;;;;;EAAA,UAHa,cAAA,CACd,QAAA,UACA,IAAA,EAAM,SAAA,GACL,OAAA;IAAU,QAAA;EAAA;EAwGA;;;;;;;;EAhDA,MAAA,IAAU,OAAA;EA6GL;;;EApGL,KAAA,IAAS,OAAA;EAsHF;;;;;EAAA,UA3GJ,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;;;;EA4BpC,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;;;;EAyCjB,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;;;;EAoBpB,KAAA,IAAK,OAAA;;;;EAkBL,OAAA,IAAO,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-cache-driver.mjs","names":[],"sources":["../../../../../../../../packages/cache/src/drivers/file-cache-driver.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport {\n atomicWriteJsonAsync,\n ensureDirectoryAsync,\n getJsonFileAsync,\n listDirectoriesAsync,\n removeDirectoryAsync,\n} from \"@warlock.js/fs\";\nimport path from \"path\";\nimport type {\n CacheData,\n CacheDriver,\n CacheKey,\n CacheSetOptions,\n CacheSetResult,\n CacheTtl,\n FileCacheOptions,\n} from \"../types\";\nimport { CacheConfigurationError, CacheError, CacheUnsupportedError } from \"../types\";\nimport { BaseCacheDriver } from \"./base-cache-driver\";\n\nexport class FileCacheDriver\n extends BaseCacheDriver<FileCacheDriver, FileCacheOptions>\n implements CacheDriver<FileCacheDriver, FileCacheOptions>\n{\n /**\n * {@inheritdoc}\n */\n public name = \"file\";\n\n /**\n * {@inheritdoc}\n */\n public setOptions(options: FileCacheOptions) {\n if (!options.directory) {\n throw new CacheConfigurationError(\n \"File driver requires 'directory' option to be configured.\",\n );\n }\n\n return super.setOptions(options);\n }\n\n /**\n * Get the cache directory\n */\n public get directory() {\n const directory = this.options.directory;\n\n if (typeof directory === \"function\") {\n return directory();\n }\n\n throw new CacheConfigurationError(\n \"Cache directory is not defined, please define it in the file driver options\",\n );\n }\n\n /**\n * Get file name\n */\n public get fileName() {\n const fileName = this.options.fileName;\n\n if (typeof fileName === \"function\") {\n return fileName();\n }\n\n return \"cache.json\";\n }\n\n /**\n * Map a parsed cache key (or namespace) to its on-disk directory.\n *\n * The key must become exactly one directory component: `%` and the path\n * separators are percent-encoded so a hostile key (`../../etc`,\n * `..\\\\..\\\\evil`) turns into an inert directory name instead of a path\n * traversal, while distinct keys can never collide after encoding. Dots are\n * left untouched — the `.`-delimited namespace scheme is purely logical and\n * only ever produces a single filesystem component here.\n */\n protected keyDirectory(parsedKey: string): string {\n // An empty key would resolve to the cache root — `remove(\"{}\")` must never\n // be able to wipe it.\n if (parsedKey === \"\") {\n throw new CacheConfigurationError(\"Cache key must not be empty.\");\n }\n\n return this.containedPath(this.encodeKeySegment(parsedKey));\n }\n\n /**\n * Percent-encode a parsed key into an inert, single-component directory\n * name. Dots are left untouched — see {@link keyDirectory}.\n */\n protected encodeKeySegment(parsedKey: string): string {\n return parsedKey\n .replace(/%/g, \"%25\")\n .replace(/\\//g, \"%2F\")\n .replace(/\\\\/g, \"%5C\");\n }\n\n /**\n * Reverse of {@link encodeKeySegment} — recovers the logical, dot-delimited\n * parsed key from an on-disk directory name. Used by {@link removeNamespace}\n * to test which sibling directories logically belong to a namespace, since\n * dotted keys (`ns.a`) are stored as sibling directories rather than nested\n * ones (see {@link keyDirectory}'s doc comment).\n */\n protected decodeKeySegment(encoded: string): string {\n return encoded\n .replace(/%2F/g, \"/\")\n .replace(/%5C/g, \"\\\\\")\n .replace(/%25/g, \"%\");\n }\n\n /**\n * Resolve `segment` against the cache root and throw when the result lands\n * outside it — the last line of defense against path traversal, independent\n * of how the key was encoded.\n */\n protected containedPath(segment: string): string {\n const base = path.resolve(this.directory);\n const resolved = path.resolve(base, segment);\n const relative = path.relative(base, resolved);\n\n if (\n relative === \"..\" ||\n relative.startsWith(`..${path.sep}`) ||\n path.isAbsolute(relative)\n ) {\n throw new CacheError(\n `Cache key resolves outside the cache directory: \"${segment}\"`,\n );\n }\n\n return resolved;\n }\n\n /**\n * {@inheritdoc}\n *\n * Dotted keys (`ns.a`) are stored as sibling directories, not nested ones\n * (see {@link keyDirectory}) — a directory literally named `<namespace>`\n * rarely exists on its own. Namespace removal therefore has to scan the\n * cache root's immediate children and remove every directory whose\n * *logical* (decoded) name equals the namespace or starts with\n * `<namespace>.`, mirroring the `key = $1 OR key LIKE $2` boundary\n * semantics the `pg` driver already uses for the same contract.\n */\n public async removeNamespace(namespace: string) {\n const parsedNamespace = this.parseKey(namespace);\n\n this.log(\"clearing\", namespace);\n\n try {\n const root = this.containedPath(\"\");\n\n if (parsedNamespace === \"\") {\n await removeDirectoryAsync(root);\n this.log(\"cleared\", namespace);\n return this;\n }\n\n const prefix = `${parsedNamespace}.`;\n const entries = await listDirectoriesAsync(root).catch(() => [] as string[]);\n\n await Promise.all(\n entries.map(async (entryPath) => {\n const decoded = this.decodeKeySegment(path.basename(entryPath));\n\n if (decoded === parsedNamespace || decoded.startsWith(prefix)) {\n await removeDirectoryAsync(entryPath);\n }\n }),\n );\n\n this.log(\"cleared\", namespace);\n } catch (error) {\n //\n }\n\n return this;\n }\n\n /**\n * {@inheritdoc}\n *\n * `onConflict: \"create\"` is exclusive (temp file + hard link, so `lock()`\n * is safe across concurrent writers). `onConflict: \"update\"` is NOT atomic:\n * the existence probe and the write are separate steps.\n */\n public async set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n const parsedKey = this.parseKey(key);\n const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);\n\n if (vector) {\n throw new CacheUnsupportedError(\n \"'file' driver does not support similarity retrieval — use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).\",\n );\n }\n\n this.log(\"caching\", parsedKey);\n\n const existing = onConflict === \"upsert\" || onConflict === \"create\" ? null : await this.get(key);\n const exists = existing !== null;\n\n if (onConflict === \"update\" && !exists) {\n const result: CacheSetResult = { wasSet: false, existing: null };\n return result;\n }\n\n const data = this.prepareDataForStorage(value, ttl, staleAt);\n\n const fileDirectory = this.keyDirectory(parsedKey);\n\n await ensureDirectoryAsync(fileDirectory);\n\n const filePath = path.resolve(fileDirectory, this.fileName);\n\n if (onConflict === \"create\") {\n const conflict = await this.writeExclusive(filePath, data);\n\n if (conflict) {\n const result: CacheSetResult = { wasSet: false, existing: conflict.existing };\n return result;\n }\n } else {\n await atomicWriteJsonAsync(filePath, data);\n }\n\n if (tags && tags.length > 0) {\n await this.applyTags(key, tags);\n }\n\n this.log(\"cached\", parsedKey);\n\n await this.emit(\"set\", { key: parsedKey, value, ttl });\n\n if (onConflict === \"create\" || onConflict === \"update\") {\n const result: CacheSetResult = { wasSet: true, existing: null };\n return result;\n }\n\n return this;\n }\n\n /**\n * Create `filePath` only if it does not exist. Writes a temp file, then\n * hard-links it to the target (EEXIST = already present). An existing but\n * expired entry is removed (the file only, never the directory) and the\n * link retried once.\n *\n * Returns `null` when written, or `{ existing }` on conflict.\n */\n protected async writeExclusive(\n filePath: string,\n data: CacheData,\n ): Promise<{ existing: any } | null> {\n const tempPath = `${filePath}.${randomBytes(6).toString(\"hex\")}.tmp`;\n\n try {\n await fs.promises.writeFile(tempPath, JSON.stringify(data, null, 2));\n\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n await fs.promises.link(tempPath, filePath);\n return null;\n } catch (error: any) {\n if (error?.code !== \"EEXIST\") {\n throw error;\n }\n }\n\n let current: CacheData | undefined;\n\n try {\n current = (await getJsonFileAsync(filePath)) as CacheData | undefined;\n } catch {\n current = undefined;\n }\n\n const expired =\n current?.expiresAt !== undefined && current.expiresAt !== null && current.expiresAt <= Date.now();\n\n if (current && !expired) {\n return { existing: current.data ?? null };\n }\n\n if (attempt === 0 && expired) {\n await fs.promises.rm(filePath, { force: true });\n continue;\n }\n\n // Unreadable (corrupt) file: treat as held, never delete it.\n return { existing: null };\n }\n\n return { existing: null };\n } finally {\n await fs.promises.unlink(tempPath).catch(() => undefined);\n }\n }\n\n /**\n * {@inheritdoc}\n *\n * File driver does not yet ship with a file-lock primitive, so concurrent\n * writers could clobber each other. Rather than ship an unsafe default, we\n * throw — consumers can fall back to memory/redis for `update` until a\n * proper file lock lands (tracked in `domains/cache/backlog.md`).\n */\n public async update(): Promise<never> {\n throw new CacheUnsupportedError(\n \"`update()` is not supported on the file driver. Use the memory or redis driver, or wait for the file-lock primitive (see domains/cache/backlog.md).\",\n );\n }\n\n /**\n * {@inheritdoc}\n */\n public async merge(): Promise<never> {\n throw new CacheUnsupportedError(\n \"`merge()` is not supported on the file driver. Use the memory or redis driver.\",\n );\n }\n\n /**\n * Read the raw {@link CacheData} wrapper from disk, including `staleAt`\n * metadata. Returns `null` for missing or expired files — `swr()`\n * consumes this to branch on freshness.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const parsedKey = this.parseKey(key);\n const fileDirectory = this.keyDirectory(parsedKey);\n\n try {\n const entry = (await getJsonFileAsync(path.resolve(fileDirectory, this.fileName))) as\n | CacheData\n | undefined;\n\n if (!entry) {\n return null;\n }\n\n // `Infinity` (no expiry) is written to JSON as `null`; `null <= now`\n // is true, so only a real deadline may count as expired.\n if (typeof entry.expiresAt === \"number\" && entry.expiresAt <= Date.now()) {\n return null;\n }\n\n return entry;\n } catch {\n return null;\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async get(key: CacheKey) {\n const parsedKey = this.parseKey(key);\n\n this.log(\"fetching\", parsedKey);\n\n const fileDirectory = this.keyDirectory(parsedKey);\n\n let value: CacheData | undefined;\n\n try {\n value = (await getJsonFileAsync(path.resolve(fileDirectory, this.fileName))) as\n | CacheData\n | undefined;\n } catch {\n // Missing file (ENOENT) or corrupt JSON: a plain miss. Never remove the\n // directory here — it may be mid-write by another process.\n value = undefined;\n }\n\n if (value === undefined || value === null || typeof value !== \"object\") {\n this.log(\"notFound\", parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n return null;\n }\n\n // Only a successfully parsed but expired entry is removed (inside\n // parseCachedData).\n const result = await this.parseCachedData(parsedKey, value);\n\n if (result === null) {\n await this.emit(\"miss\", { key: parsedKey });\n } else {\n await this.emit(\"hit\", { key: parsedKey, value: result });\n }\n\n return result;\n }\n\n /**\n * {@inheritdoc}\n */\n public async remove(key: CacheKey) {\n const parsedKey = this.parseKey(key);\n this.log(\"removing\", parsedKey);\n\n const fileDirectory = this.keyDirectory(parsedKey);\n\n try {\n await removeDirectoryAsync(fileDirectory);\n\n this.log(\"removed\", parsedKey);\n // Emit removed event\n await this.emit(\"removed\", { key: parsedKey });\n } catch (error) {\n //\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async flush() {\n this.log(\"flushing\");\n\n if (this.options.globalPrefix) {\n await this.removeNamespace(\"\");\n } else {\n await removeDirectoryAsync(this.directory);\n }\n\n this.log(\"flushed\");\n\n // Emit flushed event\n await this.emit(\"flushed\");\n }\n\n /**\n * {@inheritdoc}\n */\n public async connect() {\n this.log(\"connecting\");\n await ensureDirectoryAsync(this.directory);\n this.log(\"connected\");\n await this.emit(\"connected\");\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-cache-driver.mjs","names":[],"sources":["../../../../../../../../packages/cache/src/drivers/file-cache-driver.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport {\n atomicWriteJsonAsync,\n ensureDirectoryAsync,\n getJsonFileAsync,\n listDirectoriesAsync,\n removeDirectoryAsync,\n} from \"@warlock.js/fs\";\nimport path from \"path\";\nimport type {\n CacheData,\n CacheDriver,\n CacheKey,\n CacheSetOptions,\n CacheSetResult,\n CacheTtl,\n FileCacheOptions,\n} from \"../types\";\nimport { CacheConfigurationError, CacheError, CacheUnsupportedError } from \"../types\";\nimport { BaseCacheDriver } from \"./base-cache-driver\";\n\nexport class FileCacheDriver\n extends BaseCacheDriver<FileCacheDriver, FileCacheOptions>\n implements CacheDriver<FileCacheDriver, FileCacheOptions>\n{\n /**\n * {@inheritdoc}\n */\n public name = \"file\";\n\n /**\n * {@inheritdoc}\n */\n public setOptions(options: FileCacheOptions) {\n if (!options.directory) {\n throw new CacheConfigurationError(\n \"File driver requires 'directory' option to be configured.\",\n );\n }\n\n return super.setOptions(options);\n }\n\n /**\n * Get the cache directory\n */\n public get directory() {\n const directory = this.options.directory;\n\n if (typeof directory === \"function\") {\n return directory();\n }\n\n throw new CacheConfigurationError(\n \"Cache directory is not defined, please define it in the file driver options\",\n );\n }\n\n /**\n * Get file name\n */\n public get fileName() {\n const fileName = this.options.fileName;\n\n if (typeof fileName === \"function\") {\n return fileName();\n }\n\n return \"cache.json\";\n }\n\n /**\n * Map a parsed cache key (or namespace) to its on-disk directory.\n *\n * The key must become exactly one directory component: `%` and the path\n * separators are percent-encoded so a hostile key (`../../etc`,\n * `..\\\\..\\\\evil`) turns into an inert directory name instead of a path\n * traversal, while distinct keys can never collide after encoding. Dots are\n * left untouched — the `.`-delimited namespace scheme is purely logical and\n * only ever produces a single filesystem component here.\n */\n protected keyDirectory(parsedKey: string): string {\n // An empty key would resolve to the cache root — `remove(\"{}\")` must never\n // be able to wipe it.\n if (parsedKey === \"\") {\n throw new CacheConfigurationError(\"Cache key must not be empty.\");\n }\n\n return this.containedPath(this.encodeKeySegment(parsedKey));\n }\n\n /**\n * Percent-encode a parsed key into an inert, single-component directory\n * name. Dots are left untouched — see {@link keyDirectory}.\n */\n protected encodeKeySegment(parsedKey: string): string {\n return parsedKey.replace(/%/g, \"%25\").replace(/\\//g, \"%2F\").replace(/\\\\/g, \"%5C\");\n }\n\n /**\n * Reverse of {@link encodeKeySegment} — recovers the logical, dot-delimited\n * parsed key from an on-disk directory name. Used by {@link removeNamespace}\n * to test which sibling directories logically belong to a namespace, since\n * dotted keys (`ns.a`) are stored as sibling directories rather than nested\n * ones (see {@link keyDirectory}'s doc comment).\n */\n protected decodeKeySegment(encoded: string): string {\n return encoded.replace(/%2F/g, \"/\").replace(/%5C/g, \"\\\\\").replace(/%25/g, \"%\");\n }\n\n /**\n * Resolve `segment` against the cache root and throw when the result lands\n * outside it — the last line of defense against path traversal, independent\n * of how the key was encoded.\n */\n protected containedPath(segment: string): string {\n const base = path.resolve(this.directory);\n const resolved = path.resolve(base, segment);\n const relative = path.relative(base, resolved);\n\n if (relative === \"..\" || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {\n throw new CacheError(`Cache key resolves outside the cache directory: \"${segment}\"`);\n }\n\n return resolved;\n }\n\n /**\n * {@inheritdoc}\n *\n * Dotted keys (`ns.a`) are stored as sibling directories, not nested ones\n * (see {@link keyDirectory}) — a directory literally named `<namespace>`\n * rarely exists on its own. Namespace removal therefore has to scan the\n * cache root's immediate children and remove every directory whose\n * *logical* (decoded) name equals the namespace or starts with\n * `<namespace>.`, mirroring the `key = $1 OR key LIKE $2` boundary\n * semantics the `pg` driver already uses for the same contract.\n */\n public async removeNamespace(namespace: string) {\n const parsedNamespace = this.parseKey(namespace);\n\n this.log(\"clearing\", namespace);\n\n try {\n const root = this.containedPath(\"\");\n\n if (parsedNamespace === \"\") {\n await removeDirectoryAsync(root);\n this.log(\"cleared\", namespace);\n return this;\n }\n\n const prefix = `${parsedNamespace}.`;\n const entries = await listDirectoriesAsync(root).catch(() => [] as string[]);\n\n await Promise.all(\n entries.map(async (entryPath) => {\n const decoded = this.decodeKeySegment(path.basename(entryPath));\n\n if (decoded === parsedNamespace || decoded.startsWith(prefix)) {\n await removeDirectoryAsync(entryPath);\n }\n }),\n );\n\n this.log(\"cleared\", namespace);\n } catch (error) {\n //\n }\n\n return this;\n }\n\n /**\n * {@inheritdoc}\n *\n * `onConflict: \"create\"` is exclusive (temp file + hard link, so `lock()`\n * is safe across concurrent writers). `onConflict: \"update\"` is NOT atomic:\n * the existence probe and the write are separate steps.\n */\n public async set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n const parsedKey = this.parseKey(key);\n const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);\n\n if (vector) {\n throw new CacheUnsupportedError(\n \"'file' driver does not support similarity retrieval — use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).\",\n );\n }\n\n this.log(\"caching\", parsedKey);\n\n const existing =\n onConflict === \"upsert\" || onConflict === \"create\" ? null : await this.get(key);\n const exists = existing !== null;\n\n if (onConflict === \"update\" && !exists) {\n const result: CacheSetResult = { wasSet: false, existing: null };\n return result;\n }\n\n const data = this.prepareDataForStorage(value, ttl, staleAt);\n\n const fileDirectory = this.keyDirectory(parsedKey);\n\n await ensureDirectoryAsync(fileDirectory);\n\n const filePath = path.resolve(fileDirectory, this.fileName);\n\n if (onConflict === \"create\") {\n const conflict = await this.writeExclusive(filePath, data);\n\n if (conflict) {\n const result: CacheSetResult = { wasSet: false, existing: conflict.existing };\n return result;\n }\n } else {\n await atomicWriteJsonAsync(filePath, data);\n }\n\n if (tags && tags.length > 0) {\n await this.applyTags(key, tags);\n }\n\n this.log(\"cached\", parsedKey);\n\n await this.emit(\"set\", { key: parsedKey, value, ttl });\n\n if (onConflict === \"create\" || onConflict === \"update\") {\n const result: CacheSetResult = { wasSet: true, existing: null };\n return result;\n }\n\n return this;\n }\n\n /**\n * Create `filePath` only if it does not exist. Writes a temp file, then\n * hard-links it to the target (EEXIST = already present). An existing but\n * expired entry is removed (the file only, never the directory) and the\n * link retried once.\n *\n * Returns `null` when written, or `{ existing }` on conflict.\n */\n protected async writeExclusive(\n filePath: string,\n data: CacheData,\n ): Promise<{ existing: any } | null> {\n const tempPath = `${filePath}.${randomBytes(6).toString(\"hex\")}.tmp`;\n\n try {\n await fs.promises.writeFile(tempPath, JSON.stringify(data, null, 2));\n\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n await fs.promises.link(tempPath, filePath);\n return null;\n } catch (error: any) {\n if (error?.code !== \"EEXIST\") {\n throw error;\n }\n }\n\n let current: CacheData | undefined;\n\n try {\n current = (await getJsonFileAsync(filePath)) as CacheData | undefined;\n } catch {\n current = undefined;\n }\n\n const expired =\n current?.expiresAt !== undefined &&\n current.expiresAt !== null &&\n current.expiresAt <= Date.now();\n\n if (current && !expired) {\n return { existing: current.data ?? null };\n }\n\n if (attempt === 0 && expired) {\n await fs.promises.rm(filePath, { force: true });\n continue;\n }\n\n // Unreadable (corrupt) file: treat as held, never delete it.\n return { existing: null };\n }\n\n return { existing: null };\n } finally {\n await fs.promises.unlink(tempPath).catch(() => undefined);\n }\n }\n\n /**\n * {@inheritdoc}\n *\n * File driver does not yet ship with a file-lock primitive, so concurrent\n * writers could clobber each other. Rather than ship an unsafe default, we\n * throw — consumers can fall back to memory/redis for `update` until a\n * proper file lock lands (tracked in `domains/cache/backlog.md`).\n */\n public async update(): Promise<never> {\n throw new CacheUnsupportedError(\n \"`update()` is not supported on the file driver. Use the memory or redis driver, or wait for the file-lock primitive (see domains/cache/backlog.md).\",\n );\n }\n\n /**\n * {@inheritdoc}\n */\n public async merge(): Promise<never> {\n throw new CacheUnsupportedError(\n \"`merge()` is not supported on the file driver. Use the memory or redis driver.\",\n );\n }\n\n /**\n * Read the raw {@link CacheData} wrapper from disk, including `staleAt`\n * metadata. Returns `null` for missing or expired files — `swr()`\n * consumes this to branch on freshness.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const parsedKey = this.parseKey(key);\n const fileDirectory = this.keyDirectory(parsedKey);\n\n try {\n const entry = (await getJsonFileAsync(path.resolve(fileDirectory, this.fileName))) as\n | CacheData\n | undefined;\n\n if (!entry) {\n return null;\n }\n\n // `Infinity` (no expiry) is written to JSON as `null`; `null <= now`\n // is true, so only a real deadline may count as expired.\n if (typeof entry.expiresAt === \"number\" && entry.expiresAt <= Date.now()) {\n return null;\n }\n\n return entry;\n } catch {\n return null;\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async get(key: CacheKey) {\n const parsedKey = this.parseKey(key);\n\n this.log(\"fetching\", parsedKey);\n\n const fileDirectory = this.keyDirectory(parsedKey);\n\n let value: CacheData | undefined;\n\n try {\n value = (await getJsonFileAsync(path.resolve(fileDirectory, this.fileName))) as\n | CacheData\n | undefined;\n } catch {\n // Missing file (ENOENT) or corrupt JSON: a plain miss. Never remove the\n // directory here — it may be mid-write by another process.\n value = undefined;\n }\n\n if (value === undefined || value === null || typeof value !== \"object\") {\n this.log(\"notFound\", parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n return null;\n }\n\n // Only a successfully parsed but expired entry is removed (inside\n // parseCachedData).\n const result = await this.parseCachedData(parsedKey, value);\n\n if (result === null) {\n await this.emit(\"miss\", { key: parsedKey });\n } else {\n await this.emit(\"hit\", { key: parsedKey, value: result });\n }\n\n return result;\n }\n\n /**\n * {@inheritdoc}\n */\n public async remove(key: CacheKey) {\n const parsedKey = this.parseKey(key);\n this.log(\"removing\", parsedKey);\n\n const fileDirectory = this.keyDirectory(parsedKey);\n\n try {\n await removeDirectoryAsync(fileDirectory);\n\n this.log(\"removed\", parsedKey);\n // Emit removed event\n await this.emit(\"removed\", { key: parsedKey });\n } catch (error) {\n //\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async flush() {\n this.log(\"flushing\");\n\n if (this.options.globalPrefix) {\n await this.removeNamespace(\"\");\n } else {\n await removeDirectoryAsync(this.directory);\n }\n\n this.log(\"flushed\");\n\n // Emit flushed event\n await this.emit(\"flushed\");\n }\n\n /**\n * {@inheritdoc}\n */\n public async connect() {\n this.log(\"connecting\");\n await ensureDirectoryAsync(this.directory);\n this.log(\"connected\");\n await this.emit(\"connected\");\n }\n}\n"],"mappings":"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"}