@warlock.js/cache 5.19.1 → 5.21.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 (102) hide show
  1. package/CHANGELOG.md +61 -1
  2. package/cjs/index.cjs +1439 -331
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/cache-manager.d.mts +4 -0
  5. package/esm/cache-manager.d.mts.map +1 -1
  6. package/esm/cache-manager.mjs +23 -5
  7. package/esm/cache-manager.mjs.map +1 -1
  8. package/esm/cached/auto-key.d.mts +14 -14
  9. package/esm/cached/auto-key.d.mts.map +1 -1
  10. package/esm/cached/auto-key.mjs +50 -34
  11. package/esm/cached/auto-key.mjs.map +1 -1
  12. package/esm/cached/cached.mjs +4 -0
  13. package/esm/cached/cached.mjs.map +1 -1
  14. package/esm/drivers/base-cache-driver.d.mts +84 -8
  15. package/esm/drivers/base-cache-driver.d.mts.map +1 -1
  16. package/esm/drivers/base-cache-driver.mjs +152 -42
  17. package/esm/drivers/base-cache-driver.mjs.map +1 -1
  18. package/esm/drivers/file-cache-driver.d.mts +15 -0
  19. package/esm/drivers/file-cache-driver.d.mts.map +1 -1
  20. package/esm/drivers/file-cache-driver.mjs +68 -18
  21. package/esm/drivers/file-cache-driver.mjs.map +1 -1
  22. package/esm/drivers/in-memory-tag-index.d.mts +44 -0
  23. package/esm/drivers/in-memory-tag-index.d.mts.map +1 -0
  24. package/esm/drivers/in-memory-tag-index.mjs +73 -0
  25. package/esm/drivers/in-memory-tag-index.mjs.map +1 -0
  26. package/esm/drivers/index.d.mts +1 -1
  27. package/esm/drivers/index.mjs +1 -1
  28. package/esm/drivers/lru-memory-cache-driver.d.mts +43 -0
  29. package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -1
  30. package/esm/drivers/lru-memory-cache-driver.mjs +96 -0
  31. package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -1
  32. package/esm/drivers/memory-cache-driver.d.mts +87 -30
  33. package/esm/drivers/memory-cache-driver.d.mts.map +1 -1
  34. package/esm/drivers/memory-cache-driver.mjs +214 -96
  35. package/esm/drivers/memory-cache-driver.mjs.map +1 -1
  36. package/esm/drivers/memory-extended-cache-driver.d.mts +9 -5
  37. package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -1
  38. package/esm/drivers/memory-extended-cache-driver.mjs +10 -23
  39. package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -1
  40. package/esm/drivers/mock-cache-driver.d.mts +41 -0
  41. package/esm/drivers/mock-cache-driver.d.mts.map +1 -1
  42. package/esm/drivers/mock-cache-driver.mjs +105 -8
  43. package/esm/drivers/mock-cache-driver.mjs.map +1 -1
  44. package/esm/drivers/null-cache-driver.d.mts +8 -2
  45. package/esm/drivers/null-cache-driver.d.mts.map +1 -1
  46. package/esm/drivers/null-cache-driver.mjs +10 -2
  47. package/esm/drivers/null-cache-driver.mjs.map +1 -1
  48. package/esm/drivers/pg-cache-driver.d.mts +77 -3
  49. package/esm/drivers/pg-cache-driver.d.mts.map +1 -1
  50. package/esm/drivers/pg-cache-driver.mjs +229 -7
  51. package/esm/drivers/pg-cache-driver.mjs.map +1 -1
  52. package/esm/drivers/redis-cache-driver.d.mts +86 -1
  53. package/esm/drivers/redis-cache-driver.d.mts.map +1 -1
  54. package/esm/drivers/redis-cache-driver.mjs +252 -16
  55. package/esm/drivers/redis-cache-driver.mjs.map +1 -1
  56. package/esm/index.d.mts +2 -2
  57. package/esm/index.mjs +2 -2
  58. package/esm/list/memory-cache-list.d.mts +11 -8
  59. package/esm/list/memory-cache-list.d.mts.map +1 -1
  60. package/esm/list/memory-cache-list.mjs +39 -37
  61. package/esm/list/memory-cache-list.mjs.map +1 -1
  62. package/esm/scoped-cache.d.mts +9 -3
  63. package/esm/scoped-cache.d.mts.map +1 -1
  64. package/esm/scoped-cache.mjs +19 -7
  65. package/esm/scoped-cache.mjs.map +1 -1
  66. package/esm/tagged-cache.d.mts +20 -0
  67. package/esm/tagged-cache.d.mts.map +1 -1
  68. package/esm/tagged-cache.mjs +62 -24
  69. package/esm/tagged-cache.mjs.map +1 -1
  70. package/esm/tagged-scoped-cache.d.mts +7 -3
  71. package/esm/tagged-scoped-cache.d.mts.map +1 -1
  72. package/esm/tagged-scoped-cache.mjs +19 -7
  73. package/esm/tagged-scoped-cache.mjs.map +1 -1
  74. package/esm/types.d.mts +41 -3
  75. package/esm/types.d.mts.map +1 -1
  76. package/esm/types.mjs.map +1 -1
  77. package/esm/utils.d.mts +9 -3
  78. package/esm/utils.d.mts.map +1 -1
  79. package/esm/utils.mjs +16 -6
  80. package/esm/utils.mjs.map +1 -1
  81. package/llms-full.txt +1685 -1666
  82. package/llms.txt +18 -18
  83. package/package.json +4 -4
  84. package/skills/apply-cache-patterns/SKILL.md +98 -97
  85. package/skills/configure-pg-cache/SKILL.md +6 -5
  86. package/skills/configure-set-options/SKILL.md +102 -96
  87. package/skills/handle-cache-errors/SKILL.md +92 -92
  88. package/skills/index.md +20 -0
  89. package/skills/observe-cache/SKILL.md +103 -103
  90. package/skills/pick-cache-driver/SKILL.md +129 -123
  91. package/skills/test-cache-code/SKILL.md +219 -219
  92. package/skills/use-cache-atomic/SKILL.md +57 -65
  93. package/skills/use-cache-bulk/SKILL.md +57 -57
  94. package/skills/use-cache-list/SKILL.md +85 -85
  95. package/skills/use-cache-lock/SKILL.md +113 -104
  96. package/skills/use-cache-namespace/SKILL.md +88 -88
  97. package/skills/use-cache-similarity/SKILL.md +94 -94
  98. package/skills/use-cache-tags/SKILL.md +7 -4
  99. package/skills/use-cache-update-merge/SKILL.md +84 -84
  100. package/skills/use-cache-utils/SKILL.md +89 -89
  101. package/skills/use-cached-hof/SKILL.md +100 -99
  102. package/skills/use-swr/SKILL.md +104 -104
@@ -59,17 +59,29 @@ var TaggedScopedCache = class {
59
59
  return this.scope.has(key);
60
60
  }
61
61
  /**
62
- * Remove the scoped key. The tag-index entry will eventually be cleaned up
63
- * by `invalidate()`; we don't proactively rewrite it here for cost reasons.
62
+ * Remove the scoped key and take it out of every tag index this handle
63
+ * writes to (scope tags + handle tags).
64
64
  */
65
- remove(key) {
66
- return this.scope.remove(key);
65
+ async remove(key) {
66
+ await this.scope.remove(key);
67
+ await this.detachFromTags(key);
67
68
  }
68
69
  /**
69
- * Read-and-remove the scoped key.
70
+ * Read-and-remove the scoped key, then take it out of this handle's tags.
70
71
  */
71
- pull(key) {
72
- return this.scope.pull(key);
72
+ async pull(key) {
73
+ const value = await this.scope.pull(key);
74
+ if (value !== null) await this.detachFromTags(key);
75
+ return value;
76
+ }
77
+ /**
78
+ * Remove the un-prefixed scoped key from every tag index of this handle.
79
+ */
80
+ async detachFromTags(key) {
81
+ const allTags = mergeTagSets(this.scope.defaults.tags, this.handleTags);
82
+ if (!allTags || allTags.length === 0) return;
83
+ const scopedKey = this.buildScopedKey(key);
84
+ await this.scope.source.tags(allTags).detachFromTags(parseCacheKey(scopedKey));
73
85
  }
74
86
  /**
75
87
  * Permanent write with handle tags applied. Bypasses both the scope's and
@@ -1 +1 @@
1
- {"version":3,"file":"tagged-scoped-cache.mjs","names":[],"sources":["../../../../../../cache/src/tagged-scoped-cache.ts"],"sourcesContent":["import type { ScopedCache } from \"./scoped-cache\";\nimport type {\n CacheKey,\n CacheSetOptions,\n CacheTtl,\n RememberOptions,\n TaggedScopedCacheContract,\n} from \"./types\";\nimport {\n injectTags,\n mergeTagSets,\n normalizeToOptions,\n normalizeToRememberOptions,\n parseCacheKey,\n} from \"./utils\";\n\n/**\n * One-shot tagged write handle on top of a {@link ScopedCache}.\n *\n * **Role.** Returned by `scope.tags([...])`. Adds a fixed list of tags to\n * every write produced through this handle, on top of any tags the parent\n * scope already contributes. Stateless except for the captured tag list.\n *\n * **Responsibility.**\n * - Owns: appending the handle's tags to writes, delegating tag-index\n * bookkeeping for `setNX` (which lacks an inline `tags` knob on the driver\n * contract), and computing the union for `invalidate()` calls.\n * - Does NOT own: storage, prefix-prepending (delegated to the scope),\n * default `ttl` (delegated to the scope), or any kind of long-lived state.\n *\n * Tags compose additively: scope tags + handle tags + per-call tags, all\n * unioned and deduped. The handle never replaces scope tags — `invalidate()`\n * always sees the full union.\n *\n * @example\n * // Inside application code — scope provides the per-user tag automatically:\n * const feed = cache.namespace(`feed.${userId}`, { tags: [`user.${userId}`] });\n *\n * await feed.tags([\"unread\"]).set(\"messages.1\", message);\n * // → tagged with [user.<id>, unread]\n *\n * await feed.tags([\"unread\"]).invalidate();\n * // → wipes everything tagged with user.<id> OR unread.\n */\nexport class TaggedScopedCache implements TaggedScopedCacheContract {\n /**\n * The {@link ScopedCache} this handle delegates to. Held by reference so\n * scope-default changes (none today, but the option is preserved for the\n * future) are visible without rebuilding the handle.\n */\n protected readonly scope: ScopedCache;\n\n /**\n * Tags this handle contributes to every write. Cloned at the call site so\n * later mutation of the input array doesn't leak in.\n */\n protected readonly handleTags: string[];\n\n /**\n * Build a tagged handle. Constructed via `scope.tags([...])` — users never\n * call this directly.\n */\n public constructor(scope: ScopedCache, handleTags: string[]) {\n this.scope = scope;\n this.handleTags = [...handleTags];\n }\n\n /**\n * Write the scoped key with the handle's tags appended to whatever the\n * caller passed. Scope-level tags are added on top by the scope itself.\n */\n public set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n const options = injectTags(normalizeToOptions(ttlOrOptions), this.handleTags);\n\n return this.scope.set(key, value, options);\n }\n\n /**\n * Read the scoped key. Tags don't affect reads — pass-through.\n */\n public get<T = any>(key: CacheKey): Promise<T | null> {\n return this.scope.get<T>(key);\n }\n\n /**\n * Check presence of the scoped key.\n */\n public has(key: CacheKey): Promise<boolean> {\n return this.scope.has(key);\n }\n\n /**\n * Remove the scoped key. The tag-index entry will eventually be cleaned up\n * by `invalidate()`; we don't proactively rewrite it here for cost reasons.\n */\n public remove(key: CacheKey): Promise<void> {\n return this.scope.remove(key);\n }\n\n /**\n * Read-and-remove the scoped key.\n */\n public pull<T = any>(key: CacheKey): Promise<T | null> {\n return this.scope.pull<T>(key);\n }\n\n /**\n * Permanent write with handle tags applied. Bypasses both the scope's and\n * the caller's TTL (forever means forever) — only tags get injected.\n */\n public forever<T = any>(key: CacheKey, value: T): Promise<T> {\n return this.scope.set(key, value, {\n ttl: Infinity,\n tags: this.handleTags,\n }) as Promise<T>;\n }\n\n /**\n * Atomic create-or-skip with the handle's tags applied on success. The\n * driver contract has no inline `tags` knob on `setNX`, so we register the\n * tag relationship manually after a successful write.\n */\n public async setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\n const wasSet = await this.scope.setNX(key, value, ttl);\n\n if (!wasSet) {\n return false;\n }\n\n const allTags = mergeTagSets(this.scope.defaults.tags, this.handleTags);\n\n if (!allTags || allTags.length === 0) {\n return true;\n }\n\n // Register the un-prefixed scoped key — the tag index never holds the\n // driver-parsed form, so `invalidate()` can remove it via `remove(key)`.\n const scopedKey = this.buildScopedKey(key);\n const tagged = this.scope.source.tags(allTags) as unknown as {\n storeTagRelationship: (key: CacheKey) => Promise<void>;\n };\n\n await tagged.storeTagRelationship(scopedKey);\n\n return true;\n }\n\n /**\n * Read-or-compute with handle tags appended on the cache-miss write.\n */\n public remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n const options = injectTags(\n normalizeToRememberOptions(ttlOrOptions),\n this.handleTags,\n );\n\n return this.scope.remember<T>(key, options, callback);\n }\n\n /**\n * Atomic counter increment on the scoped key. Tags aren't applied to\n * subsequent increments — they're attached at first-write time.\n */\n public increment(key: CacheKey, value?: number): Promise<number> {\n return this.scope.increment(key, value);\n }\n\n /**\n * Atomic counter decrement on the scoped key. See {@link increment}.\n */\n public decrement(key: CacheKey, value?: number): Promise<number> {\n return this.scope.decrement(key, value);\n }\n\n /**\n * Wipe every entry tagged with the union of (scope tags + handle tags).\n * Tags are global across the package, so this reaches outside the scope's\n * prefix when scope tags are also used elsewhere.\n */\n public async invalidate(): Promise<void> {\n const allTags = mergeTagSets(this.scope.defaults.tags, this.handleTags);\n\n if (!allTags || allTags.length === 0) {\n return;\n }\n\n await this.scope.source.tags(allTags).invalidate();\n }\n\n /**\n * Compute the source-side key the same way `ScopedCache.scopedKey` does —\n * needed for `setNX`, where we have to register the tag relationship by\n * hand because the driver contract doesn't accept inline tags there.\n */\n protected buildScopedKey(key: CacheKey): string {\n const keyString = typeof key === \"string\" ? key : parseCacheKey(key);\n\n if (!keyString) {\n return this.scope.prefix;\n }\n\n return `${this.scope.prefix}.${keyString}`;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,IAAa,oBAAb,MAAoE;;;;;CAkBlE,AAAO,YAAY,OAAoB,YAAsB;EAC3D,KAAK,QAAQ;EACb,KAAK,aAAa,CAAC,GAAG,UAAU;CAClC;;;;;CAMA,AAAO,IACL,KACA,OACA,cACc;EACd,MAAM,UAAU,WAAW,mBAAmB,YAAY,GAAG,KAAK,UAAU;EAE5E,OAAO,KAAK,MAAM,IAAI,KAAK,OAAO,OAAO;CAC3C;;;;CAKA,AAAO,IAAa,KAAkC;EACpD,OAAO,KAAK,MAAM,IAAO,GAAG;CAC9B;;;;CAKA,AAAO,IAAI,KAAiC;EAC1C,OAAO,KAAK,MAAM,IAAI,GAAG;CAC3B;;;;;CAMA,AAAO,OAAO,KAA8B;EAC1C,OAAO,KAAK,MAAM,OAAO,GAAG;CAC9B;;;;CAKA,AAAO,KAAc,KAAkC;EACrD,OAAO,KAAK,MAAM,KAAQ,GAAG;CAC/B;;;;;CAMA,AAAO,QAAiB,KAAe,OAAsB;EAC3D,OAAO,KAAK,MAAM,IAAI,KAAK,OAAO;GAChC,KAAK;GACL,MAAM,KAAK;EACb,CAAC;CACH;;;;;;CAOA,MAAa,MAAM,KAAe,OAAY,KAAgC;EAG5E,IAAI,CAAC,MAFgB,KAAK,MAAM,MAAM,KAAK,OAAO,GAAG,GAGnD,OAAO;EAGT,MAAM,UAAU,aAAa,KAAK,MAAM,SAAS,MAAM,KAAK,UAAU;EAEtE,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO;EAKT,MAAM,YAAY,KAAK,eAAe,GAAG;EAKzC,MAJe,KAAK,MAAM,OAAO,KAAK,OAI3B,CAAC,CAAC,qBAAqB,SAAS;EAE3C,OAAO;CACT;;;;CAKA,AAAO,SACL,KACA,cACA,UACY;EACZ,MAAM,UAAU,WACd,2BAA2B,YAAY,GACvC,KAAK,UACP;EAEA,OAAO,KAAK,MAAM,SAAY,KAAK,SAAS,QAAQ;CACtD;;;;;CAMA,AAAO,UAAU,KAAe,OAAiC;EAC/D,OAAO,KAAK,MAAM,UAAU,KAAK,KAAK;CACxC;;;;CAKA,AAAO,UAAU,KAAe,OAAiC;EAC/D,OAAO,KAAK,MAAM,UAAU,KAAK,KAAK;CACxC;;;;;;CAOA,MAAa,aAA4B;EACvC,MAAM,UAAU,aAAa,KAAK,MAAM,SAAS,MAAM,KAAK,UAAU;EAEtE,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC;EAGF,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW;CACnD;;;;;;CAOA,AAAU,eAAe,KAAuB;EAC9C,MAAM,YAAY,OAAO,QAAQ,WAAW,MAAM,cAAc,GAAG;EAEnE,IAAI,CAAC,WACH,OAAO,KAAK,MAAM;EAGpB,OAAO,GAAG,KAAK,MAAM,OAAO,GAAG;CACjC;AACF"}
1
+ {"version":3,"file":"tagged-scoped-cache.mjs","names":[],"sources":["../../../../../../cache/src/tagged-scoped-cache.ts"],"sourcesContent":["import type { ScopedCache } from \"./scoped-cache\";\nimport type {\n CacheKey,\n CacheSetOptions,\n CacheTtl,\n RememberOptions,\n TaggedScopedCacheContract,\n} from \"./types\";\nimport {\n injectTags,\n mergeTagSets,\n normalizeToOptions,\n normalizeToRememberOptions,\n parseCacheKey,\n} from \"./utils\";\n\n/**\n * One-shot tagged write handle on top of a {@link ScopedCache}.\n *\n * **Role.** Returned by `scope.tags([...])`. Adds a fixed list of tags to\n * every write produced through this handle, on top of any tags the parent\n * scope already contributes. Stateless except for the captured tag list.\n *\n * **Responsibility.**\n * - Owns: appending the handle's tags to writes, delegating tag-index\n * bookkeeping for `setNX` (which lacks an inline `tags` knob on the driver\n * contract), and computing the union for `invalidate()` calls.\n * - Does NOT own: storage, prefix-prepending (delegated to the scope),\n * default `ttl` (delegated to the scope), or any kind of long-lived state.\n *\n * Tags compose additively: scope tags + handle tags + per-call tags, all\n * unioned and deduped. The handle never replaces scope tags — `invalidate()`\n * always sees the full union.\n *\n * @example\n * // Inside application code — scope provides the per-user tag automatically:\n * const feed = cache.namespace(`feed.${userId}`, { tags: [`user.${userId}`] });\n *\n * await feed.tags([\"unread\"]).set(\"messages.1\", message);\n * // → tagged with [user.<id>, unread]\n *\n * await feed.tags([\"unread\"]).invalidate();\n * // → wipes everything tagged with user.<id> OR unread.\n */\nexport class TaggedScopedCache implements TaggedScopedCacheContract {\n /**\n * The {@link ScopedCache} this handle delegates to. Held by reference so\n * scope-default changes (none today, but the option is preserved for the\n * future) are visible without rebuilding the handle.\n */\n protected readonly scope: ScopedCache;\n\n /**\n * Tags this handle contributes to every write. Cloned at the call site so\n * later mutation of the input array doesn't leak in.\n */\n protected readonly handleTags: string[];\n\n /**\n * Build a tagged handle. Constructed via `scope.tags([...])` — users never\n * call this directly.\n */\n public constructor(scope: ScopedCache, handleTags: string[]) {\n this.scope = scope;\n this.handleTags = [...handleTags];\n }\n\n /**\n * Write the scoped key with the handle's tags appended to whatever the\n * caller passed. Scope-level tags are added on top by the scope itself.\n */\n public set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n const options = injectTags(normalizeToOptions(ttlOrOptions), this.handleTags);\n\n return this.scope.set(key, value, options);\n }\n\n /**\n * Read the scoped key. Tags don't affect reads — pass-through.\n */\n public get<T = any>(key: CacheKey): Promise<T | null> {\n return this.scope.get<T>(key);\n }\n\n /**\n * Check presence of the scoped key.\n */\n public has(key: CacheKey): Promise<boolean> {\n return this.scope.has(key);\n }\n\n /**\n * Remove the scoped key and take it out of every tag index this handle\n * writes to (scope tags + handle tags).\n */\n public async remove(key: CacheKey): Promise<void> {\n await this.scope.remove(key);\n await this.detachFromTags(key);\n }\n\n /**\n * Read-and-remove the scoped key, then take it out of this handle's tags.\n */\n public async pull<T = any>(key: CacheKey): Promise<T | null> {\n const value = await this.scope.pull<T>(key);\n\n if (value !== null) {\n await this.detachFromTags(key);\n }\n\n return value;\n }\n\n /**\n * Remove the un-prefixed scoped key from every tag index of this handle.\n */\n protected async detachFromTags(key: CacheKey): Promise<void> {\n const allTags = mergeTagSets(this.scope.defaults.tags, this.handleTags);\n\n if (!allTags || allTags.length === 0) {\n return;\n }\n\n const scopedKey = this.buildScopedKey(key);\n const tagged = this.scope.source.tags(allTags) as unknown as {\n detachFromTags: (indexedKey: string) => Promise<void>;\n };\n\n await tagged.detachFromTags(parseCacheKey(scopedKey));\n }\n\n /**\n * Permanent write with handle tags applied. Bypasses both the scope's and\n * the caller's TTL (forever means forever) — only tags get injected.\n */\n public forever<T = any>(key: CacheKey, value: T): Promise<T> {\n return this.scope.set(key, value, {\n ttl: Infinity,\n tags: this.handleTags,\n }) as Promise<T>;\n }\n\n /**\n * Atomic create-or-skip with the handle's tags applied on success. The\n * driver contract has no inline `tags` knob on `setNX`, so we register the\n * tag relationship manually after a successful write.\n */\n public async setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean> {\n const wasSet = await this.scope.setNX(key, value, ttl);\n\n if (!wasSet) {\n return false;\n }\n\n const allTags = mergeTagSets(this.scope.defaults.tags, this.handleTags);\n\n if (!allTags || allTags.length === 0) {\n return true;\n }\n\n // Register the un-prefixed scoped key — the tag index never holds the\n // driver-parsed form, so `invalidate()` can remove it via `remove(key)`.\n const scopedKey = this.buildScopedKey(key);\n const tagged = this.scope.source.tags(allTags) as unknown as {\n storeTagRelationship: (key: CacheKey) => Promise<void>;\n };\n\n await tagged.storeTagRelationship(scopedKey);\n\n return true;\n }\n\n /**\n * Read-or-compute with handle tags appended on the cache-miss write.\n */\n public remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T> {\n const options = injectTags(\n normalizeToRememberOptions(ttlOrOptions),\n this.handleTags,\n );\n\n return this.scope.remember<T>(key, options, callback);\n }\n\n /**\n * Atomic counter increment on the scoped key. Tags aren't applied to\n * subsequent increments — they're attached at first-write time.\n */\n public increment(key: CacheKey, value?: number): Promise<number> {\n return this.scope.increment(key, value);\n }\n\n /**\n * Atomic counter decrement on the scoped key. See {@link increment}.\n */\n public decrement(key: CacheKey, value?: number): Promise<number> {\n return this.scope.decrement(key, value);\n }\n\n /**\n * Wipe every entry tagged with the union of (scope tags + handle tags).\n * Tags are global across the package, so this reaches outside the scope's\n * prefix when scope tags are also used elsewhere.\n */\n public async invalidate(): Promise<void> {\n const allTags = mergeTagSets(this.scope.defaults.tags, this.handleTags);\n\n if (!allTags || allTags.length === 0) {\n return;\n }\n\n await this.scope.source.tags(allTags).invalidate();\n }\n\n /**\n * Compute the source-side key the same way `ScopedCache.scopedKey` does —\n * needed for `setNX`, where we have to register the tag relationship by\n * hand because the driver contract doesn't accept inline tags there.\n */\n protected buildScopedKey(key: CacheKey): string {\n const keyString = typeof key === \"string\" ? key : parseCacheKey(key);\n\n if (!keyString) {\n return this.scope.prefix;\n }\n\n return `${this.scope.prefix}.${keyString}`;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,IAAa,oBAAb,MAAoE;;;;;CAkBlE,AAAO,YAAY,OAAoB,YAAsB;EAC3D,KAAK,QAAQ;EACb,KAAK,aAAa,CAAC,GAAG,UAAU;CAClC;;;;;CAMA,AAAO,IACL,KACA,OACA,cACc;EACd,MAAM,UAAU,WAAW,mBAAmB,YAAY,GAAG,KAAK,UAAU;EAE5E,OAAO,KAAK,MAAM,IAAI,KAAK,OAAO,OAAO;CAC3C;;;;CAKA,AAAO,IAAa,KAAkC;EACpD,OAAO,KAAK,MAAM,IAAO,GAAG;CAC9B;;;;CAKA,AAAO,IAAI,KAAiC;EAC1C,OAAO,KAAK,MAAM,IAAI,GAAG;CAC3B;;;;;CAMA,MAAa,OAAO,KAA8B;EAChD,MAAM,KAAK,MAAM,OAAO,GAAG;EAC3B,MAAM,KAAK,eAAe,GAAG;CAC/B;;;;CAKA,MAAa,KAAc,KAAkC;EAC3D,MAAM,QAAQ,MAAM,KAAK,MAAM,KAAQ,GAAG;EAE1C,IAAI,UAAU,MACZ,MAAM,KAAK,eAAe,GAAG;EAG/B,OAAO;CACT;;;;CAKA,MAAgB,eAAe,KAA8B;EAC3D,MAAM,UAAU,aAAa,KAAK,MAAM,SAAS,MAAM,KAAK,UAAU;EAEtE,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC;EAGF,MAAM,YAAY,KAAK,eAAe,GAAG;EAKzC,MAJe,KAAK,MAAM,OAAO,KAAK,OAI3B,CAAC,CAAC,eAAe,cAAc,SAAS,CAAC;CACtD;;;;;CAMA,AAAO,QAAiB,KAAe,OAAsB;EAC3D,OAAO,KAAK,MAAM,IAAI,KAAK,OAAO;GAChC,KAAK;GACL,MAAM,KAAK;EACb,CAAC;CACH;;;;;;CAOA,MAAa,MAAM,KAAe,OAAY,KAAgC;EAG5E,IAAI,CAAC,MAFgB,KAAK,MAAM,MAAM,KAAK,OAAO,GAAG,GAGnD,OAAO;EAGT,MAAM,UAAU,aAAa,KAAK,MAAM,SAAS,MAAM,KAAK,UAAU;EAEtE,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO;EAKT,MAAM,YAAY,KAAK,eAAe,GAAG;EAKzC,MAJe,KAAK,MAAM,OAAO,KAAK,OAI3B,CAAC,CAAC,qBAAqB,SAAS;EAE3C,OAAO;CACT;;;;CAKA,AAAO,SACL,KACA,cACA,UACY;EACZ,MAAM,UAAU,WACd,2BAA2B,YAAY,GACvC,KAAK,UACP;EAEA,OAAO,KAAK,MAAM,SAAY,KAAK,SAAS,QAAQ;CACtD;;;;;CAMA,AAAO,UAAU,KAAe,OAAiC;EAC/D,OAAO,KAAK,MAAM,UAAU,KAAK,KAAK;CACxC;;;;CAKA,AAAO,UAAU,KAAe,OAAiC;EAC/D,OAAO,KAAK,MAAM,UAAU,KAAK,KAAK;CACxC;;;;;;CAOA,MAAa,aAA4B;EACvC,MAAM,UAAU,aAAa,KAAK,MAAM,SAAS,MAAM,KAAK,UAAU;EAEtE,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC;EAGF,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW;CACnD;;;;;;CAOA,AAAU,eAAe,KAAuB;EAC9C,MAAM,YAAY,OAAO,QAAQ,WAAW,MAAM,cAAc,GAAG;EAEnE,IAAI,CAAC,WACH,OAAO,KAAK,MAAM;EAGpB,OAAO,GAAG,KAAK,MAAM,OAAO,GAAG;CACjC;AACF"}
package/esm/types.d.mts CHANGED
@@ -760,7 +760,11 @@ interface CacheDriver<ClientType, Options> {
760
760
  */
761
761
  swr<T = any>(key: CacheKey, options: CacheSwrOptions, callback: () => Promise<T>): Promise<T>;
762
762
  /**
763
- * Get value and remove it from cache (atomic operation)
763
+ * Get a value and remove it, handing it out exactly once.
764
+ *
765
+ * Atomicity: across servers on `redis` (`GETDEL`) and `pg`
766
+ * (`DELETE … RETURNING`); within the process on `memory`, `memoryExtended`,
767
+ * `lru` and `mock`; serialized per key within the process only on `file`.
764
768
  */
765
769
  pull<T = any>(key: CacheKey): Promise<T | null>;
766
770
  /**
@@ -768,12 +772,17 @@ interface CacheDriver<ClientType, Options> {
768
772
  */
769
773
  forever<T = any>(key: CacheKey, value: T): Promise<T>;
770
774
  /**
771
- * Atomically read, transform, and write a cached value.
775
+ * Read, transform, and write a cached value without losing concurrent updates.
772
776
  *
773
777
  * The callback receives the current value (or `null` on miss) and returns the
774
778
  * next value. Returning `null` removes the key. TTL is preserved unless
775
779
  * explicitly overridden via options.
776
780
  *
781
+ * Atomicity: across servers on `redis` and `pg` (optimistic
782
+ * compare-and-set with retries — `fn` may run more than once under
783
+ * contention, so keep it side-effect free). On every other driver updates
784
+ * are serialized per key within ONE process only. `file` does not support it.
785
+ *
777
786
  * @example
778
787
  * await cache.update<User>("user:1", (current) => {
779
788
  * if (!current) return null;
@@ -819,7 +828,12 @@ interface CacheDriver<ClientType, Options> {
819
828
  */
820
829
  lock<T>(key: CacheKey, ttlOrOptions: CacheTtl | Omit<LockOptions, "driver">, fn: () => Promise<T>): Promise<LockOutcome<T>>;
821
830
  /**
822
- * Increment a numeric value in cache
831
+ * Increment a numeric value in cache. A missing key starts from 0 with the
832
+ * driver default TTL; an existing key keeps its remaining TTL.
833
+ *
834
+ * Atomicity: across servers on `redis` (`INCRBY`) and `pg` (single
835
+ * upsert statement); within the process on `memory`, `memoryExtended`,
836
+ * `lru` and `mock`; serialized per key within the process only on `file`.
823
837
  *
824
838
  * @param key The cache key
825
839
  * @param value The value to increment by (default 1)
@@ -862,6 +876,30 @@ interface CacheDriver<ClientType, Options> {
862
876
  * Create a tagged cache instance for the given tags
863
877
  */
864
878
  tags(tags: string[]): TaggedCacheDriver;
879
+ /**
880
+ * Tag-index primitive: add `members` (normalized, un-prefixed cache keys)
881
+ * to the index stored under `tagKey`. Optional — `TaggedCache` falls back to
882
+ * a get/set array when a driver has none.
883
+ *
884
+ * Built-in drivers: redis → a native SET (`SADD`, atomic across servers);
885
+ * pg → one atomic upsert of a JSON array row; memory / lru / mock → an
886
+ * in-process set that is never evicted or expired; file (base default) →
887
+ * a get/set array serialized per tag key in this process only.
888
+ */
889
+ tagAdd?(tagKey: CacheKey, members: string[]): Promise<void>;
890
+ /**
891
+ * Tag-index primitive: the members stored under `tagKey` (empty when none).
892
+ */
893
+ tagMembers?(tagKey: CacheKey): Promise<string[]>;
894
+ /**
895
+ * Tag-index primitive: remove `members` from `tagKey`. Members added
896
+ * concurrently and not listed are kept.
897
+ */
898
+ tagRemove?(tagKey: CacheKey, members: string[]): Promise<void>;
899
+ /**
900
+ * Tag-index primitive: drop the whole index stored under `tagKey`.
901
+ */
902
+ tagDelete?(tagKey: CacheKey): Promise<void>;
865
903
  /**
866
904
  * Similarity retrieval. Returns the nearest stored entries to `vector` by
867
905
  * cosine similarity, ordered by descending score.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../cache/src/types.ts"],"mappings":"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"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../cache/src/types.ts"],"mappings":"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"}
package/esm/types.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.mjs","names":[],"sources":["../../../../../../cache/src/types.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\r\nimport type { RedisClientOptions } from \"redis\";\r\nimport type {\r\n BaseCacheDriver,\r\n FileCacheDriver,\r\n LRUMemoryCacheDriver,\r\n MemoryCacheDriver,\r\n MockCacheDriver,\r\n MemoryExtendedCacheDriver,\r\n NullCacheDriver,\r\n PgCacheDriver,\r\n RedisCacheDriver,\r\n} from \"./drivers\";\r\n\r\n/**\r\n * Base error class for cache-related errors\r\n */\r\nexport class CacheError extends Error {\r\n public constructor(message: string) {\r\n super(message);\r\n this.name = \"CacheError\";\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when cache connection fails\r\n */\r\nexport class CacheConnectionError extends CacheError {\r\n public constructor(message: string) {\r\n super(message);\r\n this.name = \"CacheConnectionError\";\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when cache driver configuration is invalid\r\n */\r\nexport class CacheConfigurationError extends CacheError {\r\n public constructor(message: string) {\r\n super(message);\r\n this.name = \"CacheConfigurationError\";\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when cache driver is not initialized\r\n */\r\nexport class CacheDriverNotInitializedError extends CacheError {\r\n public constructor(\r\n message: string = \"No cache driver initialized. Call cache.init() or cache.use() first.\",\r\n ) {\r\n super(message);\r\n this.name = \"CacheDriverNotInitializedError\";\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a driver does not implement a requested operation.\r\n *\r\n * Raised when a caller invokes a method the driver cannot fulfill —\r\n * e.g. `update()` on the file driver before the file-lock primitive lands.\r\n */\r\nexport class CacheUnsupportedError extends CacheError {\r\n public constructor(message: string) {\r\n super(message);\r\n this.name = \"CacheUnsupportedError\";\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when an optimistic-concurrency update exhausts its retry budget.\r\n */\r\nexport class CacheConcurrencyError extends CacheError {\r\n public constructor(message: string) {\r\n super(message);\r\n this.name = \"CacheConcurrencyError\";\r\n }\r\n}\r\n\r\n/**\r\n * TTL shape accepted by set/remember/config calls.\r\n *\r\n * - `number` — seconds\r\n * - `string` — human-readable duration parsed via `ms` (`\"1h\"`, `\"30m\"`, `\"7d\"`, `\"2 weeks\"`, …)\r\n * - `Infinity` — no expiration\r\n */\r\nexport type CacheTtl = number | string;\r\n\r\n/**\r\n * Conflict resolution policy for `set` operations.\r\n *\r\n * - `\"create\"` — set only if the key does not exist (Redis `NX`)\r\n * - `\"update\"` — set only if the key already exists (Redis `XX`)\r\n * - `\"upsert\"` — default; overwrite whether the key exists or not\r\n */\r\nexport type CacheConflictPolicy = \"create\" | \"update\" | \"upsert\";\r\n\r\n/**\r\n * Result of a conditional write (`onConflict: \"create\" | \"update\"`).\r\n */\r\nexport type CacheSetResult<T = any> = {\r\n /**\r\n * Whether the write actually took effect.\r\n */\r\n wasSet: boolean;\r\n /**\r\n * The existing value when the write was rejected. Undefined for successful writes\r\n * and for unconditional `upsert` writes.\r\n */\r\n existing: T | null;\r\n};\r\n\r\n/**\r\n * Options for `lock()` — TTL is required (forgotten locks would stay forever),\r\n * `owner` identifies who holds the lock (handy for debugging), `driver` routes\r\n * the call through a non-default driver.\r\n *\r\n * @example\r\n * await cache.lock(\"lock.import\", { ttl: \"5m\", driver: \"redis\" }, async () => {\r\n * await runImport();\r\n * });\r\n */\r\nexport type LockOptions = {\r\n /**\r\n * How long the lock lives before auto-release. Required — guards against\r\n * forgotten locks if the process crashes between acquire and release.\r\n */\r\n ttl: CacheTtl;\r\n /**\r\n * Identifying value stored under the lock key. Defaults to `pid.<process.pid>`.\r\n * Use a custom owner when you want a human-readable label (e.g. `\"worker.jobs-2\"`).\r\n */\r\n owner?: string;\r\n /**\r\n * Per-call driver override by registered name. Manager-level only.\r\n */\r\n driver?: string;\r\n};\r\n\r\n/**\r\n * Discriminated-union result of `lock()`. Unambiguous even when the wrapped\r\n * function legitimately returns `undefined` — the `acquired` flag tells you\r\n * whether `fn` ran at all.\r\n *\r\n * @example\r\n * const outcome = await cache.lock(\"lock.x\", \"1m\", async () => computeValue());\r\n * if (outcome.acquired) {\r\n * console.log(\"ran, got\", outcome.value);\r\n * } else {\r\n * console.log(\"skipped — someone else holds the lock\");\r\n * }\r\n */\r\nexport type LockOutcome<T> =\r\n | { acquired: true; value: T }\r\n | { acquired: false };\r\n\r\n/**\r\n * Options for `remember()` when you need more than just a TTL — e.g. attaching\r\n * tags to the cache-miss write, or routing the single call to a non-default driver.\r\n *\r\n * Passed in the TTL position: `cache.remember(key, { ttl: \"1h\", tags: [\"users\"] }, fn)`.\r\n *\r\n * @example\r\n * await cache.remember(\"user.1\", { ttl: \"1h\", tags: [\"users\"] }, () =>\r\n * db.users.find(1),\r\n * );\r\n */\r\nexport type RememberOptions = {\r\n /**\r\n * TTL applied on cache miss. Accepts seconds (number) or duration string.\r\n */\r\n ttl?: CacheTtl;\r\n /**\r\n * Tags attached to the entry created on miss. Invalidate via `cache.tags([...]).invalidate()`.\r\n */\r\n tags?: string[];\r\n /**\r\n * Per-call driver override. Routes this remember call (both read and write)\r\n * to the named driver without mutating `currentDriver`.\r\n */\r\n driver?: string;\r\n};\r\n\r\n/**\r\n * Rich options for the `set` call-site. Mutually exclusive with a positional TTL.\r\n *\r\n * @example\r\n * await cache.set(\"user:1\", user, {\r\n * ttl: \"1h\",\r\n * tags: [\"users\", \"active\"],\r\n * onConflict: \"create\",\r\n * });\r\n */\r\nexport type CacheSetOptions = {\r\n /**\r\n * Relative TTL (seconds or duration string). Mutually exclusive with `expiresAt`.\r\n */\r\n ttl?: CacheTtl;\r\n /**\r\n * Absolute expiration as a Unix epoch in milliseconds or a Date. Mutually exclusive with `ttl`.\r\n */\r\n expiresAt?: number | Date;\r\n /**\r\n * Tags attached to this entry for tag-based invalidation. Inline equivalent of\r\n * `cache.tags([...]).set(key, value)`.\r\n */\r\n tags?: string[];\r\n /**\r\n * Conflict resolution policy. Defaults to `\"upsert\"`.\r\n */\r\n onConflict?: CacheConflictPolicy;\r\n /**\r\n * Per-call namespace override. Applied ahead of `globalPrefix` for this write only.\r\n */\r\n namespace?: string;\r\n /**\r\n * Per-call driver override by registered name. Routes this write to a non-default driver\r\n * without mutating `currentDriver`.\r\n */\r\n driver?: string;\r\n /**\r\n * Optional embedding vector indexed alongside the entry for similarity retrieval\r\n * via `cache.similar(...)`. Drivers without similarity support throw\r\n * {@link CacheUnsupportedError} when this option is supplied.\r\n *\r\n * Cache is embedding-agnostic — callers compute the vector with whichever embedder\r\n * they like and pass the result here.\r\n *\r\n * @example\r\n * await cache.set(\"doc.123\", doc, { vector: await embed(doc.text), ttl: \"1d\" });\r\n */\r\n vector?: number[];\r\n /**\r\n * Freshness deadline as a Unix epoch in milliseconds. Primarily set by\r\n * {@link CacheSwrOptions} flow — entries with `staleAt` in the future are\r\n * considered \"fresh\" by `cache.swr()`; entries past `staleAt` but before\r\n * `expiresAt` are \"stale-but-revalidatable.\"\r\n *\r\n * Direct callers can pin this manually when bypassing `swr()`, but the\r\n * common path is `cache.swr(key, { freshTtl, staleTtl }, fn)`.\r\n */\r\n staleAt?: number;\r\n};\r\n\r\n/**\r\n * Options for `cache.namespace(prefix, options)` — defaults applied to every\r\n * write produced through the returned scope.\r\n *\r\n * Per-call options always win over scope defaults. Tags merge additively\r\n * (scope tags + call tags, deduped). Nested scopes inherit from the parent;\r\n * the child's own values override.\r\n */\r\nexport type CacheNamespaceOptions = {\r\n /**\r\n * Default TTL for writes inside this scope. Same shape as everywhere else\r\n * — number of seconds or a duration string (`\"1h\"`, `\"30d\"`, …).\r\n */\r\n ttl?: CacheTtl;\r\n /**\r\n * Tags auto-attached to every write inside this scope. Useful for cross-scope\r\n * invalidation hooks (e.g. tag every entry with `user.<id>` so a single\r\n * `cache.flushTag(\"user.42\")` wipes the user's footprint everywhere).\r\n *\r\n * Merged additively with per-call tags; scope tags are never replaced.\r\n */\r\n tags?: string[];\r\n};\r\n\r\n/**\r\n * Options for `cache.swr(key, options, callback)` — stale-while-revalidate.\r\n *\r\n * Two TTLs split the entry's lifecycle into three windows:\r\n * - `now < freshTtl` → fresh, return cached value, no upstream call.\r\n * - `freshTtl <= now < staleTtl` → stale-but-revalidatable, return cached\r\n * value immediately, kick off `callback` in the background to refresh.\r\n * - `now >= staleTtl` → expired, block on `callback` like a normal miss.\r\n *\r\n * Concurrent SWR callers in the stale window all return the cached value;\r\n * the background refresh runs exactly once per key (deduped via the\r\n * driver's per-key lock map).\r\n *\r\n * If the background refresh fails, the stale entry is preserved and the\r\n * error is logged + emitted on the `error` event. Callers that received\r\n * the stale value never see the failure — they got their data.\r\n */\r\nexport type CacheSwrOptions = {\r\n /**\r\n * How long the value is considered fresh. Within this window, SWR returns\r\n * the cached value with no upstream call. Accepts seconds or duration\r\n * string (`\"1m\"`, `\"30s\"`).\r\n */\r\n freshTtl: CacheTtl;\r\n /**\r\n * Total lifetime of the entry (must be greater than `freshTtl`). Between\r\n * `freshTtl` and `staleTtl`, SWR returns the stale value and triggers a\r\n * background refresh. Past `staleTtl`, SWR blocks and refetches.\r\n */\r\n staleTtl: CacheTtl;\r\n /**\r\n * Optional tags applied on the first miss-fetch and on every successful\r\n * background refresh. Same semantics as `CacheSetOptions.tags`.\r\n */\r\n tags?: string[];\r\n /**\r\n * Per-call driver override by registered name. Routes both the read and\r\n * any write/refresh through the named driver without mutating\r\n * `currentDriver`. Same semantics as `RememberOptions.driver`.\r\n */\r\n driver?: string;\r\n};\r\n\r\n/**\r\n * Options for `cache.similar(vector, options)` — similarity retrieval against\r\n * stored entries previously written with `set({ vector })`.\r\n */\r\nexport type CacheSimilarOptions = {\r\n /**\r\n * Maximum number of hits to return. Required — no implicit default keeps\r\n * callers from accidentally pulling the entire candidate set.\r\n */\r\n topK: number;\r\n /**\r\n * Cosine similarity floor in `[0, 1]`. Hits scoring strictly below this are\r\n * filtered out before `topK` truncation.\r\n */\r\n threshold?: number;\r\n /**\r\n * Optional tag filter. Only entries tagged with at least one of the given\r\n * tags are considered (matches the union semantics elsewhere in the package).\r\n */\r\n tags?: string[];\r\n};\r\n\r\n/**\r\n * One result from a `similar()` query — the original key, its stored value,\r\n * and the cosine similarity to the query vector.\r\n */\r\nexport type CacheSimilarHit<T = unknown> = {\r\n /**\r\n * The original cache key, post-`parseKey` normalization.\r\n */\r\n key: string;\r\n /**\r\n * Stored value, deep-cloned to protect the cache from accidental mutation\r\n * (same semantics as `get`).\r\n */\r\n value: T;\r\n /**\r\n * Cosine similarity in `[0, 1]`. Higher means more similar.\r\n */\r\n score: number;\r\n};\r\n\r\n/**\r\n * Cache key type - can be a string or an object\r\n */\r\nexport type CacheKey = string | GenericObject;\r\n\r\nexport type CacheOperationType =\r\n | \"fetching\"\r\n | \"fetched\"\r\n | \"caching\"\r\n | \"cached\"\r\n | \"flushing\"\r\n | \"flushed\"\r\n | \"removing\"\r\n | \"removed\"\r\n | \"clearing\"\r\n | \"cleared\"\r\n | \"expired\"\r\n | \"notFound\"\r\n | \"connecting\"\r\n | \"error\"\r\n | \"connectionFailed\"\r\n | \"connected\"\r\n | \"disconnecting\"\r\n | \"disconnected\";\r\n\r\n/**\r\n * Cache event types for observability\r\n */\r\nexport type CacheEventType =\r\n | \"hit\"\r\n | \"miss\"\r\n | \"set\"\r\n | \"removed\"\r\n | \"flushed\"\r\n | \"expired\"\r\n | \"connected\"\r\n | \"disconnected\"\r\n | \"error\";\r\n\r\n/**\r\n * Cache event data structure\r\n */\r\nexport type CacheEventData = {\r\n /**\r\n * The cache key involved in the event\r\n */\r\n key?: string;\r\n /**\r\n * The value (for set/hit events)\r\n */\r\n value?: any;\r\n /**\r\n * TTL in seconds (for set events)\r\n */\r\n ttl?: number;\r\n /**\r\n * Driver name that emitted the event\r\n */\r\n driver: string;\r\n /**\r\n * Error object (for error events)\r\n */\r\n error?: any;\r\n /**\r\n * Namespace (for namespace operations)\r\n */\r\n namespace?: string;\r\n};\r\n\r\n/**\r\n * Event handler function type\r\n */\r\nexport type CacheEventHandler = (eventData: CacheEventData) => void | Promise<void>;\r\n\r\n/**\r\n * Per-operation latency + counter snapshot returned by `cache.metrics()`.\r\n *\r\n * The shape is a single recursive level — the top object covers all drivers\r\n * and each entry under `byDriver` repeats the same fields without nesting\r\n * further. Use this for ad-hoc dashboards, exporters to Prometheus / StatsD,\r\n * or just `console.log` during development.\r\n */\r\nexport type CacheMetricsSnapshot = {\r\n /** Cumulative cache hits across the lifetime of the collector. */\r\n hits: number;\r\n /** Cumulative cache misses (lookups that returned null). */\r\n misses: number;\r\n /** Cumulative successful writes. */\r\n sets: number;\r\n /** Cumulative key removals. */\r\n removed: number;\r\n /** Cumulative cache errors emitted via the `error` event. */\r\n errors: number;\r\n /** `hits / (hits + misses)` — `0` when no read ops have happened yet. */\r\n hitRate: number;\r\n /** Latency percentiles in milliseconds, computed from a circular buffer. */\r\n latencyMs: {\r\n p50: number;\r\n p95: number;\r\n p99: number;\r\n /** Size of the underlying buffer at snapshot time (capped at the configured max). */\r\n samples: number;\r\n };\r\n /** Per-driver breakdowns keyed by driver name (`\"memory\"`, `\"redis\"`, …). */\r\n byDriver: Record<string, Omit<CacheMetricsSnapshot, \"byDriver\">>;\r\n /** Millisecond timestamp the collector last reset (or was instantiated). */\r\n startedAt: number;\r\n};\r\n\r\n/**\r\n * Tagged cache interface for working with cache tags\r\n */\r\nexport interface TaggedCacheDriver {\r\n /**\r\n * Set a value in cache with tags\r\n */\r\n set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;\r\n /**\r\n * Get a value from cache (checks tags)\r\n */\r\n get(key: CacheKey): Promise<any | null>;\r\n /**\r\n * Remove a specific key\r\n */\r\n remove(key: CacheKey): Promise<void>;\r\n /**\r\n * Invalidate (clear) all keys associated with the current tags\r\n */\r\n invalidate(): Promise<void>;\r\n /**\r\n * Flush all keys associated with the current tags\r\n * @deprecated Use invalidate() instead\r\n */\r\n flush(): Promise<void>;\r\n /**\r\n * Check if a key exists\r\n */\r\n has(key: CacheKey): Promise<boolean>;\r\n /**\r\n * Remember pattern with tags\r\n */\r\n remember(key: CacheKey, ttl: number, callback: () => Promise<any>): Promise<any>;\r\n /**\r\n * Pull value with tags\r\n */\r\n pull(key: CacheKey): Promise<any | null>;\r\n /**\r\n * Forever with tags\r\n */\r\n forever(key: CacheKey, value: any): Promise<any>;\r\n /**\r\n * Increment with tags\r\n */\r\n increment(key: CacheKey, value?: number): Promise<number>;\r\n /**\r\n * Decrement with tags\r\n */\r\n decrement(key: CacheKey, value?: number): Promise<number>;\r\n}\r\n\r\nexport type MemoryCacheOptions = {\r\n /**\r\n * The global prefix for the cache key\r\n */\r\n globalPrefix?: string | (() => string);\r\n /**\r\n * The default TTL for the cache. Accepts a number of seconds or a human-readable\r\n * duration string like `\"1h\"`, `\"30m\"`, `\"7d\"`.\r\n *\r\n * @default Infinity\r\n */\r\n ttl?: CacheTtl;\r\n /**\r\n * Maximum number of items in cache\r\n * When exceeded, least recently used items will be evicted\r\n *\r\n * @default undefined (no limit)\r\n */\r\n maxSize?: number;\r\n};\r\n\r\nexport type MemoryExtendedCacheOptions = MemoryCacheOptions;\r\n\r\nexport type LRUMemoryCacheOptions = {\r\n /**\r\n * The maximum number of items in the cache\r\n *\r\n * @default 1000\r\n */\r\n capacity?: number;\r\n /**\r\n * The global prefix for the cache key. Applied via `parseCacheKey`, same\r\n * semantics as the other drivers.\r\n */\r\n globalPrefix?: string | (() => string);\r\n /**\r\n * The default TTL for new entries. Accepts a number of seconds or a\r\n * human-readable duration string like `\"1h\"`, `\"30m\"`, `\"7d\"`.\r\n *\r\n * @default Infinity\r\n */\r\n ttl?: CacheTtl;\r\n};\r\n\r\nexport type FileCacheOptions = {\r\n /**\r\n * The global prefix for the cache key\r\n */\r\n globalPrefix?: string | (() => string);\r\n /**\r\n * The default TTL for the cache. Accepts a number of seconds or a human-readable\r\n * duration string like `\"1h\"`, `\"30m\"`, `\"7d\"`.\r\n *\r\n * @default 0\r\n */\r\n ttl?: CacheTtl;\r\n /**\r\n * Storage cache directory\r\n *\r\n * @default storagePath(\"cache\")\r\n */\r\n directory: string | (() => string);\r\n /**\r\n * File name\r\n *\r\n * @default cache.json\r\n */\r\n fileName?: string | (() => string);\r\n};\r\n\r\nexport type RedisOptions = {\r\n /**\r\n * Redis Port\r\n *\r\n * @default 6379\r\n */\r\n port?: number;\r\n /**\r\n * Redis Host\r\n */\r\n host?: string;\r\n /**\r\n * Redis Username\r\n */\r\n username?: string;\r\n /**\r\n * Redis Password\r\n */\r\n password?: string;\r\n /**\r\n * Redis URL\r\n *\r\n * If used, it will override the host and port options\r\n */\r\n url?: string;\r\n /**\r\n * Global prefix for the cache key\r\n */\r\n globalPrefix?: string | (() => string);\r\n /**\r\n * Default TTL. Accepts a number of seconds or a human-readable duration string\r\n * like `\"1h\"`, `\"30m\"`, `\"7d\"`.\r\n *\r\n * @default Infinity\r\n */\r\n ttl?: CacheTtl;\r\n /**\r\n * Redis client options\r\n */\r\n clientOptions?: RedisClientOptions;\r\n};\r\n\r\nexport type NullCacheDriverOptions = GenericObject;\r\n\r\n/**\r\n * Options accepted by {@link MockCacheDriver}. Same shape as the memory\r\n * driver — only `globalPrefix` and `ttl` apply, since the mock's storage is\r\n * a plain `Map` with no eviction policy.\r\n */\r\nexport type MockCacheOptions = {\r\n /**\r\n * Global key prefix, applied via `parseKey` (matches the other drivers).\r\n */\r\n globalPrefix?: string | (() => string);\r\n /**\r\n * Default TTL for new entries. Accepts seconds or a duration string.\r\n *\r\n * @default Infinity\r\n */\r\n ttl?: CacheTtl;\r\n};\r\n\r\n/**\r\n * One row in {@link MockCacheDriver.callLog} — captures every public op\r\n * the driver received in arrival order. Useful for behavioral assertions\r\n * in downstream tests (\"did my service actually invalidate the cache?\").\r\n */\r\nexport type CacheCall = {\r\n /**\r\n * Operation name as it appears on the driver contract — `\"set\"`, `\"get\"`,\r\n * `\"remove\"`, `\"flush\"`, `\"removeNamespace\"`, `\"has\"`, etc.\r\n */\r\n operation: string;\r\n /**\r\n * Post-`parseKey` cache key when the op is key-addressed; `undefined` for\r\n * keyless ops (`flush`, `connect`, `disconnect`).\r\n */\r\n key?: string;\r\n /**\r\n * Raw arguments passed to the call site, in declaration order. Lets\r\n * callers assert on TTLs, options objects, vector payloads, etc.\r\n */\r\n args: unknown[];\r\n /**\r\n * `Date.now()` when the call was recorded. Useful for timing-related\r\n * assertions (e.g. \"the second invalidation came within 100ms\").\r\n */\r\n timestamp: number;\r\n};\r\n\r\n/**\r\n * Minimal `pg`-compatible client surface the cache driver depends on.\r\n *\r\n * Both `pg.Pool` and `pg.Client` from the `pg` package satisfy this — the\r\n * driver only ever calls `query(text, values)`. Typing it loosely keeps\r\n * `pg` strictly optional: consumers without the `pg` package installed\r\n * never hit a missing import.\r\n */\r\nexport type PgClientLike = {\r\n query(text: string, values?: unknown[]): Promise<{ rows: any[]; rowCount?: number | null }>;\r\n};\r\n\r\nexport type PgCacheOptions = {\r\n /**\r\n * User-supplied `pg.Pool` or `pg.Client`. The driver does NOT own the\r\n * connection lifecycle — `cache.disconnect()` will not close this client.\r\n */\r\n client: PgClientLike;\r\n /**\r\n * Database table name. Default: `\"warlock_cache\"`.\r\n *\r\n * Sanitized: only `[A-Za-z0-9_]` are allowed; anything else throws\r\n * {@link CacheConfigurationError} at `setOptions` time.\r\n */\r\n table?: string;\r\n /**\r\n * Global key prefix, applied via `parseKey` (same semantics as the other drivers).\r\n */\r\n globalPrefix?: string | (() => string);\r\n /**\r\n * Default TTL for new entries. Accepts seconds or a duration string\r\n * (`\"1h\"`, `\"7d\"`, …).\r\n *\r\n * @default Infinity\r\n */\r\n ttl?: CacheTtl;\r\n /**\r\n * Optional pgvector configuration. When present, the driver provisions an\r\n * `embedding VECTOR(dimensions)` column + similarity index, and `similar()`\r\n * queries via the `<=>` cosine-distance operator.\r\n *\r\n * Requires the pgvector extension (`CREATE EXTENSION vector;`) — the driver\r\n * verifies its presence on the first vector operation and throws\r\n * {@link CacheConfigurationError} if missing.\r\n *\r\n * Omit this block (or remove it) to run the driver in KV-only mode —\r\n * `set({ vector })` and `similar()` then throw {@link CacheUnsupportedError}.\r\n */\r\n vector?: {\r\n /**\r\n * Vector dimension count. Must match the embedder you use throughout the\r\n * lifetime of the table. Mixing dimensions on the same table is unsupported.\r\n */\r\n dimensions: number;\r\n /**\r\n * pgvector index strategy. `hnsw` (default) is faster to query and slightly\r\n * slower to build; `ivfflat` is faster to build but typically slower to query.\r\n *\r\n * @default \"hnsw\"\r\n */\r\n index?: \"hnsw\" | \"ivfflat\";\r\n };\r\n};\r\n\r\nexport interface CacheDriver<ClientType, Options> {\r\n /**\r\n * The cache driver options\r\n */\r\n options: Options;\r\n /**\r\n * Cache driver name\r\n */\r\n name: string;\r\n /**\r\n * Set logging state\r\n */\r\n setLoggingState(shouldLog: boolean): any;\r\n /**\r\n * Remove all cached items by namespace\r\n */\r\n removeNamespace(namespace: string): Promise<any>;\r\n /**\r\n * Set the cache driver options\r\n */\r\n setOptions(options: Options): any;\r\n /**\r\n * Parse the key to be used in the cache\r\n */\r\n parseKey(key: CacheKey): string;\r\n /**\r\n * Set a value in the cache.\r\n *\r\n * @param key The cache key, could be an object or string\r\n * @param value The value to be stored in the cache\r\n * @param ttlOrOptions Either a TTL (seconds number, or duration string like `\"1h\"`),\r\n * or a full {@link CacheSetOptions} object.\r\n *\r\n * @example\r\n * // Positional TTL (back-compat)\r\n * await cache.set(\"user:1\", user, 3600);\r\n *\r\n * @example\r\n * // Human-readable duration\r\n * await cache.set(\"user:1\", user, \"1h\");\r\n *\r\n * @example\r\n * // Rich options\r\n * await cache.set(\"user:1\", user, { ttl: \"1h\", tags: [\"users\"], onConflict: \"create\" });\r\n */\r\n set(\r\n key: CacheKey,\r\n value: any,\r\n ttlOrOptions?: CacheTtl | CacheSetOptions,\r\n ): Promise<any>;\r\n /**\r\n * Get a value from the cache\r\n */\r\n get<T = any>(key: CacheKey): Promise<T | null>;\r\n /**\r\n * Remove a value from the cache\r\n */\r\n remove(key: CacheKey): Promise<void>;\r\n /**\r\n * Flush the entire cache\r\n */\r\n flush(): Promise<void>;\r\n /**\r\n * Connect to the cache driver\r\n */\r\n connect(): Promise<any>;\r\n /**\r\n * The cache client\r\n */\r\n client?: ClientType;\r\n /**\r\n * Disconnect the cache driver\r\n */\r\n disconnect(): Promise<void>;\r\n /**\r\n * Check if a key exists in the cache without fetching its value\r\n */\r\n has(key: CacheKey): Promise<boolean>;\r\n /**\r\n * Get value from cache or execute callback and cache the result.\r\n *\r\n * The second argument accepts a TTL (number of seconds or duration string like `\"1h\"`)\r\n * or a full {@link RememberOptions} object when you need to attach tags or route to\r\n * a non-default driver.\r\n *\r\n * @example\r\n * // Positional TTL — the common case\r\n * const user = await cache.remember(\"user.1\", \"1h\", () => db.users.find(1));\r\n *\r\n * @example\r\n * // Options form — tag the cache-miss write for bulk invalidation\r\n * const user = await cache.remember(\"user.1\", { ttl: \"1h\", tags: [\"users\"] }, () =>\r\n * db.users.find(1),\r\n * );\r\n */\r\n remember<T = any>(\r\n key: CacheKey,\r\n ttlOrOptions: CacheTtl | RememberOptions,\r\n callback: () => Promise<T>,\r\n ): Promise<T>;\r\n /**\r\n * Stale-while-revalidate. Returns the cached value when fresh; returns\r\n * the stale value + kicks off a background refresh when within the\r\n * `freshTtl..staleTtl` window; blocks like a normal miss when past\r\n * `staleTtl`. Concurrent stale-window callers share one in-flight\r\n * refresh.\r\n *\r\n * Background refresh failures preserve the stale entry and emit an\r\n * `error` event — the stale-returning caller never sees the failure.\r\n *\r\n * @example\r\n * const product = await cache.swr(\r\n * \"product.42\",\r\n * { freshTtl: \"1m\", staleTtl: \"1h\" },\r\n * () => db.products.find(42),\r\n * );\r\n */\r\n swr<T = any>(\r\n key: CacheKey,\r\n options: CacheSwrOptions,\r\n callback: () => Promise<T>,\r\n ): Promise<T>;\r\n /**\r\n * Get value and remove it from cache (atomic operation)\r\n */\r\n pull<T = any>(key: CacheKey): Promise<T | null>;\r\n /**\r\n * Set a value in cache permanently (no expiration)\r\n */\r\n forever<T = any>(key: CacheKey, value: T): Promise<T>;\r\n /**\r\n * Atomically read, transform, and write a cached value.\r\n *\r\n * The callback receives the current value (or `null` on miss) and returns the\r\n * next value. Returning `null` removes the key. TTL is preserved unless\r\n * explicitly overridden via options.\r\n *\r\n * @example\r\n * await cache.update<User>(\"user:1\", (current) => {\r\n * if (!current) return null;\r\n * return { ...current, lastSeen: Date.now() };\r\n * });\r\n */\r\n update<T = any>(\r\n key: CacheKey,\r\n fn: (current: T | null) => T | null | Promise<T | null>,\r\n options?: { ttl?: CacheTtl },\r\n ): Promise<T | null>;\r\n /**\r\n * Shallow-merge a partial object into a cached value.\r\n *\r\n * If the key is missing, treats the current value as `{}`. Preserves TTL by default.\r\n *\r\n * @example\r\n * await cache.merge<User>(\"user:1\", { name: \"Jane\" });\r\n */\r\n merge<T extends Record<string, any> = Record<string, any>>(\r\n key: CacheKey,\r\n partial: Partial<T>,\r\n options?: { ttl?: CacheTtl },\r\n ): Promise<T>;\r\n /**\r\n * List sub-API factory — returns a {@link CacheListAccessor} bound to the given key.\r\n *\r\n * @example\r\n * const recent = cache.list<Event>(\"recent-events\");\r\n * await recent.push(event);\r\n * const last10 = await recent.slice(0, 10);\r\n */\r\n list<T = any>(key: CacheKey): CacheListAccessor<T>;\r\n /**\r\n * Acquire a distributed lock, run `fn`, and auto-release. Returns a\r\n * {@link LockOutcome} so callers can distinguish \"ran and produced this value\"\r\n * from \"skipped because someone else holds the lock.\"\r\n *\r\n * Built on top of `set({ onConflict: \"create\" })` — Redis-native, emulated\r\n * on other drivers (single-process semantics elsewhere).\r\n *\r\n * @example\r\n * const outcome = await cache.lock(\"lock.import\", \"5m\", async () => {\r\n * await runImport();\r\n * return \"done\";\r\n * });\r\n */\r\n lock<T>(\r\n key: CacheKey,\r\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\r\n fn: () => Promise<T>,\r\n ): Promise<LockOutcome<T>>;\r\n /**\r\n * Increment a numeric value in cache\r\n *\r\n * @param key The cache key\r\n * @param value The value to increment by (default 1)\r\n */\r\n increment(key: CacheKey, value?: number): Promise<number>;\r\n /**\r\n * Decrement a numeric value in cache\r\n *\r\n * @param key The cache key\r\n * @param value The value to decrement by (default 1)\r\n */\r\n decrement(key: CacheKey, value?: number): Promise<number>;\r\n /**\r\n * Get multiple values from cache at once\r\n */\r\n many(keys: CacheKey[]): Promise<any[]>;\r\n /**\r\n * Set multiple values in cache at once\r\n */\r\n setMany(items: Record<string, any>, ttl?: number): Promise<void>;\r\n /**\r\n * Register an event listener\r\n */\r\n on(event: CacheEventType, handler: CacheEventHandler): this;\r\n /**\r\n * Remove an event listener\r\n */\r\n off(event: CacheEventType, handler: CacheEventHandler): this;\r\n /**\r\n * Register a one-time event listener\r\n */\r\n once(event: CacheEventType, handler: CacheEventHandler): this;\r\n /**\r\n * Set if not exists (atomic operation)\r\n * Returns true if key was set, false if key already existed\r\n * Note: Not all drivers support this operation\r\n */\r\n setNX?(key: CacheKey, value: any, ttl?: number): Promise<boolean>;\r\n /**\r\n * Create a tagged cache instance for the given tags\r\n */\r\n tags(tags: string[]): TaggedCacheDriver;\r\n /**\r\n * Similarity retrieval. Returns the nearest stored entries to `vector` by\r\n * cosine similarity, ordered by descending score.\r\n *\r\n * Drivers without a similarity index throw {@link CacheUnsupportedError}.\r\n * Memory-family drivers brute-force scan in O(N) — suitable for development\r\n * but not for production knowledge bases beyond a few thousand entries; use\r\n * the `pg` driver (with pgvector) or `redis` driver (with RediSearch) instead.\r\n *\r\n * @example\r\n * const hits = await cache.similar(await embed(query), { topK: 5, threshold: 0.7 });\r\n */\r\n similar<T = any>(\r\n vector: number[],\r\n options: CacheSimilarOptions,\r\n ): Promise<CacheSimilarHit<T>[]>;\r\n}\r\n\r\n/**\r\n * Accessor for list-shaped cached values.\r\n *\r\n * Defaults to read-mutate-write semantics on non-native drivers (memory, file, LRU).\r\n * The Redis driver overrides this with native `LPUSH` / `RPUSH` / `LRANGE` / `LTRIM` for O(1) ops.\r\n */\r\nexport interface CacheListAccessor<T = any> {\r\n /**\r\n * Append one or more items to the tail of the list. Returns the new length.\r\n */\r\n push(...items: T[]): Promise<number>;\r\n /**\r\n * Prepend one or more items to the head of the list. Returns the new length.\r\n */\r\n unshift(...items: T[]): Promise<number>;\r\n /**\r\n * Remove and return the tail item, or `null` if the list is empty.\r\n */\r\n pop(): Promise<T | null>;\r\n /**\r\n * Remove and return the head item, or `null` if the list is empty.\r\n */\r\n shift(): Promise<T | null>;\r\n /**\r\n * Return a slice of the list. End is exclusive, mirroring `Array.prototype.slice`.\r\n */\r\n slice(start?: number, end?: number): Promise<T[]>;\r\n /**\r\n * Return the full list.\r\n */\r\n all(): Promise<T[]>;\r\n /**\r\n * Return the length of the list.\r\n */\r\n length(): Promise<number>;\r\n /**\r\n * Trim the list to the inclusive range `[start, end]`. Outside elements are dropped.\r\n */\r\n trim(start: number, end: number): Promise<void>;\r\n /**\r\n * Remove the entire list.\r\n */\r\n clear(): Promise<void>;\r\n}\r\n\r\n/**\r\n * One-shot tagged handle returned by `ScopedCacheContract.tags(...)`.\r\n *\r\n * Identical write surface to {@link TaggedCacheDriver}, except the underlying\r\n * scope's prefix is applied to every key and the scope's default TTL/tags\r\n * still flow through. Tags supplied here merge additively with scope-level\r\n * tags.\r\n */\r\nexport interface TaggedScopedCacheContract {\r\n set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;\r\n get<T = any>(key: CacheKey): Promise<T | null>;\r\n has(key: CacheKey): Promise<boolean>;\r\n remove(key: CacheKey): Promise<void>;\r\n pull<T = any>(key: CacheKey): Promise<T | null>;\r\n forever<T = any>(key: CacheKey, value: T): Promise<T>;\r\n setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean>;\r\n remember<T = any>(\r\n key: CacheKey,\r\n ttlOrOptions: CacheTtl | RememberOptions,\r\n callback: () => Promise<T>,\r\n ): Promise<T>;\r\n increment(key: CacheKey, value?: number): Promise<number>;\r\n decrement(key: CacheKey, value?: number): Promise<number>;\r\n /**\r\n * Invalidate every entry tagged with the union of scope tags + handle tags.\r\n * Forwarded to the underlying tag index — tags are global, scope-agnostic.\r\n */\r\n invalidate(): Promise<void>;\r\n}\r\n\r\n/**\r\n * Scoped view over the cache. Returned by `cache.namespace(prefix, options?)`.\r\n *\r\n * A scope prepends its `prefix` to every key, applies optional default TTL/tags\r\n * to every write, and forwards everything else to the underlying source. It\r\n * stores nothing of its own — purely a convenience wrapper.\r\n *\r\n * @example\r\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\r\n * await chat.set(\"messages.1\", msg); // → \"chats.10.messages.1\", TTL 30d\r\n * await chat.set(\"draft\", d, { ttl: \"1h\" }); // per-call ttl wins\r\n * await chat.clear(); // wipe the whole scope\r\n */\r\nexport interface ScopedCacheContract {\r\n /** The fully-qualified prefix this scope prepends to every key. */\r\n readonly prefix: string;\r\n\r\n /**\r\n * Nested scope. Inherits the parent's defaults; child's own options override.\r\n *\r\n * @example\r\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\r\n * const typing = chat.namespace(\"typing\", { ttl: \"5s\" });\r\n * // typing.prefix === \"chats.10.typing\"\r\n */\r\n namespace(prefix: string, options?: CacheNamespaceOptions): ScopedCacheContract;\r\n\r\n /**\r\n * One-shot tagged write handle. Tags merge additively with scope defaults.\r\n */\r\n tags(tags: string[]): TaggedScopedCacheContract;\r\n\r\n /** Wipe every entry under this scope's prefix. Sugar for `removeNamespace(prefix)`. */\r\n clear(): Promise<void>;\r\n\r\n // Reads\r\n get<T = any>(key: CacheKey): Promise<T | null>;\r\n has(key: CacheKey): Promise<boolean>;\r\n many(keys: CacheKey[]): Promise<any[]>;\r\n pull<T = any>(key: CacheKey): Promise<T | null>;\r\n\r\n // Writes\r\n set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;\r\n setMany(items: Record<string, any>, ttl?: number): Promise<void>;\r\n setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean>;\r\n forever<T = any>(key: CacheKey, value: T): Promise<T>;\r\n remove(key: CacheKey): Promise<void>;\r\n\r\n // Read-or-compute\r\n remember<T = any>(\r\n key: CacheKey,\r\n ttlOrOptions: CacheTtl | RememberOptions,\r\n callback: () => Promise<T>,\r\n ): Promise<T>;\r\n swr<T = any>(\r\n key: CacheKey,\r\n options: CacheSwrOptions,\r\n callback: () => Promise<T>,\r\n ): Promise<T>;\r\n\r\n // Mutations\r\n increment(key: CacheKey, value?: number): Promise<number>;\r\n decrement(key: CacheKey, value?: number): Promise<number>;\r\n update<T = any>(\r\n key: CacheKey,\r\n fn: (current: T | null) => T | null | Promise<T | null>,\r\n options?: { ttl?: CacheTtl },\r\n ): Promise<T | null>;\r\n merge<T extends Record<string, any> = Record<string, any>>(\r\n key: CacheKey,\r\n partial: Partial<T>,\r\n options?: { ttl?: CacheTtl },\r\n ): Promise<T>;\r\n\r\n // Structured accessors\r\n list<T = any>(key: CacheKey): CacheListAccessor<T>;\r\n\r\n // Coordination\r\n lock<T>(\r\n key: CacheKey,\r\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\r\n fn: () => Promise<T>,\r\n ): Promise<LockOutcome<T>>;\r\n\r\n // Similarity (delegated; throws CacheUnsupportedError if the driver lacks it)\r\n similar<T = any>(\r\n vector: number[],\r\n options: CacheSimilarOptions,\r\n ): Promise<CacheSimilarHit<T>[]>;\r\n}\r\n\r\nexport type CacheData = {\r\n /**\r\n * Value stored in the cache\r\n */\r\n data: any;\r\n /**\r\n * The expiration date in milliseconds\r\n */\r\n expiresAt?: number;\r\n /**\r\n * Time to live in seconds\r\n */\r\n ttl?: number;\r\n /**\r\n * Freshness deadline as a millisecond timestamp. Used by `swr()` — entries\r\n * with `staleAt` in the future are \"fresh\"; past `staleAt` but before\r\n * `expiresAt` are \"stale-but-revalidatable\" and trigger a background\r\n * refresh on the next read. Optional — entries written through plain\r\n * `set()` skip this field entirely and `swr()` treats them as always-fresh.\r\n */\r\n staleAt?: number;\r\n};\r\n\r\nexport type DriverClass = new () => CacheDriver<any, any>;\r\n\r\ntype DefaultDrivers =\r\n | \"redis\"\r\n | \"file\"\r\n | \"memory\"\r\n | \"memoryExtended\"\r\n | \"null\"\r\n | \"lru\"\r\n | \"pg\"\r\n | \"mock\";\r\n\r\ntype MergeWithDefaultDrivers<T> = T extends undefined ? DefaultDrivers : DefaultDrivers | T;\r\n\r\nexport type CacheConfigurations<\r\n T extends string | undefined = undefined,\r\n DriverName = MergeWithDefaultDrivers<T>,\r\n> = {\r\n /**\r\n * The default cache driver name\r\n */\r\n default?: DriverName;\r\n /**\r\n * Determine whether to log or not\r\n *\r\n * @default true\r\n */\r\n logging?: boolean;\r\n /**\r\n * The cache drivers list\r\n */\r\n drivers: {\r\n redis?: typeof RedisCacheDriver;\r\n file?: typeof FileCacheDriver;\r\n null?: typeof NullCacheDriver;\r\n memory?: typeof MemoryCacheDriver;\r\n memoryExtended?: typeof MemoryExtendedCacheDriver;\r\n lru?: typeof LRUMemoryCacheDriver;\r\n pg?: typeof PgCacheDriver;\r\n mock?: typeof MockCacheDriver;\r\n } & {\r\n [key in Extract<T, string>]?: typeof BaseCacheDriver<any, any> | undefined;\r\n };\r\n /**\r\n * The cache driver options\r\n */\r\n options: {\r\n redis?: RedisOptions;\r\n file?: FileCacheOptions;\r\n memory?: MemoryCacheOptions;\r\n memoryExtended?: MemoryExtendedCacheOptions;\r\n null?: NullCacheDriverOptions;\r\n lru?: LRUMemoryCacheOptions;\r\n pg?: PgCacheOptions;\r\n mock?: MockCacheOptions;\r\n } & {\r\n [key in Extract<T, string>]?: GenericObject;\r\n };\r\n};\r\n"],"mappings":";;;;AAiBA,IAAa,aAAb,cAAgC,MAAM;CACpC,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,uBAAb,cAA0C,WAAW;CACnD,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,0BAAb,cAA6C,WAAW;CACtD,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,iCAAb,cAAoD,WAAW;CAC7D,AAAO,YACL,UAAkB,wEAClB;EACA,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;;;;AAQA,IAAa,wBAAb,cAA2C,WAAW;CACpD,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,wBAAb,cAA2C,WAAW;CACpD,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF"}
1
+ {"version":3,"file":"types.mjs","names":[],"sources":["../../../../../../cache/src/types.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\nimport type { RedisClientOptions } from \"redis\";\nimport type {\n BaseCacheDriver,\n FileCacheDriver,\n LRUMemoryCacheDriver,\n MemoryCacheDriver,\n MockCacheDriver,\n MemoryExtendedCacheDriver,\n NullCacheDriver,\n PgCacheDriver,\n RedisCacheDriver,\n} from \"./drivers\";\n\n/**\n * Base error class for cache-related errors\n */\nexport class CacheError extends Error {\n public constructor(message: string) {\n super(message);\n this.name = \"CacheError\";\n }\n}\n\n/**\n * Error thrown when cache connection fails\n */\nexport class CacheConnectionError extends CacheError {\n public constructor(message: string) {\n super(message);\n this.name = \"CacheConnectionError\";\n }\n}\n\n/**\n * Error thrown when cache driver configuration is invalid\n */\nexport class CacheConfigurationError extends CacheError {\n public constructor(message: string) {\n super(message);\n this.name = \"CacheConfigurationError\";\n }\n}\n\n/**\n * Error thrown when cache driver is not initialized\n */\nexport class CacheDriverNotInitializedError extends CacheError {\n public constructor(\n message: string = \"No cache driver initialized. Call cache.init() or cache.use() first.\",\n ) {\n super(message);\n this.name = \"CacheDriverNotInitializedError\";\n }\n}\n\n/**\n * Error thrown when a driver does not implement a requested operation.\n *\n * Raised when a caller invokes a method the driver cannot fulfill —\n * e.g. `update()` on the file driver before the file-lock primitive lands.\n */\nexport class CacheUnsupportedError extends CacheError {\n public constructor(message: string) {\n super(message);\n this.name = \"CacheUnsupportedError\";\n }\n}\n\n/**\n * Error thrown when an optimistic-concurrency update exhausts its retry budget.\n */\nexport class CacheConcurrencyError extends CacheError {\n public constructor(message: string) {\n super(message);\n this.name = \"CacheConcurrencyError\";\n }\n}\n\n/**\n * TTL shape accepted by set/remember/config calls.\n *\n * - `number` — seconds\n * - `string` — human-readable duration parsed via `ms` (`\"1h\"`, `\"30m\"`, `\"7d\"`, `\"2 weeks\"`, …)\n * - `Infinity` — no expiration\n */\nexport type CacheTtl = number | string;\n\n/**\n * Conflict resolution policy for `set` operations.\n *\n * - `\"create\"` — set only if the key does not exist (Redis `NX`)\n * - `\"update\"` — set only if the key already exists (Redis `XX`)\n * - `\"upsert\"` — default; overwrite whether the key exists or not\n */\nexport type CacheConflictPolicy = \"create\" | \"update\" | \"upsert\";\n\n/**\n * Result of a conditional write (`onConflict: \"create\" | \"update\"`).\n */\nexport type CacheSetResult<T = any> = {\n /**\n * Whether the write actually took effect.\n */\n wasSet: boolean;\n /**\n * The existing value when the write was rejected. Undefined for successful writes\n * and for unconditional `upsert` writes.\n */\n existing: T | null;\n};\n\n/**\n * Options for `lock()` — TTL is required (forgotten locks would stay forever),\n * `owner` identifies who holds the lock (handy for debugging), `driver` routes\n * the call through a non-default driver.\n *\n * @example\n * await cache.lock(\"lock.import\", { ttl: \"5m\", driver: \"redis\" }, async () => {\n * await runImport();\n * });\n */\nexport type LockOptions = {\n /**\n * How long the lock lives before auto-release. Required — guards against\n * forgotten locks if the process crashes between acquire and release.\n */\n ttl: CacheTtl;\n /**\n * Identifying value stored under the lock key. Defaults to `pid.<process.pid>`.\n * Use a custom owner when you want a human-readable label (e.g. `\"worker.jobs-2\"`).\n */\n owner?: string;\n /**\n * Per-call driver override by registered name. Manager-level only.\n */\n driver?: string;\n};\n\n/**\n * Discriminated-union result of `lock()`. Unambiguous even when the wrapped\n * function legitimately returns `undefined` — the `acquired` flag tells you\n * whether `fn` ran at all.\n *\n * @example\n * const outcome = await cache.lock(\"lock.x\", \"1m\", async () => computeValue());\n * if (outcome.acquired) {\n * console.log(\"ran, got\", outcome.value);\n * } else {\n * console.log(\"skipped — someone else holds the lock\");\n * }\n */\nexport type LockOutcome<T> =\n | { acquired: true; value: T }\n | { acquired: false };\n\n/**\n * Options for `remember()` when you need more than just a TTL — e.g. attaching\n * tags to the cache-miss write, or routing the single call to a non-default driver.\n *\n * Passed in the TTL position: `cache.remember(key, { ttl: \"1h\", tags: [\"users\"] }, fn)`.\n *\n * @example\n * await cache.remember(\"user.1\", { ttl: \"1h\", tags: [\"users\"] }, () =>\n * db.users.find(1),\n * );\n */\nexport type RememberOptions = {\n /**\n * TTL applied on cache miss. Accepts seconds (number) or duration string.\n */\n ttl?: CacheTtl;\n /**\n * Tags attached to the entry created on miss. Invalidate via `cache.tags([...]).invalidate()`.\n */\n tags?: string[];\n /**\n * Per-call driver override. Routes this remember call (both read and write)\n * to the named driver without mutating `currentDriver`.\n */\n driver?: string;\n};\n\n/**\n * Rich options for the `set` call-site. Mutually exclusive with a positional TTL.\n *\n * @example\n * await cache.set(\"user:1\", user, {\n * ttl: \"1h\",\n * tags: [\"users\", \"active\"],\n * onConflict: \"create\",\n * });\n */\nexport type CacheSetOptions = {\n /**\n * Relative TTL (seconds or duration string). Mutually exclusive with `expiresAt`.\n */\n ttl?: CacheTtl;\n /**\n * Absolute expiration as a Unix epoch in milliseconds or a Date. Mutually exclusive with `ttl`.\n */\n expiresAt?: number | Date;\n /**\n * Tags attached to this entry for tag-based invalidation. Inline equivalent of\n * `cache.tags([...]).set(key, value)`.\n */\n tags?: string[];\n /**\n * Conflict resolution policy. Defaults to `\"upsert\"`.\n */\n onConflict?: CacheConflictPolicy;\n /**\n * Per-call namespace override. Applied ahead of `globalPrefix` for this write only.\n */\n namespace?: string;\n /**\n * Per-call driver override by registered name. Routes this write to a non-default driver\n * without mutating `currentDriver`.\n */\n driver?: string;\n /**\n * Optional embedding vector indexed alongside the entry for similarity retrieval\n * via `cache.similar(...)`. Drivers without similarity support throw\n * {@link CacheUnsupportedError} when this option is supplied.\n *\n * Cache is embedding-agnostic — callers compute the vector with whichever embedder\n * they like and pass the result here.\n *\n * @example\n * await cache.set(\"doc.123\", doc, { vector: await embed(doc.text), ttl: \"1d\" });\n */\n vector?: number[];\n /**\n * Freshness deadline as a Unix epoch in milliseconds. Primarily set by\n * {@link CacheSwrOptions} flow — entries with `staleAt` in the future are\n * considered \"fresh\" by `cache.swr()`; entries past `staleAt` but before\n * `expiresAt` are \"stale-but-revalidatable.\"\n *\n * Direct callers can pin this manually when bypassing `swr()`, but the\n * common path is `cache.swr(key, { freshTtl, staleTtl }, fn)`.\n */\n staleAt?: number;\n};\n\n/**\n * Options for `cache.namespace(prefix, options)` — defaults applied to every\n * write produced through the returned scope.\n *\n * Per-call options always win over scope defaults. Tags merge additively\n * (scope tags + call tags, deduped). Nested scopes inherit from the parent;\n * the child's own values override.\n */\nexport type CacheNamespaceOptions = {\n /**\n * Default TTL for writes inside this scope. Same shape as everywhere else\n * — number of seconds or a duration string (`\"1h\"`, `\"30d\"`, …).\n */\n ttl?: CacheTtl;\n /**\n * Tags auto-attached to every write inside this scope. Useful for cross-scope\n * invalidation hooks (e.g. tag every entry with `user.<id>` so a single\n * `cache.flushTag(\"user.42\")` wipes the user's footprint everywhere).\n *\n * Merged additively with per-call tags; scope tags are never replaced.\n */\n tags?: string[];\n};\n\n/**\n * Options for `cache.swr(key, options, callback)` — stale-while-revalidate.\n *\n * Two TTLs split the entry's lifecycle into three windows:\n * - `now < freshTtl` → fresh, return cached value, no upstream call.\n * - `freshTtl <= now < staleTtl` → stale-but-revalidatable, return cached\n * value immediately, kick off `callback` in the background to refresh.\n * - `now >= staleTtl` → expired, block on `callback` like a normal miss.\n *\n * Concurrent SWR callers in the stale window all return the cached value;\n * the background refresh runs exactly once per key (deduped via the\n * driver's per-key lock map).\n *\n * If the background refresh fails, the stale entry is preserved and the\n * error is logged + emitted on the `error` event. Callers that received\n * the stale value never see the failure — they got their data.\n */\nexport type CacheSwrOptions = {\n /**\n * How long the value is considered fresh. Within this window, SWR returns\n * the cached value with no upstream call. Accepts seconds or duration\n * string (`\"1m\"`, `\"30s\"`).\n */\n freshTtl: CacheTtl;\n /**\n * Total lifetime of the entry (must be greater than `freshTtl`). Between\n * `freshTtl` and `staleTtl`, SWR returns the stale value and triggers a\n * background refresh. Past `staleTtl`, SWR blocks and refetches.\n */\n staleTtl: CacheTtl;\n /**\n * Optional tags applied on the first miss-fetch and on every successful\n * background refresh. Same semantics as `CacheSetOptions.tags`.\n */\n tags?: string[];\n /**\n * Per-call driver override by registered name. Routes both the read and\n * any write/refresh through the named driver without mutating\n * `currentDriver`. Same semantics as `RememberOptions.driver`.\n */\n driver?: string;\n};\n\n/**\n * Options for `cache.similar(vector, options)` — similarity retrieval against\n * stored entries previously written with `set({ vector })`.\n */\nexport type CacheSimilarOptions = {\n /**\n * Maximum number of hits to return. Required — no implicit default keeps\n * callers from accidentally pulling the entire candidate set.\n */\n topK: number;\n /**\n * Cosine similarity floor in `[0, 1]`. Hits scoring strictly below this are\n * filtered out before `topK` truncation.\n */\n threshold?: number;\n /**\n * Optional tag filter. Only entries tagged with at least one of the given\n * tags are considered (matches the union semantics elsewhere in the package).\n */\n tags?: string[];\n};\n\n/**\n * One result from a `similar()` query — the original key, its stored value,\n * and the cosine similarity to the query vector.\n */\nexport type CacheSimilarHit<T = unknown> = {\n /**\n * The original cache key, post-`parseKey` normalization.\n */\n key: string;\n /**\n * Stored value, deep-cloned to protect the cache from accidental mutation\n * (same semantics as `get`).\n */\n value: T;\n /**\n * Cosine similarity in `[0, 1]`. Higher means more similar.\n */\n score: number;\n};\n\n/**\n * Cache key type - can be a string or an object\n */\nexport type CacheKey = string | GenericObject;\n\nexport type CacheOperationType =\n | \"fetching\"\n | \"fetched\"\n | \"caching\"\n | \"cached\"\n | \"flushing\"\n | \"flushed\"\n | \"removing\"\n | \"removed\"\n | \"clearing\"\n | \"cleared\"\n | \"expired\"\n | \"notFound\"\n | \"connecting\"\n | \"error\"\n | \"connectionFailed\"\n | \"connected\"\n | \"disconnecting\"\n | \"disconnected\";\n\n/**\n * Cache event types for observability\n */\nexport type CacheEventType =\n | \"hit\"\n | \"miss\"\n | \"set\"\n | \"removed\"\n | \"flushed\"\n | \"expired\"\n | \"connected\"\n | \"disconnected\"\n | \"error\";\n\n/**\n * Cache event data structure\n */\nexport type CacheEventData = {\n /**\n * The cache key involved in the event\n */\n key?: string;\n /**\n * The value (for set/hit events)\n */\n value?: any;\n /**\n * TTL in seconds (for set events)\n */\n ttl?: number;\n /**\n * Driver name that emitted the event\n */\n driver: string;\n /**\n * Error object (for error events)\n */\n error?: any;\n /**\n * Namespace (for namespace operations)\n */\n namespace?: string;\n};\n\n/**\n * Event handler function type\n */\nexport type CacheEventHandler = (eventData: CacheEventData) => void | Promise<void>;\n\n/**\n * Per-operation latency + counter snapshot returned by `cache.metrics()`.\n *\n * The shape is a single recursive level — the top object covers all drivers\n * and each entry under `byDriver` repeats the same fields without nesting\n * further. Use this for ad-hoc dashboards, exporters to Prometheus / StatsD,\n * or just `console.log` during development.\n */\nexport type CacheMetricsSnapshot = {\n /** Cumulative cache hits across the lifetime of the collector. */\n hits: number;\n /** Cumulative cache misses (lookups that returned null). */\n misses: number;\n /** Cumulative successful writes. */\n sets: number;\n /** Cumulative key removals. */\n removed: number;\n /** Cumulative cache errors emitted via the `error` event. */\n errors: number;\n /** `hits / (hits + misses)` — `0` when no read ops have happened yet. */\n hitRate: number;\n /** Latency percentiles in milliseconds, computed from a circular buffer. */\n latencyMs: {\n p50: number;\n p95: number;\n p99: number;\n /** Size of the underlying buffer at snapshot time (capped at the configured max). */\n samples: number;\n };\n /** Per-driver breakdowns keyed by driver name (`\"memory\"`, `\"redis\"`, …). */\n byDriver: Record<string, Omit<CacheMetricsSnapshot, \"byDriver\">>;\n /** Millisecond timestamp the collector last reset (or was instantiated). */\n startedAt: number;\n};\n\n/**\n * Tagged cache interface for working with cache tags\n */\nexport interface TaggedCacheDriver {\n /**\n * Set a value in cache with tags\n */\n set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;\n /**\n * Get a value from cache (checks tags)\n */\n get(key: CacheKey): Promise<any | null>;\n /**\n * Remove a specific key\n */\n remove(key: CacheKey): Promise<void>;\n /**\n * Invalidate (clear) all keys associated with the current tags\n */\n invalidate(): Promise<void>;\n /**\n * Flush all keys associated with the current tags\n * @deprecated Use invalidate() instead\n */\n flush(): Promise<void>;\n /**\n * Check if a key exists\n */\n has(key: CacheKey): Promise<boolean>;\n /**\n * Remember pattern with tags\n */\n remember(key: CacheKey, ttl: number, callback: () => Promise<any>): Promise<any>;\n /**\n * Pull value with tags\n */\n pull(key: CacheKey): Promise<any | null>;\n /**\n * Forever with tags\n */\n forever(key: CacheKey, value: any): Promise<any>;\n /**\n * Increment with tags\n */\n increment(key: CacheKey, value?: number): Promise<number>;\n /**\n * Decrement with tags\n */\n decrement(key: CacheKey, value?: number): Promise<number>;\n}\n\nexport type MemoryCacheOptions = {\n /**\n * The global prefix for the cache key\n */\n globalPrefix?: string | (() => string);\n /**\n * The default TTL for the cache. Accepts a number of seconds or a human-readable\n * duration string like `\"1h\"`, `\"30m\"`, `\"7d\"`.\n *\n * @default Infinity\n */\n ttl?: CacheTtl;\n /**\n * Maximum number of items in cache\n * When exceeded, least recently used items will be evicted\n *\n * @default undefined (no limit)\n */\n maxSize?: number;\n};\n\nexport type MemoryExtendedCacheOptions = MemoryCacheOptions;\n\nexport type LRUMemoryCacheOptions = {\n /**\n * The maximum number of items in the cache\n *\n * @default 1000\n */\n capacity?: number;\n /**\n * The global prefix for the cache key. Applied via `parseCacheKey`, same\n * semantics as the other drivers.\n */\n globalPrefix?: string | (() => string);\n /**\n * The default TTL for new entries. Accepts a number of seconds or a\n * human-readable duration string like `\"1h\"`, `\"30m\"`, `\"7d\"`.\n *\n * @default Infinity\n */\n ttl?: CacheTtl;\n};\n\nexport type FileCacheOptions = {\n /**\n * The global prefix for the cache key\n */\n globalPrefix?: string | (() => string);\n /**\n * The default TTL for the cache. Accepts a number of seconds or a human-readable\n * duration string like `\"1h\"`, `\"30m\"`, `\"7d\"`.\n *\n * @default 0\n */\n ttl?: CacheTtl;\n /**\n * Storage cache directory\n *\n * @default storagePath(\"cache\")\n */\n directory: string | (() => string);\n /**\n * File name\n *\n * @default cache.json\n */\n fileName?: string | (() => string);\n};\n\nexport type RedisOptions = {\n /**\n * Redis Port\n *\n * @default 6379\n */\n port?: number;\n /**\n * Redis Host\n */\n host?: string;\n /**\n * Redis Username\n */\n username?: string;\n /**\n * Redis Password\n */\n password?: string;\n /**\n * Redis URL\n *\n * If used, it will override the host and port options\n */\n url?: string;\n /**\n * Global prefix for the cache key\n */\n globalPrefix?: string | (() => string);\n /**\n * Default TTL. Accepts a number of seconds or a human-readable duration string\n * like `\"1h\"`, `\"30m\"`, `\"7d\"`.\n *\n * @default Infinity\n */\n ttl?: CacheTtl;\n /**\n * Redis client options\n */\n clientOptions?: RedisClientOptions;\n};\n\nexport type NullCacheDriverOptions = GenericObject;\n\n/**\n * Options accepted by {@link MockCacheDriver}. Same shape as the memory\n * driver — only `globalPrefix` and `ttl` apply, since the mock's storage is\n * a plain `Map` with no eviction policy.\n */\nexport type MockCacheOptions = {\n /**\n * Global key prefix, applied via `parseKey` (matches the other drivers).\n */\n globalPrefix?: string | (() => string);\n /**\n * Default TTL for new entries. Accepts seconds or a duration string.\n *\n * @default Infinity\n */\n ttl?: CacheTtl;\n};\n\n/**\n * One row in {@link MockCacheDriver.callLog} — captures every public op\n * the driver received in arrival order. Useful for behavioral assertions\n * in downstream tests (\"did my service actually invalidate the cache?\").\n */\nexport type CacheCall = {\n /**\n * Operation name as it appears on the driver contract — `\"set\"`, `\"get\"`,\n * `\"remove\"`, `\"flush\"`, `\"removeNamespace\"`, `\"has\"`, etc.\n */\n operation: string;\n /**\n * Post-`parseKey` cache key when the op is key-addressed; `undefined` for\n * keyless ops (`flush`, `connect`, `disconnect`).\n */\n key?: string;\n /**\n * Raw arguments passed to the call site, in declaration order. Lets\n * callers assert on TTLs, options objects, vector payloads, etc.\n */\n args: unknown[];\n /**\n * `Date.now()` when the call was recorded. Useful for timing-related\n * assertions (e.g. \"the second invalidation came within 100ms\").\n */\n timestamp: number;\n};\n\n/**\n * Minimal `pg`-compatible client surface the cache driver depends on.\n *\n * Both `pg.Pool` and `pg.Client` from the `pg` package satisfy this — the\n * driver only ever calls `query(text, values)`. Typing it loosely keeps\n * `pg` strictly optional: consumers without the `pg` package installed\n * never hit a missing import.\n */\nexport type PgClientLike = {\n query(text: string, values?: unknown[]): Promise<{ rows: any[]; rowCount?: number | null }>;\n};\n\nexport type PgCacheOptions = {\n /**\n * User-supplied `pg.Pool` or `pg.Client`. The driver does NOT own the\n * connection lifecycle — `cache.disconnect()` will not close this client.\n */\n client: PgClientLike;\n /**\n * Database table name. Default: `\"warlock_cache\"`.\n *\n * Sanitized: only `[A-Za-z0-9_]` are allowed; anything else throws\n * {@link CacheConfigurationError} at `setOptions` time.\n */\n table?: string;\n /**\n * Global key prefix, applied via `parseKey` (same semantics as the other drivers).\n */\n globalPrefix?: string | (() => string);\n /**\n * Default TTL for new entries. Accepts seconds or a duration string\n * (`\"1h\"`, `\"7d\"`, …).\n *\n * @default Infinity\n */\n ttl?: CacheTtl;\n /**\n * Optional pgvector configuration. When present, the driver provisions an\n * `embedding VECTOR(dimensions)` column + similarity index, and `similar()`\n * queries via the `<=>` cosine-distance operator.\n *\n * Requires the pgvector extension (`CREATE EXTENSION vector;`) — the driver\n * verifies its presence on the first vector operation and throws\n * {@link CacheConfigurationError} if missing.\n *\n * Omit this block (or remove it) to run the driver in KV-only mode —\n * `set({ vector })` and `similar()` then throw {@link CacheUnsupportedError}.\n */\n vector?: {\n /**\n * Vector dimension count. Must match the embedder you use throughout the\n * lifetime of the table. Mixing dimensions on the same table is unsupported.\n */\n dimensions: number;\n /**\n * pgvector index strategy. `hnsw` (default) is faster to query and slightly\n * slower to build; `ivfflat` is faster to build but typically slower to query.\n *\n * @default \"hnsw\"\n */\n index?: \"hnsw\" | \"ivfflat\";\n };\n};\n\nexport interface CacheDriver<ClientType, Options> {\n /**\n * The cache driver options\n */\n options: Options;\n /**\n * Cache driver name\n */\n name: string;\n /**\n * Set logging state\n */\n setLoggingState(shouldLog: boolean): any;\n /**\n * Remove all cached items by namespace\n */\n removeNamespace(namespace: string): Promise<any>;\n /**\n * Set the cache driver options\n */\n setOptions(options: Options): any;\n /**\n * Parse the key to be used in the cache\n */\n parseKey(key: CacheKey): string;\n /**\n * Set a value in the cache.\n *\n * @param key The cache key, could be an object or string\n * @param value The value to be stored in the cache\n * @param ttlOrOptions Either a TTL (seconds number, or duration string like `\"1h\"`),\n * or a full {@link CacheSetOptions} object.\n *\n * @example\n * // Positional TTL (back-compat)\n * await cache.set(\"user:1\", user, 3600);\n *\n * @example\n * // Human-readable duration\n * await cache.set(\"user:1\", user, \"1h\");\n *\n * @example\n * // Rich options\n * await cache.set(\"user:1\", user, { ttl: \"1h\", tags: [\"users\"], onConflict: \"create\" });\n */\n set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any>;\n /**\n * Get a value from the cache\n */\n get<T = any>(key: CacheKey): Promise<T | null>;\n /**\n * Remove a value from the cache\n */\n remove(key: CacheKey): Promise<void>;\n /**\n * Flush the entire cache\n */\n flush(): Promise<void>;\n /**\n * Connect to the cache driver\n */\n connect(): Promise<any>;\n /**\n * The cache client\n */\n client?: ClientType;\n /**\n * Disconnect the cache driver\n */\n disconnect(): Promise<void>;\n /**\n * Check if a key exists in the cache without fetching its value\n */\n has(key: CacheKey): Promise<boolean>;\n /**\n * Get value from cache or execute callback and cache the result.\n *\n * The second argument accepts a TTL (number of seconds or duration string like `\"1h\"`)\n * or a full {@link RememberOptions} object when you need to attach tags or route to\n * a non-default driver.\n *\n * @example\n * // Positional TTL — the common case\n * const user = await cache.remember(\"user.1\", \"1h\", () => db.users.find(1));\n *\n * @example\n * // Options form — tag the cache-miss write for bulk invalidation\n * const user = await cache.remember(\"user.1\", { ttl: \"1h\", tags: [\"users\"] }, () =>\n * db.users.find(1),\n * );\n */\n remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T>;\n /**\n * Stale-while-revalidate. Returns the cached value when fresh; returns\n * the stale value + kicks off a background refresh when within the\n * `freshTtl..staleTtl` window; blocks like a normal miss when past\n * `staleTtl`. Concurrent stale-window callers share one in-flight\n * refresh.\n *\n * Background refresh failures preserve the stale entry and emit an\n * `error` event — the stale-returning caller never sees the failure.\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 swr<T = any>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n ): Promise<T>;\n /**\n * Get a value and remove it, handing it out exactly once.\n *\n * Atomicity: across servers on `redis` (`GETDEL`) and `pg`\n * (`DELETE … RETURNING`); within the process on `memory`, `memoryExtended`,\n * `lru` and `mock`; serialized per key within the process only on `file`.\n */\n pull<T = any>(key: CacheKey): Promise<T | null>;\n /**\n * Set a value in cache permanently (no expiration)\n */\n forever<T = any>(key: CacheKey, value: T): Promise<T>;\n /**\n * Read, transform, and write a cached value without losing concurrent updates.\n *\n * The callback receives the current value (or `null` on miss) and returns the\n * next value. Returning `null` removes the key. TTL is preserved unless\n * explicitly overridden via options.\n *\n * Atomicity: across servers on `redis` and `pg` (optimistic\n * compare-and-set with retries — `fn` may run more than once under\n * contention, so keep it side-effect free). On every other driver updates\n * are serialized per key within ONE process only. `file` does not support it.\n *\n * @example\n * await cache.update<User>(\"user:1\", (current) => {\n * if (!current) return null;\n * return { ...current, lastSeen: Date.now() };\n * });\n */\n 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 /**\n * Shallow-merge a partial object into a cached value.\n *\n * If the key is missing, treats the current value as `{}`. Preserves TTL by default.\n *\n * @example\n * await cache.merge<User>(\"user:1\", { name: \"Jane\" });\n */\n merge<T extends Record<string, any> = Record<string, any>>(\n key: CacheKey,\n partial: Partial<T>,\n options?: { ttl?: CacheTtl },\n ): Promise<T>;\n /**\n * List sub-API factory — returns a {@link CacheListAccessor} bound to the given key.\n *\n * @example\n * const recent = cache.list<Event>(\"recent-events\");\n * await recent.push(event);\n * const last10 = await recent.slice(0, 10);\n */\n list<T = any>(key: CacheKey): CacheListAccessor<T>;\n /**\n * Acquire a distributed lock, run `fn`, and auto-release. Returns a\n * {@link LockOutcome} so callers can distinguish \"ran and produced this value\"\n * from \"skipped because someone else holds the lock.\"\n *\n * Built on top of `set({ onConflict: \"create\" })` — Redis-native, emulated\n * on other drivers (single-process semantics elsewhere).\n *\n * @example\n * const outcome = await cache.lock(\"lock.import\", \"5m\", async () => {\n * await runImport();\n * return \"done\";\n * });\n */\n lock<T>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\n fn: () => Promise<T>,\n ): Promise<LockOutcome<T>>;\n /**\n * Increment a numeric value in cache. A missing key starts from 0 with the\n * driver default TTL; an existing key keeps its remaining TTL.\n *\n * Atomicity: across servers on `redis` (`INCRBY`) and `pg` (single\n * upsert statement); within the process on `memory`, `memoryExtended`,\n * `lru` and `mock`; serialized per key within the process only on `file`.\n *\n * @param key The cache key\n * @param value The value to increment by (default 1)\n */\n increment(key: CacheKey, value?: number): Promise<number>;\n /**\n * Decrement a numeric value in cache\n *\n * @param key The cache key\n * @param value The value to decrement by (default 1)\n */\n decrement(key: CacheKey, value?: number): Promise<number>;\n /**\n * Get multiple values from cache at once\n */\n many(keys: CacheKey[]): Promise<any[]>;\n /**\n * Set multiple values in cache at once\n */\n setMany(items: Record<string, any>, ttl?: number): Promise<void>;\n /**\n * Register an event listener\n */\n on(event: CacheEventType, handler: CacheEventHandler): this;\n /**\n * Remove an event listener\n */\n off(event: CacheEventType, handler: CacheEventHandler): this;\n /**\n * Register a one-time event listener\n */\n once(event: CacheEventType, handler: CacheEventHandler): this;\n /**\n * Set if not exists (atomic operation)\n * Returns true if key was set, false if key already existed\n * Note: Not all drivers support this operation\n */\n setNX?(key: CacheKey, value: any, ttl?: number): Promise<boolean>;\n /**\n * Create a tagged cache instance for the given tags\n */\n tags(tags: string[]): TaggedCacheDriver;\n /**\n * Tag-index primitive: add `members` (normalized, un-prefixed cache keys)\n * to the index stored under `tagKey`. Optional — `TaggedCache` falls back to\n * a get/set array when a driver has none.\n *\n * Built-in drivers: redis → a native SET (`SADD`, atomic across servers);\n * pg → one atomic upsert of a JSON array row; memory / lru / mock → an\n * in-process set that is never evicted or expired; file (base default) →\n * a get/set array serialized per tag key in this process only.\n */\n tagAdd?(tagKey: CacheKey, members: string[]): Promise<void>;\n /**\n * Tag-index primitive: the members stored under `tagKey` (empty when none).\n */\n tagMembers?(tagKey: CacheKey): Promise<string[]>;\n /**\n * Tag-index primitive: remove `members` from `tagKey`. Members added\n * concurrently and not listed are kept.\n */\n tagRemove?(tagKey: CacheKey, members: string[]): Promise<void>;\n /**\n * Tag-index primitive: drop the whole index stored under `tagKey`.\n */\n tagDelete?(tagKey: CacheKey): Promise<void>;\n /**\n * Similarity retrieval. Returns the nearest stored entries to `vector` by\n * cosine similarity, ordered by descending score.\n *\n * Drivers without a similarity index throw {@link CacheUnsupportedError}.\n * Memory-family drivers brute-force scan in O(N) — suitable for development\n * but not for production knowledge bases beyond a few thousand entries; use\n * the `pg` driver (with pgvector) or `redis` driver (with RediSearch) instead.\n *\n * @example\n * const hits = await cache.similar(await embed(query), { topK: 5, threshold: 0.7 });\n */\n similar<T = any>(\n vector: number[],\n options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]>;\n}\n\n/**\n * Accessor for list-shaped cached values.\n *\n * Defaults to read-mutate-write semantics on non-native drivers (memory, file, LRU).\n * The Redis driver overrides this with native `LPUSH` / `RPUSH` / `LRANGE` / `LTRIM` for O(1) ops.\n */\nexport interface CacheListAccessor<T = any> {\n /**\n * Append one or more items to the tail of the list. Returns the new length.\n */\n push(...items: T[]): Promise<number>;\n /**\n * Prepend one or more items to the head of the list. Returns the new length.\n */\n unshift(...items: T[]): Promise<number>;\n /**\n * Remove and return the tail item, or `null` if the list is empty.\n */\n pop(): Promise<T | null>;\n /**\n * Remove and return the head item, or `null` if the list is empty.\n */\n shift(): Promise<T | null>;\n /**\n * Return a slice of the list. End is exclusive, mirroring `Array.prototype.slice`.\n */\n slice(start?: number, end?: number): Promise<T[]>;\n /**\n * Return the full list.\n */\n all(): Promise<T[]>;\n /**\n * Return the length of the list.\n */\n length(): Promise<number>;\n /**\n * Trim the list to the inclusive range `[start, end]`. Outside elements are dropped.\n */\n trim(start: number, end: number): Promise<void>;\n /**\n * Remove the entire list.\n */\n clear(): Promise<void>;\n}\n\n/**\n * One-shot tagged handle returned by `ScopedCacheContract.tags(...)`.\n *\n * Identical write surface to {@link TaggedCacheDriver}, except the underlying\n * scope's prefix is applied to every key and the scope's default TTL/tags\n * still flow through. Tags supplied here merge additively with scope-level\n * tags.\n */\nexport interface TaggedScopedCacheContract {\n set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;\n get<T = any>(key: CacheKey): Promise<T | null>;\n has(key: CacheKey): Promise<boolean>;\n remove(key: CacheKey): Promise<void>;\n pull<T = any>(key: CacheKey): Promise<T | null>;\n forever<T = any>(key: CacheKey, value: T): Promise<T>;\n setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean>;\n remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T>;\n increment(key: CacheKey, value?: number): Promise<number>;\n decrement(key: CacheKey, value?: number): Promise<number>;\n /**\n * Invalidate every entry tagged with the union of scope tags + handle tags.\n * Forwarded to the underlying tag index — tags are global, scope-agnostic.\n */\n invalidate(): Promise<void>;\n}\n\n/**\n * Scoped view over the cache. Returned by `cache.namespace(prefix, options?)`.\n *\n * A scope prepends its `prefix` to every key, applies optional default TTL/tags\n * to every write, and forwards everything else to the underlying source. It\n * stores nothing of its own — purely a convenience wrapper.\n *\n * @example\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\n * await chat.set(\"messages.1\", msg); // → \"chats.10.messages.1\", TTL 30d\n * await chat.set(\"draft\", d, { ttl: \"1h\" }); // per-call ttl wins\n * await chat.clear(); // wipe the whole scope\n */\nexport interface ScopedCacheContract {\n /** The fully-qualified prefix this scope prepends to every key. */\n readonly prefix: string;\n\n /**\n * Nested scope. Inherits the parent's defaults; child's own options override.\n *\n * @example\n * const chat = cache.namespace(\"chats.10\", { ttl: \"30d\" });\n * const typing = chat.namespace(\"typing\", { ttl: \"5s\" });\n * // typing.prefix === \"chats.10.typing\"\n */\n namespace(prefix: string, options?: CacheNamespaceOptions): ScopedCacheContract;\n\n /**\n * One-shot tagged write handle. Tags merge additively with scope defaults.\n */\n tags(tags: string[]): TaggedScopedCacheContract;\n\n /** Wipe every entry under this scope's prefix. Sugar for `removeNamespace(prefix)`. */\n clear(): Promise<void>;\n\n // Reads\n get<T = any>(key: CacheKey): Promise<T | null>;\n has(key: CacheKey): Promise<boolean>;\n many(keys: CacheKey[]): Promise<any[]>;\n pull<T = any>(key: CacheKey): Promise<T | null>;\n\n // Writes\n set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;\n setMany(items: Record<string, any>, ttl?: number): Promise<void>;\n setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean>;\n forever<T = any>(key: CacheKey, value: T): Promise<T>;\n remove(key: CacheKey): Promise<void>;\n\n // Read-or-compute\n remember<T = any>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | RememberOptions,\n callback: () => Promise<T>,\n ): Promise<T>;\n swr<T = any>(\n key: CacheKey,\n options: CacheSwrOptions,\n callback: () => Promise<T>,\n ): Promise<T>;\n\n // Mutations\n increment(key: CacheKey, value?: number): Promise<number>;\n decrement(key: CacheKey, value?: number): Promise<number>;\n 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 merge<T extends Record<string, any> = Record<string, any>>(\n key: CacheKey,\n partial: Partial<T>,\n options?: { ttl?: CacheTtl },\n ): Promise<T>;\n\n // Structured accessors\n list<T = any>(key: CacheKey): CacheListAccessor<T>;\n\n // Coordination\n lock<T>(\n key: CacheKey,\n ttlOrOptions: CacheTtl | Omit<LockOptions, \"driver\">,\n fn: () => Promise<T>,\n ): Promise<LockOutcome<T>>;\n\n // Similarity (delegated; throws CacheUnsupportedError if the driver lacks it)\n similar<T = any>(\n vector: number[],\n options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]>;\n}\n\nexport type CacheData = {\n /**\n * Value stored in the cache\n */\n data: any;\n /**\n * The expiration date in milliseconds\n */\n expiresAt?: number;\n /**\n * Time to live in seconds\n */\n ttl?: number;\n /**\n * Freshness deadline as a millisecond timestamp. Used by `swr()` — entries\n * with `staleAt` in the future are \"fresh\"; past `staleAt` but before\n * `expiresAt` are \"stale-but-revalidatable\" and trigger a background\n * refresh on the next read. Optional — entries written through plain\n * `set()` skip this field entirely and `swr()` treats them as always-fresh.\n */\n staleAt?: number;\n};\n\nexport type DriverClass = new () => CacheDriver<any, any>;\n\ntype DefaultDrivers =\n | \"redis\"\n | \"file\"\n | \"memory\"\n | \"memoryExtended\"\n | \"null\"\n | \"lru\"\n | \"pg\"\n | \"mock\";\n\ntype MergeWithDefaultDrivers<T> = T extends undefined ? DefaultDrivers : DefaultDrivers | T;\n\nexport type CacheConfigurations<\n T extends string | undefined = undefined,\n DriverName = MergeWithDefaultDrivers<T>,\n> = {\n /**\n * The default cache driver name\n */\n default?: DriverName;\n /**\n * Determine whether to log or not\n *\n * @default true\n */\n logging?: boolean;\n /**\n * The cache drivers list\n */\n drivers: {\n redis?: typeof RedisCacheDriver;\n file?: typeof FileCacheDriver;\n null?: typeof NullCacheDriver;\n memory?: typeof MemoryCacheDriver;\n memoryExtended?: typeof MemoryExtendedCacheDriver;\n lru?: typeof LRUMemoryCacheDriver;\n pg?: typeof PgCacheDriver;\n mock?: typeof MockCacheDriver;\n } & {\n [key in Extract<T, string>]?: typeof BaseCacheDriver<any, any> | undefined;\n };\n /**\n * The cache driver options\n */\n options: {\n redis?: RedisOptions;\n file?: FileCacheOptions;\n memory?: MemoryCacheOptions;\n memoryExtended?: MemoryExtendedCacheOptions;\n null?: NullCacheDriverOptions;\n lru?: LRUMemoryCacheOptions;\n pg?: PgCacheOptions;\n mock?: MockCacheOptions;\n } & {\n [key in Extract<T, string>]?: GenericObject;\n };\n};\n"],"mappings":";;;;AAiBA,IAAa,aAAb,cAAgC,MAAM;CACpC,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,uBAAb,cAA0C,WAAW;CACnD,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,0BAAb,cAA6C,WAAW;CACtD,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,iCAAb,cAAoD,WAAW;CAC7D,AAAO,YACL,UAAkB,wEAClB;EACA,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;;;;AAQA,IAAa,wBAAb,cAA2C,WAAW;CACpD,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,wBAAb,cAA2C,WAAW;CACpD,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF"}
package/esm/utils.d.mts CHANGED
@@ -11,14 +11,20 @@ declare function parseCacheKey(key: CacheKey, options?: {
11
11
  * Parse a TTL value into seconds.
12
12
  *
13
13
  * Accepts:
14
- * - a number (already in seconds) — returned unchanged
14
+ * - a number (seconds) — fractions are rounded UP to whole seconds
15
+ * (so Redis `EX` never receives a fraction); `0` means "no expiry"
15
16
  * - `Infinity` — no expiration, returned unchanged
16
- * - a human-readable duration string (e.g. `"1h"`, `"30m"`, `"7d"`) — parsed via `ms` then converted to seconds
17
+ * - a bare numeric string (e.g. `"3600"`, from env vars) — treated as seconds
18
+ * - a human-readable duration string (e.g. `"1h"`, `"30m"`, `"7d"`) — parsed via `ms`,
19
+ * then converted to seconds, rounding up
17
20
  *
18
- * Throws `CacheConfigurationError` on unparseable strings or negative numbers.
21
+ * Throws `CacheConfigurationError` on NaN, negative numbers, or unparseable strings.
19
22
  *
20
23
  * @example
21
24
  * parseTtl(3600); // 3600
25
+ * parseTtl(0.5); // 1
26
+ * parseTtl("3600"); // 3600
27
+ * parseTtl("500ms"); // 1
22
28
  * parseTtl("1h"); // 3600
23
29
  * parseTtl("7d"); // 604800
24
30
  * parseTtl(Infinity); // Infinity
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.mts","names":[],"sources":["../../../../../../cache/src/utils.ts"],"mappings":";;;;;AAQA;iBAAgB,aAAA,CACd,GAAA,EAAK,QAAQ,EACb,OAAA;EAAW,YAAA;AAAA;;;;;;AAA6C;AA+B1D;;;;AAAwC;AAsCxC;;;;AAAuD;iBAtCvC,QAAA,CAAS,KAAe,EAAR,QAAQ;;;;;;;;;;;;;iBAsCxB,cAAA,CAAe,SAAwB,WAAJ,IAAI;AA+CvD;;;;;;;;;AAAA,iBAzBgB,kBAAA,CACd,KAAA,GAAQ,QAAA,GAAW,eAAA,GAClB,eAAA;;;;AAyBe;AAuBlB;;;;;;;iBAzBgB,0BAAA,CACd,KAAA,GAAQ,QAAA,GAAW,eAAA,GAClB,eAAA;;;AA0Be;AAiClB;;;;AACU;AA4BV;;;iBAjEgB,UAAA,CACd,GAAA,EAAK,QAAA,cACL,SAAA,WAAoB,IAAI,cACxB,QAAA;;;;;;;;AAiEE;AA0BJ;;;;AAAyD;AAwDzD;iBAlHgB,YAAA,IACX,KAAK;;;AAiHuC;AAWjD;;;;;;iBAhGgB,UAAA;EAAuB,IAAA;AAAA,GACrC,OAAA,EAAS,CAAA,EACT,SAAA,aACC,CAAC;AA4GJ;;;;;;;;;;;;;;;AAAA,iBAlFgB,gBAAA,CAAiB,CAAA,YAAa,CAAW;;;;;iBAwDzC,iBAAA,CAAkB,OAAe;;;;;;;;iBAWjC,aAAA,CAAc,KAAA;EAAmB,OAAA;EAAiB,IAAA;AAAA;AAAA,aAetD,SAAA;;;;EAIV,SAAA;;;;EAIA,QAAA;;;;EAIA,QAAA;;;;EAIA,OAAA;;;;EAIA,QAAA;;;;EAIA,UAAA;;;;EAIA,SAAA;;;;EAIA,UAAA;;;;EAIA,UAAA;;;;EAIA,QAAA;AAAA"}
1
+ {"version":3,"file":"utils.d.mts","names":[],"sources":["../../../../../../cache/src/utils.ts"],"mappings":";;;;;AAQA;iBAAgB,aAAA,CACd,GAAA,EAAK,QAAQ,EACb,OAAA;EAAW,YAAA;AAAA;;;;;;AAA6C;AAqC1D;;;;AAAwC;AAoDxC;;;;AAAuD;AAsBvD;;;;;;iBA1EgB,QAAA,CAAS,KAAe,EAAR,QAAQ;;;;;;;AA4EtB;AAuBlB;;;;;iBA/CgB,cAAA,CAAe,SAAwB,WAAJ,IAAI;;;;;;;;AAiDrC;AAuBlB;iBAlDgB,kBAAA,CACd,KAAA,GAAQ,QAAA,GAAW,eAAA,GAClB,eAAA;;;;;;;;;AAmDe;AAiClB;;iBA7DgB,0BAAA,CACd,KAAA,GAAQ,QAAA,GAAW,eAAA,GAClB,eAAA;;AA4DO;AA4BV;;;;;;;;;iBAjEgB,UAAA,CACd,GAAA,EAAK,QAAA,cACL,SAAA,WAAoB,IAAI,cACxB,QAAA;;AAiEE;AA0BJ;;;;AAAyD;AAwDzD;;;;AAAiD;AAWjD;;iBA7HgB,YAAA,IACX,KAAK;;;;;;AA4H4D;AAetE;;;iBA/GgB,UAAA;EAAuB,IAAA;AAAA,GACrC,OAAA,EAAS,CAAA,EACT,SAAA,aACC,CAAC;;;;;;;;;;AAoJM;;;;;;iBA1HM,gBAAA,CAAiB,CAAA,YAAa,CAAW;;;;;iBAwDzC,iBAAA,CAAkB,OAAe;;;;;;;;iBAWjC,aAAA,CAAc,KAAA;EAAmB,OAAA;EAAiB,IAAA;AAAA;AAAA,aAetD,SAAA;;;;EAIV,SAAA;;;;EAIA,QAAA;;;;EAIA,QAAA;;;;EAIA,OAAA;;;;EAIA,QAAA;;;;EAIA,UAAA;;;;EAIA,SAAA;;;;EAIA,UAAA;;;;EAIA,UAAA;;;;EAIA,QAAA;AAAA"}
package/esm/utils.mjs CHANGED
@@ -16,27 +16,37 @@ function parseCacheKey(key, options = {}) {
16
16
  * Parse a TTL value into seconds.
17
17
  *
18
18
  * Accepts:
19
- * - a number (already in seconds) — returned unchanged
19
+ * - a number (seconds) — fractions are rounded UP to whole seconds
20
+ * (so Redis `EX` never receives a fraction); `0` means "no expiry"
20
21
  * - `Infinity` — no expiration, returned unchanged
21
- * - a human-readable duration string (e.g. `"1h"`, `"30m"`, `"7d"`) — parsed via `ms` then converted to seconds
22
+ * - a bare numeric string (e.g. `"3600"`, from env vars) — treated as seconds
23
+ * - a human-readable duration string (e.g. `"1h"`, `"30m"`, `"7d"`) — parsed via `ms`,
24
+ * then converted to seconds, rounding up
22
25
  *
23
- * Throws `CacheConfigurationError` on unparseable strings or negative numbers.
26
+ * Throws `CacheConfigurationError` on NaN, negative numbers, or unparseable strings.
24
27
  *
25
28
  * @example
26
29
  * parseTtl(3600); // 3600
30
+ * parseTtl(0.5); // 1
31
+ * parseTtl("3600"); // 3600
32
+ * parseTtl("500ms"); // 1
27
33
  * parseTtl("1h"); // 3600
28
34
  * parseTtl("7d"); // 604800
29
35
  * parseTtl(Infinity); // Infinity
30
36
  */
31
37
  function parseTtl(input) {
32
38
  if (typeof input === "number") {
39
+ if (Number.isNaN(input)) throw new CacheConfigurationError("Invalid TTL: NaN.");
33
40
  if (input < 0) throw new CacheConfigurationError(`Invalid TTL: negative number (${input}).`);
34
- return input;
41
+ return Math.ceil(input);
35
42
  }
36
43
  if (typeof input !== "string" || input.trim() === "") throw new CacheConfigurationError(`Invalid TTL: expected number or duration string, got ${typeof input}.`);
37
- const milliseconds = ms(input);
44
+ const trimmed = input.trim();
45
+ if (/^\d+(\.\d+)?$/.test(trimmed)) return Math.ceil(Number(trimmed));
46
+ const milliseconds = ms(trimmed);
38
47
  if (milliseconds === void 0 || Number.isNaN(milliseconds)) throw new CacheConfigurationError(`Invalid TTL duration string: "${input}". Expected forms like "1h", "30m", "7d".`);
39
- return Math.floor(milliseconds / 1e3);
48
+ if (milliseconds < 0) throw new CacheConfigurationError(`Invalid TTL: negative duration ("${input}").`);
49
+ return Math.ceil(milliseconds / 1e3);
40
50
  }
41
51
  /**
42
52
  * Convert an absolute `expiresAt` (Date or epoch milliseconds) into a
package/esm/utils.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","names":[],"sources":["../../../../../../cache/src/utils.ts"],"sourcesContent":["import { rtrim } from \"@mongez/reinforcements\";\r\nimport ms, { type StringValue } from \"ms\";\r\nimport type { CacheKey, CacheSetOptions, CacheTtl, RememberOptions } from \"./types\";\r\nimport { CacheConfigurationError } from \"./types\";\r\n\r\n/**\r\n * Make a proper key for the cache\r\n */\r\nexport function parseCacheKey(\r\n key: CacheKey,\r\n options: { globalPrefix?: string | (() => string) } = {},\r\n): string {\r\n if (typeof key === \"object\") {\r\n key = JSON.stringify(key);\r\n }\r\n\r\n // remove any curly braces and double quotes along with []\r\n key = key.replace(/[{}\"[\\]]/g, \"\").replaceAll(/[:,]/g, \".\");\r\n\r\n const cachePrefix =\r\n typeof options.globalPrefix === \"function\" ? options.globalPrefix() : options.globalPrefix;\r\n\r\n return rtrim(String(cachePrefix ? rtrim(cachePrefix, \".\") + \".\" + key : key), \".\");\r\n}\r\n\r\n/**\r\n * Parse a TTL value into seconds.\r\n *\r\n * Accepts:\r\n * - a number (already in seconds) — returned unchanged\r\n * - `Infinity` — no expiration, returned unchanged\r\n * - a human-readable duration string (e.g. `\"1h\"`, `\"30m\"`, `\"7d\"`) — parsed via `ms` then converted to seconds\r\n *\r\n * Throws `CacheConfigurationError` on unparseable strings or negative numbers.\r\n *\r\n * @example\r\n * parseTtl(3600); // 3600\r\n * parseTtl(\"1h\"); // 3600\r\n * parseTtl(\"7d\"); // 604800\r\n * parseTtl(Infinity); // Infinity\r\n */\r\nexport function parseTtl(input: CacheTtl): number {\r\n if (typeof input === \"number\") {\r\n if (input < 0) {\r\n throw new CacheConfigurationError(`Invalid TTL: negative number (${input}).`);\r\n }\r\n\r\n return input;\r\n }\r\n\r\n if (typeof input !== \"string\" || input.trim() === \"\") {\r\n throw new CacheConfigurationError(\r\n `Invalid TTL: expected number or duration string, got ${typeof input}.`,\r\n );\r\n }\r\n\r\n const milliseconds = ms(input as StringValue);\r\n\r\n if (milliseconds === undefined || Number.isNaN(milliseconds)) {\r\n throw new CacheConfigurationError(\r\n `Invalid TTL duration string: \"${input}\". Expected forms like \"1h\", \"30m\", \"7d\".`,\r\n );\r\n }\r\n\r\n return Math.floor(milliseconds / 1000);\r\n}\r\n\r\n/**\r\n * Convert an absolute `expiresAt` (Date or epoch milliseconds) into a\r\n * relative TTL in seconds.\r\n *\r\n * Throws {@link CacheConfigurationError} when the deadline is in the past —\r\n * the caller almost certainly has a bug (stale timestamp, wrong unit, etc.)\r\n * and silently storing an already-expired entry would hide it.\r\n *\r\n * @example\r\n * expiresAtToTtl(new Date(Date.now() + 60_000)); // ~60\r\n * expiresAtToTtl(Date.now() + 30 * 60 * 1000); // ~1800\r\n */\r\nexport function expiresAtToTtl(expiresAt: number | Date): number {\r\n const deadline = expiresAt instanceof Date ? expiresAt.getTime() : expiresAt;\r\n const relativeMs = deadline - Date.now();\r\n\r\n if (relativeMs <= 0) {\r\n throw new CacheConfigurationError(\r\n `\\`expiresAt\\` must be in the future; got ${new Date(deadline).toISOString()}.`,\r\n );\r\n }\r\n\r\n return Math.ceil(relativeMs / 1000);\r\n}\r\n\r\n/**\r\n * Coerce the polymorphic 3rd `set` argument into a uniform `CacheSetOptions`\r\n * shape. Lets callers (and `BaseCacheDriver.resolveSetOptions`) skip per-shape\r\n * branching.\r\n *\r\n * - `undefined` / `null` → `{}` (resolves to driver-level defaults later)\r\n * - `number` / `string` (positional TTL) → `{ ttl }`\r\n * - already an options object → returned as-is\r\n */\r\nexport function normalizeToOptions(\r\n input?: CacheTtl | CacheSetOptions,\r\n): CacheSetOptions {\r\n if (input === undefined || input === null) {\r\n return {};\r\n }\r\n\r\n if (typeof input === \"number\" || typeof input === \"string\") {\r\n return { ttl: input };\r\n }\r\n\r\n return input;\r\n}\r\n\r\n/**\r\n * Sibling of {@link normalizeToOptions} for the `remember()` call site, where\r\n * the polymorphic 2nd argument is `CacheTtl | RememberOptions` (no `expiresAt`,\r\n * no `onConflict`). Returns the same shape so callers can `{ ...opts, ... }`\r\n * without branching.\r\n *\r\n * @example\r\n * normalizeToRememberOptions(60); // { ttl: 60 }\r\n * normalizeToRememberOptions(\"1h\"); // { ttl: \"1h\" }\r\n * normalizeToRememberOptions({ ttl: \"1h\", tags: [\"x\"] }); // returned as-is\r\n */\r\nexport function normalizeToRememberOptions(\r\n input?: CacheTtl | RememberOptions,\r\n): RememberOptions {\r\n if (input === undefined || input === null) {\r\n return {};\r\n }\r\n\r\n if (typeof input === \"number\" || typeof input === \"string\") {\r\n return { ttl: input };\r\n }\r\n\r\n return input;\r\n}\r\n\r\n/**\r\n * Resolve the final TTL in seconds for a `set` call. Precedence:\r\n *\r\n * 1. Caller's `ttl` (number or duration string) wins.\r\n * 2. Otherwise, caller's `expiresAt` is converted to relative seconds.\r\n * 3. Otherwise, `fallback` is used (driver-level default — typically\r\n * `Infinity` when no default is configured, meaning \"never expires\").\r\n *\r\n * Throws {@link CacheConfigurationError} when `ttl` and `expiresAt` are\r\n * supplied together (mutually exclusive).\r\n */\r\nexport function resolveTtl(\r\n ttl: CacheTtl | undefined,\r\n expiresAt: number | Date | undefined,\r\n fallback: number,\r\n): number {\r\n if (ttl !== undefined && expiresAt !== undefined) {\r\n throw new CacheConfigurationError(\r\n \"Cache set options cannot specify both `ttl` and `expiresAt` — choose one.\",\r\n );\r\n }\r\n\r\n if (ttl !== undefined) {\r\n return parseTtl(ttl);\r\n }\r\n\r\n if (expiresAt !== undefined) {\r\n return expiresAtToTtl(expiresAt);\r\n }\r\n\r\n return fallback;\r\n}\r\n\r\n/**\r\n * Combine any number of tag lists into a single deduped array, dropping\r\n * `undefined`/empty entries. Returns `undefined` when no tags survive — lets\r\n * callers skip emitting empty `tags: []` into option payloads.\r\n *\r\n * Used by scoped-cache merging where scope tags + handle tags + per-call tags\r\n * must union additively without duplicates.\r\n *\r\n * @example\r\n * mergeTagSets([\"a\", \"b\"], [\"b\", \"c\"]); // [\"a\", \"b\", \"c\"]\r\n * mergeTagSets(undefined, [\"x\"]); // [\"x\"]\r\n * mergeTagSets(undefined, undefined); // undefined\r\n * mergeTagSets([], []); // undefined\r\n */\r\nexport function mergeTagSets(\r\n ...lists: (string[] | undefined)[]\r\n): string[] | undefined {\r\n const flat: string[] = [];\r\n\r\n for (const list of lists) {\r\n if (!list || list.length === 0) {\r\n continue;\r\n }\r\n\r\n flat.push(...list);\r\n }\r\n\r\n if (flat.length === 0) {\r\n return undefined;\r\n }\r\n\r\n return Array.from(new Set(flat));\r\n}\r\n\r\n/**\r\n * Add extra tags to any option-bag that already shapes `tags?: string[]`.\r\n * Pure — clones the input shape, never mutates. Tags are appended (caller\r\n * is responsible for de-duplication if needed; pair with {@link mergeTagSets}).\r\n *\r\n * @example\r\n * injectTags({ ttl: \"1h\" }, [\"unread\"]); // { ttl: \"1h\", tags: [\"unread\"] }\r\n * injectTags({ tags: [\"a\"] }, [\"b\"]); // { tags: [\"a\", \"b\"] }\r\n */\r\nexport function injectTags<T extends { tags?: string[] }>(\r\n options: T,\r\n extraTags: string[],\r\n): T {\r\n if (extraTags.length === 0) {\r\n return options;\r\n }\r\n\r\n return {\r\n ...options,\r\n tags: [...(options.tags ?? []), ...extraTags],\r\n };\r\n}\r\n\r\n/**\r\n * Cosine similarity between two equal-length numeric vectors.\r\n *\r\n * Returns a value in `[-1, 1]` where `1` means perfectly aligned, `0` means\r\n * orthogonal, and `-1` means opposing. For typical embedding spaces (where\r\n * vectors live in the positive cone) the practical range is `[0, 1]`.\r\n *\r\n * Throws {@link CacheConfigurationError} on dimension mismatch — fail loud at\r\n * the call site rather than silently returning a misleading score. A zero-norm\r\n * vector on either side returns `0` (no defined direction to compare).\r\n *\r\n * @example\r\n * cosineSimilarity([1, 0, 0], [1, 0, 0]); // 1\r\n * cosineSimilarity([1, 0, 0], [0, 1, 0]); // 0\r\n */\r\nexport function cosineSimilarity(a: number[], b: number[]): number {\r\n if (a.length !== b.length) {\r\n throw new CacheConfigurationError(\r\n `Vector dimension mismatch: got ${a.length} and ${b.length}.`,\r\n );\r\n }\r\n\r\n if (a.length === 0) {\r\n throw new CacheConfigurationError(\r\n \"Vector dimension mismatch: empty vector cannot be compared.\",\r\n );\r\n }\r\n\r\n let dot = 0;\r\n let normA = 0;\r\n let normB = 0;\r\n\r\n for (let i = 0; i < a.length; i++) {\r\n const x = a[i];\r\n const y = b[i];\r\n\r\n if (x === undefined || y === undefined) {\r\n // Unreachable: both guards above establish `a.length === b.length` and\r\n // a non-zero length, so every index in range exists on both sides. Said\r\n // out loud rather than asserted with `as number`, because the two guards\r\n // that make it true are the kind of thing a later edit removes — and if\r\n // one ever did, an assertion would let `undefined` into the arithmetic\r\n // and return NaN as a similarity SCORE. A silently wrong ranking is\r\n // worse than a stopped one.\r\n break;\r\n }\r\n\r\n dot += x * y;\r\n normA += x * x;\r\n normB += y * y;\r\n }\r\n\r\n if (normA === 0 || normB === 0) {\r\n return 0;\r\n }\r\n\r\n return dot / (Math.sqrt(normA) * Math.sqrt(normB));\r\n}\r\n\r\n/**\r\n * Matches userinfo credentials embedded in a connection URL\r\n * (e.g. `redis://user:pass@host`). Node/Redis/Postgres client errors\r\n * frequently echo the target connection string — including the password —\r\n * back in `error.message` on connection failure.\r\n */\r\nconst CREDENTIALS_IN_URL = /(:\\/\\/)[^\\s/@]+:[^\\s/@]+@/g;\r\n\r\n/**\r\n * Strip embedded `user:pass@` credentials from a string before it reaches\r\n * any log sink.\r\n */\r\nexport function redactCredentials(message: string): string {\r\n return message.replace(CREDENTIALS_IN_URL, \"$1[REDACTED]@\");\r\n}\r\n\r\n/**\r\n * Reduce an unknown thrown value to a log-safe `{ message, code? }` shape.\r\n * Callers must never log the raw error object itself — it may carry the\r\n * connection URL (with password) in the top-level message, `cause`, or\r\n * driver-specific fields, all of which `console.log`/`util.inspect` would\r\n * still print in full.\r\n */\r\nexport function safeErrorInfo(error: unknown): { message: string; code?: string } {\r\n if (error instanceof Error) {\r\n const info: { message: string; code?: string } = {\r\n message: redactCredentials(error.message),\r\n };\r\n\r\n const code = (error as any).code;\r\n if (code) info.code = String(code);\r\n\r\n return info;\r\n }\r\n\r\n return { message: redactCredentials(String(error)) };\r\n}\r\n\r\nexport enum CACHE_FOR {\r\n /**\r\n * Cache for 30 Minutes (in seconds)\r\n */\r\n HALF_HOUR = 1800,\r\n /**\r\n * Cache for 1 Hour (in seconds)\r\n */\r\n ONE_HOUR = 3600,\r\n /**\r\n * Cache for 12 Hours (in seconds)\r\n */\r\n HALF_DAY = 43200,\r\n /**\r\n * Cache for 24 Hours (in seconds)\r\n */\r\n ONE_DAY = 86400,\r\n /**\r\n * Cache for 7 Days (in seconds)\r\n */\r\n ONE_WEEK = 604800,\r\n /**\r\n * Cache for 15 Days (in seconds)\r\n */\r\n HALF_MONTH = 1296000,\r\n /**\r\n * Cache for 30 Days (in seconds)\r\n */\r\n ONE_MONTH = 2592000,\r\n /**\r\n * Cache for 60 Days (in seconds)\r\n */\r\n TWO_MONTHS = 5184000,\r\n /**\r\n * Cache for 180 Days (in seconds)\r\n */\r\n SIX_MONTHS = 15768000,\r\n /**\r\n * Cache for 365 Days (in seconds)\r\n */\r\n ONE_YEAR = 31536000,\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.mjs","names":[],"sources":["../../../../../../cache/src/utils.ts"],"sourcesContent":["import { rtrim } from \"@mongez/reinforcements\";\r\nimport ms, { type StringValue } from \"ms\";\r\nimport type { CacheKey, CacheSetOptions, CacheTtl, RememberOptions } from \"./types\";\r\nimport { CacheConfigurationError } from \"./types\";\r\n\r\n/**\r\n * Make a proper key for the cache\r\n */\r\nexport function parseCacheKey(\r\n key: CacheKey,\r\n options: { globalPrefix?: string | (() => string) } = {},\r\n): string {\r\n if (typeof key === \"object\") {\r\n key = JSON.stringify(key);\r\n }\r\n\r\n // remove any curly braces and double quotes along with []\r\n key = key.replace(/[{}\"[\\]]/g, \"\").replaceAll(/[:,]/g, \".\");\r\n\r\n const cachePrefix =\r\n typeof options.globalPrefix === \"function\" ? options.globalPrefix() : options.globalPrefix;\r\n\r\n return rtrim(String(cachePrefix ? rtrim(cachePrefix, \".\") + \".\" + key : key), \".\");\r\n}\r\n\r\n/**\r\n * Parse a TTL value into seconds.\r\n *\r\n * Accepts:\r\n * - a number (seconds) — fractions are rounded UP to whole seconds\r\n * (so Redis `EX` never receives a fraction); `0` means \"no expiry\"\r\n * - `Infinity` — no expiration, returned unchanged\r\n * - a bare numeric string (e.g. `\"3600\"`, from env vars) — treated as seconds\r\n * - a human-readable duration string (e.g. `\"1h\"`, `\"30m\"`, `\"7d\"`) — parsed via `ms`,\r\n * then converted to seconds, rounding up\r\n *\r\n * Throws `CacheConfigurationError` on NaN, negative numbers, or unparseable strings.\r\n *\r\n * @example\r\n * parseTtl(3600); // 3600\r\n * parseTtl(0.5); // 1\r\n * parseTtl(\"3600\"); // 3600\r\n * parseTtl(\"500ms\"); // 1\r\n * parseTtl(\"1h\"); // 3600\r\n * parseTtl(\"7d\"); // 604800\r\n * parseTtl(Infinity); // Infinity\r\n */\r\nexport function parseTtl(input: CacheTtl): number {\r\n if (typeof input === \"number\") {\r\n if (Number.isNaN(input)) {\r\n throw new CacheConfigurationError(\"Invalid TTL: NaN.\");\r\n }\r\n\r\n if (input < 0) {\r\n throw new CacheConfigurationError(`Invalid TTL: negative number (${input}).`);\r\n }\r\n\r\n return Math.ceil(input);\r\n }\r\n\r\n if (typeof input !== \"string\" || input.trim() === \"\") {\r\n throw new CacheConfigurationError(\r\n `Invalid TTL: expected number or duration string, got ${typeof input}.`,\r\n );\r\n }\r\n\r\n const trimmed = input.trim();\r\n\r\n if (/^\\d+(\\.\\d+)?$/.test(trimmed)) {\r\n return Math.ceil(Number(trimmed));\r\n }\r\n\r\n const milliseconds = ms(trimmed as StringValue);\r\n\r\n if (milliseconds === undefined || Number.isNaN(milliseconds)) {\r\n throw new CacheConfigurationError(\r\n `Invalid TTL duration string: \"${input}\". Expected forms like \"1h\", \"30m\", \"7d\".`,\r\n );\r\n }\r\n\r\n if (milliseconds < 0) {\r\n throw new CacheConfigurationError(`Invalid TTL: negative duration (\"${input}\").`);\r\n }\r\n\r\n return Math.ceil(milliseconds / 1000);\r\n}\r\n\r\n/**\r\n * Convert an absolute `expiresAt` (Date or epoch milliseconds) into a\r\n * relative TTL in seconds.\r\n *\r\n * Throws {@link CacheConfigurationError} when the deadline is in the past —\r\n * the caller almost certainly has a bug (stale timestamp, wrong unit, etc.)\r\n * and silently storing an already-expired entry would hide it.\r\n *\r\n * @example\r\n * expiresAtToTtl(new Date(Date.now() + 60_000)); // ~60\r\n * expiresAtToTtl(Date.now() + 30 * 60 * 1000); // ~1800\r\n */\r\nexport function expiresAtToTtl(expiresAt: number | Date): number {\r\n const deadline = expiresAt instanceof Date ? expiresAt.getTime() : expiresAt;\r\n const relativeMs = deadline - Date.now();\r\n\r\n if (relativeMs <= 0) {\r\n throw new CacheConfigurationError(\r\n `\\`expiresAt\\` must be in the future; got ${new Date(deadline).toISOString()}.`,\r\n );\r\n }\r\n\r\n return Math.ceil(relativeMs / 1000);\r\n}\r\n\r\n/**\r\n * Coerce the polymorphic 3rd `set` argument into a uniform `CacheSetOptions`\r\n * shape. Lets callers (and `BaseCacheDriver.resolveSetOptions`) skip per-shape\r\n * branching.\r\n *\r\n * - `undefined` / `null` → `{}` (resolves to driver-level defaults later)\r\n * - `number` / `string` (positional TTL) → `{ ttl }`\r\n * - already an options object → returned as-is\r\n */\r\nexport function normalizeToOptions(\r\n input?: CacheTtl | CacheSetOptions,\r\n): CacheSetOptions {\r\n if (input === undefined || input === null) {\r\n return {};\r\n }\r\n\r\n if (typeof input === \"number\" || typeof input === \"string\") {\r\n return { ttl: input };\r\n }\r\n\r\n return input;\r\n}\r\n\r\n/**\r\n * Sibling of {@link normalizeToOptions} for the `remember()` call site, where\r\n * the polymorphic 2nd argument is `CacheTtl | RememberOptions` (no `expiresAt`,\r\n * no `onConflict`). Returns the same shape so callers can `{ ...opts, ... }`\r\n * without branching.\r\n *\r\n * @example\r\n * normalizeToRememberOptions(60); // { ttl: 60 }\r\n * normalizeToRememberOptions(\"1h\"); // { ttl: \"1h\" }\r\n * normalizeToRememberOptions({ ttl: \"1h\", tags: [\"x\"] }); // returned as-is\r\n */\r\nexport function normalizeToRememberOptions(\r\n input?: CacheTtl | RememberOptions,\r\n): RememberOptions {\r\n if (input === undefined || input === null) {\r\n return {};\r\n }\r\n\r\n if (typeof input === \"number\" || typeof input === \"string\") {\r\n return { ttl: input };\r\n }\r\n\r\n return input;\r\n}\r\n\r\n/**\r\n * Resolve the final TTL in seconds for a `set` call. Precedence:\r\n *\r\n * 1. Caller's `ttl` (number or duration string) wins.\r\n * 2. Otherwise, caller's `expiresAt` is converted to relative seconds.\r\n * 3. Otherwise, `fallback` is used (driver-level default — typically\r\n * `Infinity` when no default is configured, meaning \"never expires\").\r\n *\r\n * Throws {@link CacheConfigurationError} when `ttl` and `expiresAt` are\r\n * supplied together (mutually exclusive).\r\n */\r\nexport function resolveTtl(\r\n ttl: CacheTtl | undefined,\r\n expiresAt: number | Date | undefined,\r\n fallback: number,\r\n): number {\r\n if (ttl !== undefined && expiresAt !== undefined) {\r\n throw new CacheConfigurationError(\r\n \"Cache set options cannot specify both `ttl` and `expiresAt` — choose one.\",\r\n );\r\n }\r\n\r\n if (ttl !== undefined) {\r\n return parseTtl(ttl);\r\n }\r\n\r\n if (expiresAt !== undefined) {\r\n return expiresAtToTtl(expiresAt);\r\n }\r\n\r\n return fallback;\r\n}\r\n\r\n/**\r\n * Combine any number of tag lists into a single deduped array, dropping\r\n * `undefined`/empty entries. Returns `undefined` when no tags survive — lets\r\n * callers skip emitting empty `tags: []` into option payloads.\r\n *\r\n * Used by scoped-cache merging where scope tags + handle tags + per-call tags\r\n * must union additively without duplicates.\r\n *\r\n * @example\r\n * mergeTagSets([\"a\", \"b\"], [\"b\", \"c\"]); // [\"a\", \"b\", \"c\"]\r\n * mergeTagSets(undefined, [\"x\"]); // [\"x\"]\r\n * mergeTagSets(undefined, undefined); // undefined\r\n * mergeTagSets([], []); // undefined\r\n */\r\nexport function mergeTagSets(\r\n ...lists: (string[] | undefined)[]\r\n): string[] | undefined {\r\n const flat: string[] = [];\r\n\r\n for (const list of lists) {\r\n if (!list || list.length === 0) {\r\n continue;\r\n }\r\n\r\n flat.push(...list);\r\n }\r\n\r\n if (flat.length === 0) {\r\n return undefined;\r\n }\r\n\r\n return Array.from(new Set(flat));\r\n}\r\n\r\n/**\r\n * Add extra tags to any option-bag that already shapes `tags?: string[]`.\r\n * Pure — clones the input shape, never mutates. Tags are appended (caller\r\n * is responsible for de-duplication if needed; pair with {@link mergeTagSets}).\r\n *\r\n * @example\r\n * injectTags({ ttl: \"1h\" }, [\"unread\"]); // { ttl: \"1h\", tags: [\"unread\"] }\r\n * injectTags({ tags: [\"a\"] }, [\"b\"]); // { tags: [\"a\", \"b\"] }\r\n */\r\nexport function injectTags<T extends { tags?: string[] }>(\r\n options: T,\r\n extraTags: string[],\r\n): T {\r\n if (extraTags.length === 0) {\r\n return options;\r\n }\r\n\r\n return {\r\n ...options,\r\n tags: [...(options.tags ?? []), ...extraTags],\r\n };\r\n}\r\n\r\n/**\r\n * Cosine similarity between two equal-length numeric vectors.\r\n *\r\n * Returns a value in `[-1, 1]` where `1` means perfectly aligned, `0` means\r\n * orthogonal, and `-1` means opposing. For typical embedding spaces (where\r\n * vectors live in the positive cone) the practical range is `[0, 1]`.\r\n *\r\n * Throws {@link CacheConfigurationError} on dimension mismatch — fail loud at\r\n * the call site rather than silently returning a misleading score. A zero-norm\r\n * vector on either side returns `0` (no defined direction to compare).\r\n *\r\n * @example\r\n * cosineSimilarity([1, 0, 0], [1, 0, 0]); // 1\r\n * cosineSimilarity([1, 0, 0], [0, 1, 0]); // 0\r\n */\r\nexport function cosineSimilarity(a: number[], b: number[]): number {\r\n if (a.length !== b.length) {\r\n throw new CacheConfigurationError(\r\n `Vector dimension mismatch: got ${a.length} and ${b.length}.`,\r\n );\r\n }\r\n\r\n if (a.length === 0) {\r\n throw new CacheConfigurationError(\r\n \"Vector dimension mismatch: empty vector cannot be compared.\",\r\n );\r\n }\r\n\r\n let dot = 0;\r\n let normA = 0;\r\n let normB = 0;\r\n\r\n for (let i = 0; i < a.length; i++) {\r\n const x = a[i];\r\n const y = b[i];\r\n\r\n if (x === undefined || y === undefined) {\r\n // Unreachable: both guards above establish `a.length === b.length` and\r\n // a non-zero length, so every index in range exists on both sides. Said\r\n // out loud rather than asserted with `as number`, because the two guards\r\n // that make it true are the kind of thing a later edit removes — and if\r\n // one ever did, an assertion would let `undefined` into the arithmetic\r\n // and return NaN as a similarity SCORE. A silently wrong ranking is\r\n // worse than a stopped one.\r\n break;\r\n }\r\n\r\n dot += x * y;\r\n normA += x * x;\r\n normB += y * y;\r\n }\r\n\r\n if (normA === 0 || normB === 0) {\r\n return 0;\r\n }\r\n\r\n return dot / (Math.sqrt(normA) * Math.sqrt(normB));\r\n}\r\n\r\n/**\r\n * Matches userinfo credentials embedded in a connection URL\r\n * (e.g. `redis://user:pass@host`). Node/Redis/Postgres client errors\r\n * frequently echo the target connection string — including the password —\r\n * back in `error.message` on connection failure.\r\n */\r\nconst CREDENTIALS_IN_URL = /(:\\/\\/)[^\\s/@]+:[^\\s/@]+@/g;\r\n\r\n/**\r\n * Strip embedded `user:pass@` credentials from a string before it reaches\r\n * any log sink.\r\n */\r\nexport function redactCredentials(message: string): string {\r\n return message.replace(CREDENTIALS_IN_URL, \"$1[REDACTED]@\");\r\n}\r\n\r\n/**\r\n * Reduce an unknown thrown value to a log-safe `{ message, code? }` shape.\r\n * Callers must never log the raw error object itself — it may carry the\r\n * connection URL (with password) in the top-level message, `cause`, or\r\n * driver-specific fields, all of which `console.log`/`util.inspect` would\r\n * still print in full.\r\n */\r\nexport function safeErrorInfo(error: unknown): { message: string; code?: string } {\r\n if (error instanceof Error) {\r\n const info: { message: string; code?: string } = {\r\n message: redactCredentials(error.message),\r\n };\r\n\r\n const code = (error as any).code;\r\n if (code) info.code = String(code);\r\n\r\n return info;\r\n }\r\n\r\n return { message: redactCredentials(String(error)) };\r\n}\r\n\r\nexport enum CACHE_FOR {\r\n /**\r\n * Cache for 30 Minutes (in seconds)\r\n */\r\n HALF_HOUR = 1800,\r\n /**\r\n * Cache for 1 Hour (in seconds)\r\n */\r\n ONE_HOUR = 3600,\r\n /**\r\n * Cache for 12 Hours (in seconds)\r\n */\r\n HALF_DAY = 43200,\r\n /**\r\n * Cache for 24 Hours (in seconds)\r\n */\r\n ONE_DAY = 86400,\r\n /**\r\n * Cache for 7 Days (in seconds)\r\n */\r\n ONE_WEEK = 604800,\r\n /**\r\n * Cache for 15 Days (in seconds)\r\n */\r\n HALF_MONTH = 1296000,\r\n /**\r\n * Cache for 30 Days (in seconds)\r\n */\r\n ONE_MONTH = 2592000,\r\n /**\r\n * Cache for 60 Days (in seconds)\r\n */\r\n TWO_MONTHS = 5184000,\r\n /**\r\n * Cache for 180 Days (in seconds)\r\n */\r\n SIX_MONTHS = 15768000,\r\n /**\r\n * Cache for 365 Days (in seconds)\r\n */\r\n ONE_YEAR = 31536000,\r\n}\r\n"],"mappings":"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"}