@warlock.js/cache 4.0.171 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/README.md +85 -0
  2. package/cjs/index.cjs +4088 -0
  3. package/cjs/index.cjs.map +1 -0
  4. package/esm/cache-manager.d.mts +314 -0
  5. package/esm/cache-manager.d.mts.map +1 -0
  6. package/esm/cache-manager.mjs +486 -0
  7. package/esm/cache-manager.mjs.map +1 -0
  8. package/esm/cached/auto-key.d.mts +25 -0
  9. package/esm/cached/auto-key.d.mts.map +1 -0
  10. package/esm/cached/auto-key.mjs +55 -0
  11. package/esm/cached/auto-key.mjs.map +1 -0
  12. package/esm/cached/cached.d.mts +54 -0
  13. package/esm/cached/cached.d.mts.map +1 -0
  14. package/esm/cached/cached.mjs +25 -0
  15. package/esm/cached/cached.mjs.map +1 -0
  16. package/esm/cached/index.d.mts +3 -0
  17. package/esm/cached/index.mjs +5 -0
  18. package/esm/cached/normalize-args.d.mts +51 -0
  19. package/esm/cached/normalize-args.d.mts.map +1 -0
  20. package/esm/cached/normalize-args.mjs +26 -0
  21. package/esm/cached/normalize-args.mjs.map +1 -0
  22. package/esm/drivers/base-cache-driver.d.mts +322 -0
  23. package/esm/drivers/base-cache-driver.d.mts.map +1 -0
  24. package/esm/drivers/base-cache-driver.mjs +522 -0
  25. package/esm/drivers/base-cache-driver.mjs.map +1 -0
  26. package/esm/drivers/file-cache-driver.d.mts +68 -0
  27. package/esm/drivers/file-cache-driver.d.mts.map +1 -0
  28. package/esm/drivers/file-cache-driver.mjs +174 -0
  29. package/esm/drivers/file-cache-driver.mjs.map +1 -0
  30. package/esm/drivers/index.d.mts +9 -0
  31. package/esm/drivers/index.mjs +11 -0
  32. package/esm/drivers/lru-memory-cache-driver.d.mts +136 -0
  33. package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -0
  34. package/esm/drivers/lru-memory-cache-driver.mjs +317 -0
  35. package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -0
  36. package/esm/drivers/memory-cache-driver.d.mts +112 -0
  37. package/esm/drivers/memory-cache-driver.d.mts.map +1 -0
  38. package/esm/drivers/memory-cache-driver.mjs +241 -0
  39. package/esm/drivers/memory-cache-driver.mjs.map +1 -0
  40. package/esm/drivers/memory-extended-cache-driver.d.mts +17 -0
  41. package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -0
  42. package/esm/drivers/memory-extended-cache-driver.mjs +34 -0
  43. package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -0
  44. package/esm/drivers/mock-cache-driver.d.mts +137 -0
  45. package/esm/drivers/mock-cache-driver.d.mts.map +1 -0
  46. package/esm/drivers/mock-cache-driver.mjs +226 -0
  47. package/esm/drivers/mock-cache-driver.mjs.map +1 -0
  48. package/esm/drivers/null-cache-driver.d.mts +69 -0
  49. package/esm/drivers/null-cache-driver.d.mts.map +1 -0
  50. package/esm/drivers/null-cache-driver.mjs +92 -0
  51. package/esm/drivers/null-cache-driver.mjs.map +1 -0
  52. package/esm/drivers/pg-cache-driver.d.mts +148 -0
  53. package/esm/drivers/pg-cache-driver.d.mts.map +1 -0
  54. package/esm/drivers/pg-cache-driver.mjs +437 -0
  55. package/esm/drivers/pg-cache-driver.mjs.map +1 -0
  56. package/esm/drivers/redis-cache-driver.d.mts +86 -0
  57. package/esm/drivers/redis-cache-driver.d.mts.map +1 -0
  58. package/esm/drivers/redis-cache-driver.mjs +312 -0
  59. package/esm/drivers/redis-cache-driver.mjs.map +1 -0
  60. package/esm/index.d.mts +21 -0
  61. package/esm/index.mjs +24 -0
  62. package/esm/list/index.d.mts +1 -0
  63. package/esm/list/memory-cache-list.d.mts +77 -0
  64. package/esm/list/memory-cache-list.d.mts.map +1 -0
  65. package/esm/list/memory-cache-list.mjs +119 -0
  66. package/esm/list/memory-cache-list.mjs.map +1 -0
  67. package/esm/metrics.d.mts +118 -0
  68. package/esm/metrics.d.mts.map +1 -0
  69. package/esm/metrics.mjs +197 -0
  70. package/esm/metrics.mjs.map +1 -0
  71. package/esm/scoped-cache.d.mts +205 -0
  72. package/esm/scoped-cache.d.mts.map +1 -0
  73. package/esm/scoped-cache.mjs +274 -0
  74. package/esm/scoped-cache.mjs.map +1 -0
  75. package/esm/tagged-cache.d.mts +89 -0
  76. package/esm/tagged-cache.d.mts.map +1 -0
  77. package/esm/tagged-cache.mjs +147 -0
  78. package/esm/tagged-cache.mjs.map +1 -0
  79. package/esm/tagged-scoped-cache.d.mts +111 -0
  80. package/esm/tagged-scoped-cache.d.mts.map +1 -0
  81. package/esm/tagged-scoped-cache.mjs +142 -0
  82. package/esm/tagged-scoped-cache.mjs.map +1 -0
  83. package/esm/types.d.mts +1067 -0
  84. package/esm/types.d.mts.map +1 -0
  85. package/esm/types.mjs +62 -0
  86. package/esm/types.mjs.map +1 -0
  87. package/esm/utils.d.mts +161 -0
  88. package/esm/utils.d.mts.map +1 -0
  89. package/esm/utils.mjs +222 -0
  90. package/esm/utils.mjs.map +1 -0
  91. package/llms-full.txt +2071 -0
  92. package/llms.txt +28 -0
  93. package/package.json +53 -39
  94. package/skills/apply-cache-patterns/SKILL.md +97 -0
  95. package/skills/cache-basics/SKILL.md +121 -0
  96. package/skills/configure-pg-cache/SKILL.md +115 -0
  97. package/skills/configure-set-options/SKILL.md +96 -0
  98. package/skills/handle-cache-errors/SKILL.md +91 -0
  99. package/skills/observe-cache/SKILL.md +103 -0
  100. package/skills/overview/SKILL.md +69 -0
  101. package/skills/pick-cache-driver/SKILL.md +115 -0
  102. package/skills/test-cache-code/SKILL.md +219 -0
  103. package/skills/use-cache-atomic/SKILL.md +67 -0
  104. package/skills/use-cache-bulk/SKILL.md +57 -0
  105. package/skills/use-cache-list/SKILL.md +85 -0
  106. package/skills/use-cache-lock/SKILL.md +104 -0
  107. package/skills/use-cache-namespace/SKILL.md +88 -0
  108. package/skills/use-cache-similarity/SKILL.md +94 -0
  109. package/skills/use-cache-tags/SKILL.md +85 -0
  110. package/skills/use-cache-update-merge/SKILL.md +84 -0
  111. package/skills/use-cache-utils/SKILL.md +89 -0
  112. package/skills/use-cached-hof/SKILL.md +102 -0
  113. package/skills/use-swr/SKILL.md +104 -0
  114. package/cjs/cache-manager.d.ts +0 -163
  115. package/cjs/cache-manager.d.ts.map +0 -1
  116. package/cjs/cache-manager.js +0 -322
  117. package/cjs/cache-manager.js.map +0 -1
  118. package/cjs/drivers/base-cache-driver.d.ts +0 -152
  119. package/cjs/drivers/base-cache-driver.d.ts.map +0 -1
  120. package/cjs/drivers/base-cache-driver.js +0 -321
  121. package/cjs/drivers/base-cache-driver.js.map +0 -1
  122. package/cjs/drivers/file-cache-driver.d.ts +0 -45
  123. package/cjs/drivers/file-cache-driver.d.ts.map +0 -1
  124. package/cjs/drivers/file-cache-driver.js +0 -133
  125. package/cjs/drivers/file-cache-driver.js.map +0 -1
  126. package/cjs/drivers/index.d.ts +0 -8
  127. package/cjs/drivers/index.d.ts.map +0 -1
  128. package/cjs/drivers/lru-memory-cache-driver.d.ts +0 -98
  129. package/cjs/drivers/lru-memory-cache-driver.d.ts.map +0 -1
  130. package/cjs/drivers/lru-memory-cache-driver.js +0 -252
  131. package/cjs/drivers/lru-memory-cache-driver.js.map +0 -1
  132. package/cjs/drivers/memory-cache-driver.d.ts +0 -82
  133. package/cjs/drivers/memory-cache-driver.d.ts.map +0 -1
  134. package/cjs/drivers/memory-cache-driver.js +0 -218
  135. package/cjs/drivers/memory-cache-driver.js.map +0 -1
  136. package/cjs/drivers/memory-extended-cache-driver.d.ts +0 -13
  137. package/cjs/drivers/memory-extended-cache-driver.d.ts.map +0 -1
  138. package/cjs/drivers/memory-extended-cache-driver.js +0 -25
  139. package/cjs/drivers/memory-extended-cache-driver.js.map +0 -1
  140. package/cjs/drivers/null-cache-driver.d.ts +0 -58
  141. package/cjs/drivers/null-cache-driver.d.ts.map +0 -1
  142. package/cjs/drivers/null-cache-driver.js +0 -84
  143. package/cjs/drivers/null-cache-driver.js.map +0 -1
  144. package/cjs/drivers/redis-cache-driver.d.ts +0 -57
  145. package/cjs/drivers/redis-cache-driver.d.ts.map +0 -1
  146. package/cjs/drivers/redis-cache-driver.js +0 -263
  147. package/cjs/drivers/redis-cache-driver.js.map +0 -1
  148. package/cjs/index.d.ts +0 -6
  149. package/cjs/index.d.ts.map +0 -1
  150. package/cjs/index.js +0 -1
  151. package/cjs/index.js.map +0 -1
  152. package/cjs/tagged-cache.d.ts +0 -77
  153. package/cjs/tagged-cache.d.ts.map +0 -1
  154. package/cjs/tagged-cache.js +0 -160
  155. package/cjs/tagged-cache.js.map +0 -1
  156. package/cjs/types.d.ts +0 -391
  157. package/cjs/types.d.ts.map +0 -1
  158. package/cjs/types.js +0 -36
  159. package/cjs/types.js.map +0 -1
  160. package/cjs/utils.d.ts +0 -50
  161. package/cjs/utils.d.ts.map +0 -1
  162. package/cjs/utils.js +0 -55
  163. package/cjs/utils.js.map +0 -1
  164. package/esm/cache-manager.d.ts +0 -163
  165. package/esm/cache-manager.d.ts.map +0 -1
  166. package/esm/cache-manager.js +0 -322
  167. package/esm/cache-manager.js.map +0 -1
  168. package/esm/drivers/base-cache-driver.d.ts +0 -152
  169. package/esm/drivers/base-cache-driver.d.ts.map +0 -1
  170. package/esm/drivers/base-cache-driver.js +0 -321
  171. package/esm/drivers/base-cache-driver.js.map +0 -1
  172. package/esm/drivers/file-cache-driver.d.ts +0 -45
  173. package/esm/drivers/file-cache-driver.d.ts.map +0 -1
  174. package/esm/drivers/file-cache-driver.js +0 -133
  175. package/esm/drivers/file-cache-driver.js.map +0 -1
  176. package/esm/drivers/index.d.ts +0 -8
  177. package/esm/drivers/index.d.ts.map +0 -1
  178. package/esm/drivers/lru-memory-cache-driver.d.ts +0 -98
  179. package/esm/drivers/lru-memory-cache-driver.d.ts.map +0 -1
  180. package/esm/drivers/lru-memory-cache-driver.js +0 -252
  181. package/esm/drivers/lru-memory-cache-driver.js.map +0 -1
  182. package/esm/drivers/memory-cache-driver.d.ts +0 -82
  183. package/esm/drivers/memory-cache-driver.d.ts.map +0 -1
  184. package/esm/drivers/memory-cache-driver.js +0 -218
  185. package/esm/drivers/memory-cache-driver.js.map +0 -1
  186. package/esm/drivers/memory-extended-cache-driver.d.ts +0 -13
  187. package/esm/drivers/memory-extended-cache-driver.d.ts.map +0 -1
  188. package/esm/drivers/memory-extended-cache-driver.js +0 -25
  189. package/esm/drivers/memory-extended-cache-driver.js.map +0 -1
  190. package/esm/drivers/null-cache-driver.d.ts +0 -58
  191. package/esm/drivers/null-cache-driver.d.ts.map +0 -1
  192. package/esm/drivers/null-cache-driver.js +0 -84
  193. package/esm/drivers/null-cache-driver.js.map +0 -1
  194. package/esm/drivers/redis-cache-driver.d.ts +0 -57
  195. package/esm/drivers/redis-cache-driver.d.ts.map +0 -1
  196. package/esm/drivers/redis-cache-driver.js +0 -263
  197. package/esm/drivers/redis-cache-driver.js.map +0 -1
  198. package/esm/index.d.ts +0 -6
  199. package/esm/index.d.ts.map +0 -1
  200. package/esm/index.js +0 -1
  201. package/esm/index.js.map +0 -1
  202. package/esm/tagged-cache.d.ts +0 -77
  203. package/esm/tagged-cache.d.ts.map +0 -1
  204. package/esm/tagged-cache.js +0 -160
  205. package/esm/tagged-cache.js.map +0 -1
  206. package/esm/types.d.ts +0 -391
  207. package/esm/types.d.ts.map +0 -1
  208. package/esm/types.js +0 -36
  209. package/esm/types.js.map +0 -1
  210. package/esm/utils.d.ts +0 -50
  211. package/esm/utils.d.ts.map +0 -1
  212. package/esm/utils.js +0 -55
  213. package/esm/utils.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lru-memory-cache-driver.mjs","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/lru-memory-cache-driver.ts"],"sourcesContent":["import type {\r\n CacheData,\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheSetResult,\r\n CacheSimilarHit,\r\n CacheSimilarOptions,\r\n CacheTtl,\r\n LRUMemoryCacheOptions,\r\n} from \"../types\";\r\nimport { cosineSimilarity } from \"../utils\";\r\nimport { BaseCacheDriver } from \"./base-cache-driver\";\r\n\r\nclass CacheNode {\r\n public next: CacheNode | null = null;\r\n public prev: CacheNode | null = null;\r\n public expiresAt?: number;\r\n /**\r\n * Freshness deadline (ms timestamp) — populated by `swr()`. Within\r\n * `expiresAt > now > staleAt` the entry is \"stale-but-revalidatable.\"\r\n */\r\n public staleAt?: number;\r\n /**\r\n * Optional embedding vector — populated when the entry was written with\r\n * `set({ vector })`. Scanned by `similar()`.\r\n */\r\n public vector?: number[];\r\n public constructor(\r\n public key: string,\r\n public value: any,\r\n ttl?: number,\r\n ) {\r\n if (ttl && ttl !== Infinity) {\r\n this.expiresAt = Date.now() + ttl * 1000;\r\n }\r\n }\r\n\r\n public get isExpired(): boolean {\r\n return this.expiresAt !== undefined && this.expiresAt < Date.now();\r\n }\r\n}\r\n\r\n/**\r\n * LRU Memory Cache Driver\r\n * The concept of LRU is to remove the least recently used data\r\n * whenever the cache is full\r\n * The question that resides here is how to tell the cache is full?\r\n */\r\nexport class LRUMemoryCacheDriver\r\n extends BaseCacheDriver<LRUMemoryCacheDriver, LRUMemoryCacheOptions>\r\n implements CacheDriver<LRUMemoryCacheDriver, LRUMemoryCacheOptions>\r\n{\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"lru\";\r\n\r\n /**\r\n * Cache map\r\n */\r\n protected cache: Map<string, CacheNode> = new Map();\r\n\r\n /**\r\n * Head of the cache\r\n */\r\n protected head: CacheNode = new CacheNode(\"\", null);\r\n\r\n /**\r\n * Tail of the cache\r\n */\r\n protected tail: CacheNode = new CacheNode(\"\", null);\r\n\r\n /**\r\n * Cleanup interval reference\r\n */\r\n protected cleanupInterval?: NodeJS.Timeout;\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public constructor() {\r\n super();\r\n\r\n this.init();\r\n this.startCleanup();\r\n }\r\n\r\n /**\r\n * Initialize the cache\r\n */\r\n public init() {\r\n this.head.next = this.tail;\r\n this.tail.prev = this.head;\r\n }\r\n\r\n /**\r\n * Start the cleanup process for expired items\r\n */\r\n public startCleanup() {\r\n // Clear existing interval if any\r\n if (this.cleanupInterval) {\r\n clearInterval(this.cleanupInterval);\r\n }\r\n\r\n this.cleanupInterval = setInterval(async () => {\r\n const now = Date.now();\r\n const expiredKeys: string[] = [];\r\n\r\n for (const [key, node] of this.cache) {\r\n if (node.expiresAt && node.expiresAt <= now) {\r\n expiredKeys.push(key);\r\n }\r\n }\r\n\r\n for (const key of expiredKeys) {\r\n const node = this.cache.get(key);\r\n if (node) {\r\n this.removeNode(node);\r\n this.cache.delete(key);\r\n this.log(\"expired\", key);\r\n // Emit expired event\r\n await this.emit(\"expired\", { key });\r\n }\r\n }\r\n }, 1000);\r\n\r\n // do not block the process from exiting\r\n this.cleanupInterval.unref();\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Clears every entry whose key starts with the parsed namespace (followed\r\n * by a dot) or equals it exactly. Called with an empty namespace while a\r\n * `globalPrefix` is configured, clears everything under the prefix — which\r\n * is how `flush()` scopes cleanup per tenant.\r\n */\r\n public async removeNamespace(namespace: string) {\r\n const parsedNamespace = this.parseKey(namespace);\r\n\r\n this.log(\"clearing\", parsedNamespace || \"(all)\");\r\n\r\n const removed: string[] = [];\r\n\r\n if (parsedNamespace === \"\") {\r\n for (const key of this.cache.keys()) {\r\n removed.push(key);\r\n }\r\n } else {\r\n const prefix = parsedNamespace + \".\";\r\n\r\n for (const key of this.cache.keys()) {\r\n if (key === parsedNamespace || key.startsWith(prefix)) {\r\n removed.push(key);\r\n }\r\n }\r\n }\r\n\r\n for (const key of removed) {\r\n const node = this.cache.get(key);\r\n if (node) {\r\n this.removeNode(node);\r\n this.cache.delete(key);\r\n }\r\n await this.emit(\"removed\", { key });\r\n }\r\n\r\n this.log(\"cleared\", parsedNamespace || \"(all)\");\r\n\r\n return removed;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async set(\r\n key: CacheKey,\r\n value: any,\r\n ttlOrOptions?: CacheTtl | CacheSetOptions,\r\n ): Promise<any> {\r\n const parsedKey = this.parseKey(key);\r\n const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);\r\n\r\n this.log(\"caching\", parsedKey);\r\n\r\n // For conditional writes, check existence via get() so expired entries\r\n // are treated as missing (get() handles expiry cleanup). The raw `cache.get`\r\n // node lookup would let stale entries block onConflict: \"create\".\r\n let existingNode = this.cache.get(parsedKey);\r\n if (existingNode && existingNode.isExpired) {\r\n this.removeNode(existingNode);\r\n this.cache.delete(parsedKey);\r\n existingNode = undefined;\r\n }\r\n\r\n const exists = Boolean(existingNode);\r\n\r\n if (onConflict === \"create\" && exists) {\r\n const result: CacheSetResult = {\r\n wasSet: false,\r\n existing: existingNode!.value,\r\n };\r\n return result;\r\n }\r\n\r\n if (onConflict === \"update\" && !exists) {\r\n const result: CacheSetResult = { wasSet: false, existing: null };\r\n return result;\r\n }\r\n\r\n if (existingNode) {\r\n existingNode.value = value;\r\n if (ttl && ttl !== Infinity) {\r\n existingNode.expiresAt = Date.now() + ttl * 1000;\r\n } else {\r\n existingNode.expiresAt = undefined;\r\n }\r\n existingNode.staleAt = staleAt;\r\n if (vector) {\r\n existingNode.vector = vector.slice();\r\n }\r\n\r\n this.moveHead(existingNode);\r\n } else {\r\n const newNode = new CacheNode(parsedKey, value, ttl);\r\n newNode.staleAt = staleAt;\r\n if (vector) {\r\n newNode.vector = vector.slice();\r\n }\r\n\r\n this.cache.set(parsedKey, newNode);\r\n\r\n this.addNode(newNode);\r\n if (this.cache.size > this.capacity) {\r\n this.removeTail();\r\n }\r\n }\r\n\r\n if (tags && tags.length > 0) {\r\n await this.applyTags(parsedKey, tags);\r\n }\r\n\r\n this.log(\"cached\", parsedKey);\r\n\r\n await this.emit(\"set\", { key: parsedKey, value, ttl });\r\n\r\n if (onConflict === \"create\" || onConflict === \"update\") {\r\n const result: CacheSetResult = { wasSet: true, existing: null };\r\n return result;\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Move the node to the head\r\n */\r\n protected moveHead(node: CacheNode) {\r\n this.removeNode(node);\r\n this.addNode(node);\r\n }\r\n\r\n /**\r\n * Remove the node from the cache\r\n */\r\n protected removeNode(node: CacheNode) {\r\n node.prev!.next = node.next;\r\n node.next!.prev = node.prev;\r\n }\r\n\r\n /**\r\n * Add the node to the head\r\n */\r\n protected addNode(node: CacheNode) {\r\n node.next = this.head.next;\r\n node.prev = this.head;\r\n this.head.next!.prev = node;\r\n this.head.next = node;\r\n }\r\n\r\n /**\r\n * Remove the tail node\r\n */\r\n protected removeTail() {\r\n const node = this.tail.prev!;\r\n\r\n this.removeNode(node);\r\n\r\n this.cache.delete(node.key);\r\n }\r\n\r\n /**\r\n * Read the raw {@link CacheData} wrapper, including `staleAt` metadata.\r\n * Returns `null` for missing or expired nodes — `swr()` consumes this\r\n * to branch on freshness without going through `get()`'s clone-and-emit\r\n * path.\r\n */\r\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\r\n const parsedKey = this.parseKey(key);\r\n const node = this.cache.get(parsedKey);\r\n\r\n if (!node || node.isExpired) {\r\n return null;\r\n }\r\n\r\n return {\r\n data: node.value,\r\n expiresAt: node.expiresAt,\r\n staleAt: node.staleAt,\r\n };\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async get(key: CacheKey) {\r\n const parsedKey = this.parseKey(key);\r\n\r\n this.log(\"fetching\", parsedKey);\r\n\r\n const node = this.cache.get(parsedKey);\r\n\r\n if (!node) {\r\n this.log(\"notFound\", parsedKey);\r\n // Emit miss event\r\n await this.emit(\"miss\", { key: parsedKey });\r\n return null;\r\n }\r\n\r\n // Check if expired\r\n if (node.isExpired) {\r\n this.removeNode(node);\r\n this.cache.delete(parsedKey);\r\n this.log(\"expired\", parsedKey);\r\n // Emit expired event\r\n await this.emit(\"expired\", { key: parsedKey });\r\n // Also emit miss since we're returning null\r\n await this.emit(\"miss\", { key: parsedKey });\r\n return null;\r\n }\r\n\r\n this.moveHead(node);\r\n\r\n this.log(\"fetched\", parsedKey);\r\n\r\n const value = node.value;\r\n\r\n // Apply cloning for immutability protection\r\n if (value === null || value === undefined) {\r\n return value;\r\n }\r\n\r\n const type = typeof value;\r\n if (type === \"string\" || type === \"number\" || type === \"boolean\") {\r\n // Emit hit event\r\n await this.emit(\"hit\", { key: parsedKey, value });\r\n return value;\r\n }\r\n\r\n try {\r\n const clonedValue = structuredClone(value);\r\n // Emit hit event\r\n await this.emit(\"hit\", { key: parsedKey, value: clonedValue });\r\n return clonedValue;\r\n } catch (error) {\r\n this.logError(`Failed to clone cached value for ${parsedKey}`, error);\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remove(key: CacheKey) {\r\n const parsedKey = this.parseKey(key);\r\n\r\n this.log(\"removing\", parsedKey);\r\n\r\n const node = this.cache.get(parsedKey);\r\n\r\n if (node) {\r\n this.removeNode(node);\r\n this.cache.delete(parsedKey);\r\n }\r\n\r\n this.log(\"removed\", parsedKey);\r\n\r\n // Emit removed event\r\n await this.emit(\"removed\", { key: parsedKey });\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * When a `globalPrefix` is configured, `flush` scopes itself to that prefix\r\n * so multi-tenant caches don't accidentally wipe sibling tenants. Without\r\n * a prefix, clears everything.\r\n */\r\n public async flush() {\r\n this.log(\"flushing\");\r\n\r\n if (this.options.globalPrefix) {\r\n await this.removeNamespace(\"\");\r\n } else {\r\n this.cache.clear();\r\n this.init();\r\n }\r\n\r\n this.log(\"flushed\");\r\n\r\n await this.emit(\"flushed\");\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Brute-force O(N) cosine similarity over every cached node that carries a\r\n * vector. Suitable for development and small in-memory knowledge bases —\r\n * not for production beyond ~10k entries.\r\n *\r\n * @warning Dev-only — O(N) per query.\r\n */\r\n public async similar<T = any>(\r\n vector: number[],\r\n options: CacheSimilarOptions,\r\n ): Promise<CacheSimilarHit<T>[]> {\r\n const tagFilter = await this.getKeysForTags(options.tags);\r\n\r\n const hits: CacheSimilarHit<T>[] = [];\r\n\r\n for (const [parsedKey, node] of this.cache) {\r\n if (!node.vector) continue;\r\n if (node.isExpired) continue;\r\n if (tagFilter && !tagFilter.has(parsedKey)) continue;\r\n\r\n const score = cosineSimilarity(vector, node.vector);\r\n\r\n if (options.threshold !== undefined && score < options.threshold) {\r\n continue;\r\n }\r\n\r\n // Clone object values to match get() semantics.\r\n let value: any = node.value;\r\n if (value !== null && value !== undefined) {\r\n const t = typeof value;\r\n if (t !== \"string\" && t !== \"number\" && t !== \"boolean\") {\r\n value = structuredClone(value);\r\n }\r\n }\r\n\r\n hits.push({ key: parsedKey, value, score });\r\n }\r\n\r\n hits.sort((a, b) => b.score - a.score);\r\n\r\n if (options.topK >= 0 && hits.length > options.topK) {\r\n hits.length = options.topK;\r\n }\r\n\r\n return hits;\r\n }\r\n\r\n /**\r\n * Get lru capacity\r\n */\r\n public get capacity() {\r\n return this.options.capacity || 1000;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async disconnect() {\r\n // Clear the cleanup interval to prevent memory leaks\r\n if (this.cleanupInterval) {\r\n clearInterval(this.cleanupInterval);\r\n this.cleanupInterval = undefined;\r\n }\r\n\r\n await super.disconnect();\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ import { BaseCacheDriver } from "./base-cache-driver.mjs";
2
+ import { CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheSimilarHit, CacheSimilarOptions, CacheTtl, MemoryCacheOptions } from "../types.mjs";
3
+ import { GenericObject } from "@mongez/reinforcements";
4
+
5
+ //#region ../../@warlock.js/cache/src/drivers/memory-cache-driver.d.ts
6
+ declare class MemoryCacheDriver extends BaseCacheDriver<MemoryCacheDriver, MemoryCacheOptions> implements CacheDriver<MemoryCacheDriver, MemoryCacheOptions> {
7
+ /**
8
+ * {@inheritdoc}
9
+ */
10
+ name: string;
11
+ /**
12
+ * Cached data
13
+ */
14
+ data: GenericObject;
15
+ /**
16
+ * List of data that will be cleared from cache
17
+ */
18
+ protected temporaryData: Record<string, {
19
+ key: string;
20
+ expiresAt: number;
21
+ }>;
22
+ /**
23
+ * Cleanup interval reference
24
+ */
25
+ protected cleanupInterval?: NodeJS.Timeout;
26
+ /**
27
+ * Access order tracking for LRU eviction (when maxSize is set)
28
+ */
29
+ protected accessOrder: string[];
30
+ /**
31
+ * Parallel vector index keyed by parsedKey. Populated by `set({ vector })`,
32
+ * scanned by `similar()`. Lifetime mirrors the main entry — cleared on
33
+ * `remove`, `flush`, expiry, namespace clear, and LRU eviction.
34
+ */
35
+ protected vectorIndex: Map<string, number[]>;
36
+ /**
37
+ * {@inheritdoc}
38
+ */
39
+ constructor();
40
+ /**
41
+ * Start the cleanup process whenever a data that has a cache key is set
42
+ */
43
+ startCleanup(): void;
44
+ /**
45
+ * {@inheritdoc}
46
+ */
47
+ removeNamespace(namespace: string): Promise<this>;
48
+ /**
49
+ * {@inheritdoc}
50
+ */
51
+ set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;
52
+ /**
53
+ * {@inheritdoc}
54
+ */
55
+ get(key: CacheKey): Promise<any>;
56
+ /**
57
+ * Read the raw {@link CacheData} wrapper, including `staleAt` metadata.
58
+ * Returns `null` for missing or expired entries so the SWR flow can branch
59
+ * cleanly. Does not emit `hit`/`miss` events — that's `get()`'s job.
60
+ */
61
+ protected getEntry(key: CacheKey): Promise<CacheData | null>;
62
+ /**
63
+ * {@inheritdoc}
64
+ */
65
+ remove(key: CacheKey): Promise<void>;
66
+ /**
67
+ * {@inheritdoc}
68
+ */
69
+ flush(): Promise<void>;
70
+ /**
71
+ * Set the temporary data
72
+ */
73
+ protected setTemporaryData(key: CacheKey, parsedKey: string, ttl: number): void;
74
+ /**
75
+ * Track access for LRU eviction
76
+ */
77
+ protected trackAccess(key: string): void;
78
+ /**
79
+ * Remove key from access order tracking
80
+ */
81
+ protected removeFromAccessOrder(key: string): void;
82
+ /**
83
+ * Enforce max size by evicting least recently used items.
84
+ *
85
+ * Recomputes the live cache size on every iteration — a single snapshot at
86
+ * the top of the loop would go stale and cause this routine to evict every
87
+ * entry in `accessOrder` (including the just-inserted key).
88
+ */
89
+ protected enforceMaxSize(): Promise<void>;
90
+ /**
91
+ * Get current cache size (number of cached items)
92
+ */
93
+ protected getCacheSize(): number;
94
+ /**
95
+ * {@inheritdoc}
96
+ *
97
+ * Brute-force O(N) cosine similarity over every entry that was written with
98
+ * `set({ vector })`. Suitable for development and small in-memory knowledge
99
+ * bases — not for production beyond ~10k entries. Use the `pg` driver
100
+ * (with pgvector) or `redis` (with RediSearch) at scale.
101
+ *
102
+ * @warning Dev-only — O(N) per query.
103
+ */
104
+ similar<T = any>(vector: number[], options: CacheSimilarOptions): Promise<CacheSimilarHit<T>[]>;
105
+ /**
106
+ * {@inheritdoc}
107
+ */
108
+ disconnect(): Promise<void>;
109
+ }
110
+ //#endregion
111
+ export { MemoryCacheDriver };
112
+ //# sourceMappingURL=memory-cache-driver.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-cache-driver.d.mts","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/memory-cache-driver.ts"],"mappings":";;;;;cAgBa,iBAAA,SACH,eAAA,CAAgB,iBAAA,EAAmB,kBAAA,aAChC,WAAA,CAAY,iBAAA,EAAmB,kBAAA;;AAF5C;;EAOS,IAAA;EANiB;;;EAWjB,IAAA,EAAM,aAAA;EAAA;;;EAAA,UAKH,aAAA,EAAe,MAAA;IAGrB,GAAA;IACA,SAAA;EAAA;EA2FwB;;;EAAA,UApFlB,eAAA,GAAkB,MAAA,CAAO,OAAA;EAkLL;;;EAAA,UA7KpB,WAAA;EA+LuB;;;;;EAAA,UAxLvB,WAAA,EAAa,GAAA;EAuTZ;;;;EA7VA;;;EAoDJ,YAAA,CAAA;EArDiB;;;EAiFX,eAAA,CAAgB,SAAA,WAAiB,OAAA;EAhFJ;;;EA2G7B,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EAhGO;;;EA2JG,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;EAhJpB;;;;;EAAA,UAkLM,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;;;;EAkBpC,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;EA9Ia;;;EAuKjC,KAAA,CAAA,GAAK,OAAA;EA1IhB;;;EAAA,UA6JQ,gBAAA,CAAiB,GAAA,EAAK,QAAA,EAAU,SAAA,UAAmB,GAAA;EA3J1D;;;EAAA,UAqKO,WAAA,CAAY,GAAA;EA1GQ;;;EAAA,UA0HpB,qBAAA,CAAsB,GAAA;EAxFS;;;;;;;EAAA,UAsGzB,cAAA,CAAA,GAAc,OAAA;EAxCpB;;;EAAA,UAiEA,YAAA,CAAA;EAjEmD;;;;;;;;;;EAgFhD,OAAA,SAAA,CACX,MAAA,YACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,eAAA,CAAgB,CAAA;EADhB;;;EAuCE,UAAA,CAAA,GAAU,OAAA;AAAA"}
@@ -0,0 +1,241 @@
1
+ import { cosineSimilarity } from "../utils.mjs";
2
+ import { BaseCacheDriver } from "./base-cache-driver.mjs";
3
+ import { get, set, unset } from "@mongez/reinforcements";
4
+
5
+ //#region ../../@warlock.js/cache/src/drivers/memory-cache-driver.ts
6
+ var MemoryCacheDriver = class extends BaseCacheDriver {
7
+ /**
8
+ * {@inheritdoc}
9
+ */
10
+ constructor() {
11
+ super();
12
+ this.name = "memory";
13
+ this.data = {};
14
+ this.temporaryData = {};
15
+ this.accessOrder = [];
16
+ this.vectorIndex = /* @__PURE__ */ new Map();
17
+ this.startCleanup();
18
+ }
19
+ /**
20
+ * Start the cleanup process whenever a data that has a cache key is set
21
+ */
22
+ startCleanup() {
23
+ if (this.cleanupInterval) clearInterval(this.cleanupInterval);
24
+ this.cleanupInterval = setInterval(async () => {
25
+ const now = Date.now();
26
+ for (const key in this.temporaryData) if (this.temporaryData[key].expiresAt <= now) {
27
+ await this.remove(this.temporaryData[key].key);
28
+ delete this.temporaryData[key];
29
+ this.log("expired", key);
30
+ await this.emit("expired", { key });
31
+ }
32
+ }, 1e3);
33
+ this.cleanupInterval.unref();
34
+ }
35
+ /**
36
+ * {@inheritdoc}
37
+ */
38
+ async removeNamespace(namespace) {
39
+ this.log("clearing", namespace);
40
+ namespace = this.parseKey(namespace);
41
+ unset(this.data, [namespace]);
42
+ if (namespace === "") this.vectorIndex.clear();
43
+ else {
44
+ const prefix = namespace + ".";
45
+ for (const k of [...this.vectorIndex.keys()]) if (k === namespace || k.startsWith(prefix)) this.vectorIndex.delete(k);
46
+ }
47
+ this.log("cleared", namespace);
48
+ return this;
49
+ }
50
+ /**
51
+ * {@inheritdoc}
52
+ */
53
+ async set(key, value, ttlOrOptions) {
54
+ const parsedKey = this.parseKey(key);
55
+ const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);
56
+ this.log("caching", parsedKey);
57
+ const existingValue = onConflict === "upsert" ? null : await this.get(key);
58
+ const exists = existingValue !== null;
59
+ if (onConflict === "create" && exists) return {
60
+ wasSet: false,
61
+ existing: existingValue
62
+ };
63
+ if (onConflict === "update" && !exists) return {
64
+ wasSet: false,
65
+ existing: null
66
+ };
67
+ const data = this.prepareDataForStorage(value, ttl, staleAt);
68
+ if (ttl) this.setTemporaryData(key, parsedKey, ttl);
69
+ set(this.data, parsedKey, data);
70
+ this.trackAccess(parsedKey);
71
+ if (!exists && this.options.maxSize) await this.enforceMaxSize();
72
+ if (tags && tags.length > 0) await this.applyTags(parsedKey, tags);
73
+ if (vector) this.vectorIndex.set(parsedKey, vector.slice());
74
+ this.log("cached", parsedKey);
75
+ await this.emit("set", {
76
+ key: parsedKey,
77
+ value,
78
+ ttl
79
+ });
80
+ if (onConflict === "create" || onConflict === "update") return {
81
+ wasSet: true,
82
+ existing: null
83
+ };
84
+ return this;
85
+ }
86
+ /**
87
+ * {@inheritdoc}
88
+ */
89
+ async get(key) {
90
+ const parsedKey = this.parseKey(key);
91
+ this.log("fetching", parsedKey);
92
+ const value = get(this.data, parsedKey);
93
+ if (!value) {
94
+ this.log("notFound", parsedKey);
95
+ await this.emit("miss", { key: parsedKey });
96
+ return null;
97
+ }
98
+ const result = await this.parseCachedData(parsedKey, value);
99
+ if (result === null) await this.emit("miss", { key: parsedKey });
100
+ else {
101
+ this.trackAccess(parsedKey);
102
+ await this.emit("hit", {
103
+ key: parsedKey,
104
+ value: result
105
+ });
106
+ }
107
+ return result;
108
+ }
109
+ /**
110
+ * Read the raw {@link CacheData} wrapper, including `staleAt` metadata.
111
+ * Returns `null` for missing or expired entries so the SWR flow can branch
112
+ * cleanly. Does not emit `hit`/`miss` events — that's `get()`'s job.
113
+ */
114
+ async getEntry(key) {
115
+ const parsedKey = this.parseKey(key);
116
+ const entry = get(this.data, parsedKey);
117
+ if (!entry) return null;
118
+ if (entry.expiresAt !== void 0 && entry.expiresAt <= Date.now()) return null;
119
+ return entry;
120
+ }
121
+ /**
122
+ * {@inheritdoc}
123
+ */
124
+ async remove(key) {
125
+ const parsedKey = this.parseKey(key);
126
+ this.log("removing", parsedKey);
127
+ unset(this.data, [parsedKey]);
128
+ delete this.temporaryData[parsedKey];
129
+ this.removeFromAccessOrder(parsedKey);
130
+ this.vectorIndex.delete(parsedKey);
131
+ this.log("removed", parsedKey);
132
+ await this.emit("removed", { key: parsedKey });
133
+ }
134
+ /**
135
+ * {@inheritdoc}
136
+ */
137
+ async flush() {
138
+ this.log("flushing");
139
+ if (this.options.globalPrefix) this.removeNamespace("");
140
+ else {
141
+ this.data = {};
142
+ this.accessOrder = [];
143
+ this.vectorIndex.clear();
144
+ }
145
+ this.log("flushed");
146
+ await this.emit("flushed");
147
+ }
148
+ /**
149
+ * Set the temporary data
150
+ */
151
+ setTemporaryData(key, parsedKey, ttl) {
152
+ this.temporaryData[parsedKey] = {
153
+ key: JSON.stringify(key),
154
+ expiresAt: Date.now() + ttl * 1e3
155
+ };
156
+ }
157
+ /**
158
+ * Track access for LRU eviction
159
+ */
160
+ trackAccess(key) {
161
+ if (!this.options.maxSize) return;
162
+ const index = this.accessOrder.indexOf(key);
163
+ if (index > -1) this.accessOrder.splice(index, 1);
164
+ this.accessOrder.push(key);
165
+ }
166
+ /**
167
+ * Remove key from access order tracking
168
+ */
169
+ removeFromAccessOrder(key) {
170
+ const index = this.accessOrder.indexOf(key);
171
+ if (index > -1) this.accessOrder.splice(index, 1);
172
+ }
173
+ /**
174
+ * Enforce max size by evicting least recently used items.
175
+ *
176
+ * Recomputes the live cache size on every iteration — a single snapshot at
177
+ * the top of the loop would go stale and cause this routine to evict every
178
+ * entry in `accessOrder` (including the just-inserted key).
179
+ */
180
+ async enforceMaxSize() {
181
+ if (!this.options.maxSize) return;
182
+ while (this.getCacheSize() > this.options.maxSize && this.accessOrder.length > 0) {
183
+ const lruKey = this.accessOrder.shift();
184
+ if (!lruKey) break;
185
+ this.log("removing", lruKey);
186
+ unset(this.data, [lruKey]);
187
+ delete this.temporaryData[lruKey];
188
+ this.vectorIndex.delete(lruKey);
189
+ this.log("removed", lruKey);
190
+ }
191
+ }
192
+ /**
193
+ * Get current cache size (number of cached items)
194
+ */
195
+ getCacheSize() {
196
+ return Object.keys(this.data).length;
197
+ }
198
+ /**
199
+ * {@inheritdoc}
200
+ *
201
+ * Brute-force O(N) cosine similarity over every entry that was written with
202
+ * `set({ vector })`. Suitable for development and small in-memory knowledge
203
+ * bases — not for production beyond ~10k entries. Use the `pg` driver
204
+ * (with pgvector) or `redis` (with RediSearch) at scale.
205
+ *
206
+ * @warning Dev-only — O(N) per query.
207
+ */
208
+ async similar(vector, options) {
209
+ const tagFilter = await this.getKeysForTags(options.tags);
210
+ const hits = [];
211
+ for (const [parsedKey, stored] of this.vectorIndex) {
212
+ if (tagFilter && !tagFilter.has(parsedKey)) continue;
213
+ const value = await this.get(parsedKey);
214
+ if (value === null) continue;
215
+ const score = cosineSimilarity(vector, stored);
216
+ if (options.threshold !== void 0 && score < options.threshold) continue;
217
+ hits.push({
218
+ key: parsedKey,
219
+ value,
220
+ score
221
+ });
222
+ }
223
+ hits.sort((a, b) => b.score - a.score);
224
+ if (options.topK >= 0 && hits.length > options.topK) hits.length = options.topK;
225
+ return hits;
226
+ }
227
+ /**
228
+ * {@inheritdoc}
229
+ */
230
+ async disconnect() {
231
+ if (this.cleanupInterval) {
232
+ clearInterval(this.cleanupInterval);
233
+ this.cleanupInterval = void 0;
234
+ }
235
+ await super.disconnect();
236
+ }
237
+ };
238
+
239
+ //#endregion
240
+ export { MemoryCacheDriver };
241
+ //# sourceMappingURL=memory-cache-driver.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-cache-driver.mjs","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/memory-cache-driver.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\r\nimport { get, set, unset } from \"@mongez/reinforcements\";\r\nimport type {\r\n CacheData,\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheSetResult,\r\n CacheSimilarHit,\r\n CacheSimilarOptions,\r\n CacheTtl,\r\n MemoryCacheOptions,\r\n} from \"../types\";\r\nimport { cosineSimilarity } from \"../utils\";\r\nimport { BaseCacheDriver } from \"./base-cache-driver\";\r\n\r\nexport class MemoryCacheDriver\r\n extends BaseCacheDriver<MemoryCacheDriver, MemoryCacheOptions>\r\n implements CacheDriver<MemoryCacheDriver, MemoryCacheOptions>\r\n{\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"memory\";\r\n\r\n /**\r\n * Cached data\r\n */\r\n public data: GenericObject = {};\r\n\r\n /**\r\n * List of data that will be cleared from cache\r\n */\r\n protected temporaryData: Record<\r\n string,\r\n {\r\n key: string;\r\n expiresAt: number;\r\n }\r\n > = {};\r\n\r\n /**\r\n * Cleanup interval reference\r\n */\r\n protected cleanupInterval?: NodeJS.Timeout;\r\n\r\n /**\r\n * Access order tracking for LRU eviction (when maxSize is set)\r\n */\r\n protected accessOrder: string[] = [];\r\n\r\n /**\r\n * Parallel vector index keyed by parsedKey. Populated by `set({ vector })`,\r\n * scanned by `similar()`. Lifetime mirrors the main entry — cleared on\r\n * `remove`, `flush`, expiry, namespace clear, and LRU eviction.\r\n */\r\n protected vectorIndex: Map<string, number[]> = new Map();\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public constructor() {\r\n super();\r\n\r\n this.startCleanup();\r\n }\r\n\r\n /**\r\n * Start the cleanup process whenever a data that has a cache key is set\r\n */\r\n public startCleanup() {\r\n // Clear existing interval if any\r\n if (this.cleanupInterval) {\r\n clearInterval(this.cleanupInterval);\r\n }\r\n\r\n this.cleanupInterval = setInterval(async () => {\r\n const now = Date.now();\r\n\r\n for (const key in this.temporaryData) {\r\n if (this.temporaryData[key].expiresAt <= now) {\r\n await this.remove(this.temporaryData[key].key);\r\n delete this.temporaryData[key];\r\n\r\n this.log(\"expired\", key);\r\n // Emit expired event\r\n await this.emit(\"expired\", { key });\r\n }\r\n }\r\n }, 1000);\r\n\r\n // do not block the process from exiting\r\n this.cleanupInterval.unref();\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async removeNamespace(namespace: string) {\r\n this.log(\"clearing\", namespace);\r\n\r\n namespace = this.parseKey(namespace);\r\n\r\n unset(this.data, [namespace]);\r\n\r\n // Drop vector entries that fall under this namespace.\r\n if (namespace === \"\") {\r\n this.vectorIndex.clear();\r\n } else {\r\n const prefix = namespace + \".\";\r\n for (const k of [...this.vectorIndex.keys()]) {\r\n if (k === namespace || k.startsWith(prefix)) {\r\n this.vectorIndex.delete(k);\r\n }\r\n }\r\n }\r\n\r\n this.log(\"cleared\", namespace);\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async set(\r\n key: CacheKey,\r\n value: any,\r\n ttlOrOptions?: CacheTtl | CacheSetOptions,\r\n ): Promise<any> {\r\n const parsedKey = this.parseKey(key);\r\n const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);\r\n\r\n this.log(\"caching\", parsedKey);\r\n\r\n // Use get() for the existence check so expired entries are treated as\r\n // missing (and cleaned up as a side effect). A raw map lookup would let\r\n // stale entries block onConflict: \"create\" even after their TTL elapsed.\r\n const existingValue = onConflict === \"upsert\" ? null : await this.get(key);\r\n const exists = existingValue !== null;\r\n\r\n if (onConflict === \"create\" && exists) {\r\n const result: CacheSetResult = { wasSet: false, existing: existingValue };\r\n return result;\r\n }\r\n\r\n if (onConflict === \"update\" && !exists) {\r\n const result: CacheSetResult = { wasSet: false, existing: null };\r\n return result;\r\n }\r\n\r\n const data = this.prepareDataForStorage(value, ttl, staleAt);\r\n\r\n if (ttl) {\r\n this.setTemporaryData(key, parsedKey, ttl);\r\n }\r\n\r\n set(this.data, parsedKey, data);\r\n\r\n this.trackAccess(parsedKey);\r\n\r\n if (!exists && this.options.maxSize) {\r\n await this.enforceMaxSize();\r\n }\r\n\r\n if (tags && tags.length > 0) {\r\n await this.applyTags(parsedKey, tags);\r\n }\r\n\r\n if (vector) {\r\n this.vectorIndex.set(parsedKey, vector.slice());\r\n }\r\n\r\n this.log(\"cached\", parsedKey);\r\n\r\n await this.emit(\"set\", { key: parsedKey, value, ttl });\r\n\r\n if (onConflict === \"create\" || onConflict === \"update\") {\r\n const result: CacheSetResult = { wasSet: true, existing: null };\r\n return result;\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async get(key: CacheKey) {\r\n const parsedKey = this.parseKey(key);\r\n\r\n this.log(\"fetching\", parsedKey);\r\n\r\n const value: CacheData = get(this.data, parsedKey);\r\n\r\n if (!value) {\r\n this.log(\"notFound\", parsedKey);\r\n // Emit miss event\r\n await this.emit(\"miss\", { key: parsedKey });\r\n return null;\r\n }\r\n\r\n const result = await this.parseCachedData(parsedKey, value);\r\n\r\n if (result === null) {\r\n // Expired\r\n await this.emit(\"miss\", { key: parsedKey });\r\n } else {\r\n // Track access for LRU\r\n this.trackAccess(parsedKey);\r\n // Emit hit event\r\n await this.emit(\"hit\", { key: parsedKey, value: result });\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Read the raw {@link CacheData} wrapper, including `staleAt` metadata.\r\n * Returns `null` for missing or expired entries so the SWR flow can branch\r\n * cleanly. Does not emit `hit`/`miss` events — that's `get()`'s job.\r\n */\r\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\r\n const parsedKey = this.parseKey(key);\r\n const entry: CacheData | undefined = get(this.data, parsedKey);\r\n\r\n if (!entry) {\r\n return null;\r\n }\r\n\r\n if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {\r\n return null;\r\n }\r\n\r\n return entry;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remove(key: CacheKey) {\r\n const parsedKey = this.parseKey(key);\r\n\r\n this.log(\"removing\", parsedKey);\r\n\r\n unset(this.data, [parsedKey]);\r\n\r\n // Clean up from temporaryData as well\r\n delete this.temporaryData[parsedKey];\r\n\r\n // Remove from access order\r\n this.removeFromAccessOrder(parsedKey);\r\n\r\n // Drop the vector index entry if any\r\n this.vectorIndex.delete(parsedKey);\r\n\r\n this.log(\"removed\", parsedKey);\r\n\r\n // Emit removed event\r\n await this.emit(\"removed\", { key: parsedKey });\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async flush() {\r\n this.log(\"flushing\");\r\n if (this.options.globalPrefix) {\r\n this.removeNamespace(\"\");\r\n } else {\r\n this.data = {};\r\n this.accessOrder = [];\r\n this.vectorIndex.clear();\r\n }\r\n\r\n this.log(\"flushed\");\r\n\r\n // Emit flushed event\r\n await this.emit(\"flushed\");\r\n }\r\n\r\n /**\r\n * Set the temporary data\r\n */\r\n protected setTemporaryData(key: CacheKey, parsedKey: string, ttl: number) {\r\n this.temporaryData[parsedKey] = {\r\n key: JSON.stringify(key),\r\n expiresAt: Date.now() + ttl * 1000,\r\n };\r\n }\r\n\r\n /**\r\n * Track access for LRU eviction\r\n */\r\n protected trackAccess(key: string) {\r\n if (!this.options.maxSize) return;\r\n\r\n // Remove key from current position\r\n const index = this.accessOrder.indexOf(key);\r\n if (index > -1) {\r\n this.accessOrder.splice(index, 1);\r\n }\r\n\r\n // Add to end (most recently used)\r\n this.accessOrder.push(key);\r\n }\r\n\r\n /**\r\n * Remove key from access order tracking\r\n */\r\n protected removeFromAccessOrder(key: string) {\r\n const index = this.accessOrder.indexOf(key);\r\n if (index > -1) {\r\n this.accessOrder.splice(index, 1);\r\n }\r\n }\r\n\r\n /**\r\n * Enforce max size by evicting least recently used items.\r\n *\r\n * Recomputes the live cache size on every iteration — a single snapshot at\r\n * the top of the loop would go stale and cause this routine to evict every\r\n * entry in `accessOrder` (including the just-inserted key).\r\n */\r\n protected async enforceMaxSize() {\r\n if (!this.options.maxSize) {\r\n return;\r\n }\r\n\r\n while (\r\n this.getCacheSize() > this.options.maxSize &&\r\n this.accessOrder.length > 0\r\n ) {\r\n const lruKey = this.accessOrder.shift();\r\n if (!lruKey) {\r\n break;\r\n }\r\n\r\n this.log(\"removing\", lruKey);\r\n unset(this.data, [lruKey]);\r\n delete this.temporaryData[lruKey];\r\n this.vectorIndex.delete(lruKey);\r\n this.log(\"removed\", lruKey);\r\n }\r\n }\r\n\r\n /**\r\n * Get current cache size (number of cached items)\r\n */\r\n protected getCacheSize(): number {\r\n // Count top-level keys in data object\r\n return Object.keys(this.data).length;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Brute-force O(N) cosine similarity over every entry that was written with\r\n * `set({ vector })`. Suitable for development and small in-memory knowledge\r\n * bases — not for production beyond ~10k entries. Use the `pg` driver\r\n * (with pgvector) or `redis` (with RediSearch) at scale.\r\n *\r\n * @warning Dev-only — O(N) per query.\r\n */\r\n public async similar<T = any>(\r\n vector: number[],\r\n options: CacheSimilarOptions,\r\n ): Promise<CacheSimilarHit<T>[]> {\r\n const tagFilter = await this.getKeysForTags(options.tags);\r\n\r\n const hits: CacheSimilarHit<T>[] = [];\r\n\r\n for (const [parsedKey, stored] of this.vectorIndex) {\r\n if (tagFilter && !tagFilter.has(parsedKey)) {\r\n continue;\r\n }\r\n\r\n const value = (await this.get(parsedKey)) as T | null;\r\n // get() returns null for expired entries — and remove() drops the vector\r\n // index, so the next pass won't see it. Skip in case of timing.\r\n if (value === null) {\r\n continue;\r\n }\r\n\r\n const score = cosineSimilarity(vector, stored);\r\n\r\n if (options.threshold !== undefined && score < options.threshold) {\r\n continue;\r\n }\r\n\r\n hits.push({ key: parsedKey, value, score });\r\n }\r\n\r\n hits.sort((a, b) => b.score - a.score);\r\n\r\n if (options.topK >= 0 && hits.length > options.topK) {\r\n hits.length = options.topK;\r\n }\r\n\r\n return hits;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async disconnect() {\r\n // Clear the cleanup interval to prevent memory leaks\r\n if (this.cleanupInterval) {\r\n clearInterval(this.cleanupInterval);\r\n this.cleanupInterval = undefined;\r\n }\r\n\r\n await super.disconnect();\r\n }\r\n}\r\n"],"mappings":";;;;;AAgBA,IAAa,oBAAb,cACU,gBAEV;;;;CA0CE,AAAO,cAAc;EACnB,MAAM;cAvCM;cAKe,CAAC;uBAW1B,CAAC;qBAU6B,CAAC;qCAOY,IAAI,IAAI;EAQrD,KAAK,aAAa;CACpB;;;;CAKA,AAAO,eAAe;EAEpB,IAAI,KAAK,iBACP,cAAc,KAAK,eAAe;EAGpC,KAAK,kBAAkB,YAAY,YAAY;GAC7C,MAAM,MAAM,KAAK,IAAI;GAErB,KAAK,MAAM,OAAO,KAAK,eACrB,IAAI,KAAK,cAAc,KAAK,aAAa,KAAK;IAC5C,MAAM,KAAK,OAAO,KAAK,cAAc,KAAK,GAAG;IAC7C,OAAO,KAAK,cAAc;IAE1B,KAAK,IAAI,WAAW,GAAG;IAEvB,MAAM,KAAK,KAAK,WAAW,EAAE,IAAI,CAAC;GACpC;EAEJ,GAAG,GAAI;EAGP,KAAK,gBAAgB,MAAM;CAC7B;;;;CAKA,MAAa,gBAAgB,WAAmB;EAC9C,KAAK,IAAI,YAAY,SAAS;EAE9B,YAAY,KAAK,SAAS,SAAS;EAEnC,MAAM,KAAK,MAAM,CAAC,SAAS,CAAC;EAG5B,IAAI,cAAc,IAChB,KAAK,YAAY,MAAM;OAClB;GACL,MAAM,SAAS,YAAY;GAC3B,KAAK,MAAM,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,GACzC,IAAI,MAAM,aAAa,EAAE,WAAW,MAAM,GACxC,KAAK,YAAY,OAAO,CAAC;EAG/B;EAEA,KAAK,IAAI,WAAW,SAAS;EAE7B,OAAO;CACT;;;;CAKA,MAAa,IACX,KACA,OACA,cACc;EACd,MAAM,YAAY,KAAK,SAAS,GAAG;EACnC,MAAM,EAAE,KAAK,MAAM,YAAY,QAAQ,YAAY,KAAK,kBAAkB,YAAY;EAEtF,KAAK,IAAI,WAAW,SAAS;EAK7B,MAAM,gBAAgB,eAAe,WAAW,OAAO,MAAM,KAAK,IAAI,GAAG;EACzE,MAAM,SAAS,kBAAkB;EAEjC,IAAI,eAAe,YAAY,QAE7B,OAAO;GAD0B,QAAQ;GAAO,UAAU;EAC9C;EAGd,IAAI,eAAe,YAAY,CAAC,QAE9B,OAAO;GAD0B,QAAQ;GAAO,UAAU;EAC9C;EAGd,MAAM,OAAO,KAAK,sBAAsB,OAAO,KAAK,OAAO;EAE3D,IAAI,KACF,KAAK,iBAAiB,KAAK,WAAW,GAAG;EAG3C,IAAI,KAAK,MAAM,WAAW,IAAI;EAE9B,KAAK,YAAY,SAAS;EAE1B,IAAI,CAAC,UAAU,KAAK,QAAQ,SAC1B,MAAM,KAAK,eAAe;EAG5B,IAAI,QAAQ,KAAK,SAAS,GACxB,MAAM,KAAK,UAAU,WAAW,IAAI;EAGtC,IAAI,QACF,KAAK,YAAY,IAAI,WAAW,OAAO,MAAM,CAAC;EAGhD,KAAK,IAAI,UAAU,SAAS;EAE5B,MAAM,KAAK,KAAK,OAAO;GAAE,KAAK;GAAW;GAAO;EAAI,CAAC;EAErD,IAAI,eAAe,YAAY,eAAe,UAE5C,OAAO;GAD0B,QAAQ;GAAM,UAAU;EAC7C;EAGd,OAAO;CACT;;;;CAKA,MAAa,IAAI,KAAe;EAC9B,MAAM,YAAY,KAAK,SAAS,GAAG;EAEnC,KAAK,IAAI,YAAY,SAAS;EAE9B,MAAM,QAAmB,IAAI,KAAK,MAAM,SAAS;EAEjD,IAAI,CAAC,OAAO;GACV,KAAK,IAAI,YAAY,SAAS;GAE9B,MAAM,KAAK,KAAK,QAAQ,EAAE,KAAK,UAAU,CAAC;GAC1C,OAAO;EACT;EAEA,MAAM,SAAS,MAAM,KAAK,gBAAgB,WAAW,KAAK;EAE1D,IAAI,WAAW,MAEb,MAAM,KAAK,KAAK,QAAQ,EAAE,KAAK,UAAU,CAAC;OACrC;GAEL,KAAK,YAAY,SAAS;GAE1B,MAAM,KAAK,KAAK,OAAO;IAAE,KAAK;IAAW,OAAO;GAAO,CAAC;EAC1D;EAEA,OAAO;CACT;;;;;;CAOA,MAAgB,SAAS,KAA0C;EACjE,MAAM,YAAY,KAAK,SAAS,GAAG;EACnC,MAAM,QAA+B,IAAI,KAAK,MAAM,SAAS;EAE7D,IAAI,CAAC,OACH,OAAO;EAGT,IAAI,MAAM,cAAc,UAAa,MAAM,aAAa,KAAK,IAAI,GAC/D,OAAO;EAGT,OAAO;CACT;;;;CAKA,MAAa,OAAO,KAAe;EACjC,MAAM,YAAY,KAAK,SAAS,GAAG;EAEnC,KAAK,IAAI,YAAY,SAAS;EAE9B,MAAM,KAAK,MAAM,CAAC,SAAS,CAAC;EAG5B,OAAO,KAAK,cAAc;EAG1B,KAAK,sBAAsB,SAAS;EAGpC,KAAK,YAAY,OAAO,SAAS;EAEjC,KAAK,IAAI,WAAW,SAAS;EAG7B,MAAM,KAAK,KAAK,WAAW,EAAE,KAAK,UAAU,CAAC;CAC/C;;;;CAKA,MAAa,QAAQ;EACnB,KAAK,IAAI,UAAU;EACnB,IAAI,KAAK,QAAQ,cACf,KAAK,gBAAgB,EAAE;OAClB;GACL,KAAK,OAAO,CAAC;GACb,KAAK,cAAc,CAAC;GACpB,KAAK,YAAY,MAAM;EACzB;EAEA,KAAK,IAAI,SAAS;EAGlB,MAAM,KAAK,KAAK,SAAS;CAC3B;;;;CAKA,AAAU,iBAAiB,KAAe,WAAmB,KAAa;EACxE,KAAK,cAAc,aAAa;GAC9B,KAAK,KAAK,UAAU,GAAG;GACvB,WAAW,KAAK,IAAI,IAAI,MAAM;EAChC;CACF;;;;CAKA,AAAU,YAAY,KAAa;EACjC,IAAI,CAAC,KAAK,QAAQ,SAAS;EAG3B,MAAM,QAAQ,KAAK,YAAY,QAAQ,GAAG;EAC1C,IAAI,QAAQ,IACV,KAAK,YAAY,OAAO,OAAO,CAAC;EAIlC,KAAK,YAAY,KAAK,GAAG;CAC3B;;;;CAKA,AAAU,sBAAsB,KAAa;EAC3C,MAAM,QAAQ,KAAK,YAAY,QAAQ,GAAG;EAC1C,IAAI,QAAQ,IACV,KAAK,YAAY,OAAO,OAAO,CAAC;CAEpC;;;;;;;;CASA,MAAgB,iBAAiB;EAC/B,IAAI,CAAC,KAAK,QAAQ,SAChB;EAGF,OACE,KAAK,aAAa,IAAI,KAAK,QAAQ,WACnC,KAAK,YAAY,SAAS,GAC1B;GACA,MAAM,SAAS,KAAK,YAAY,MAAM;GACtC,IAAI,CAAC,QACH;GAGF,KAAK,IAAI,YAAY,MAAM;GAC3B,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC;GACzB,OAAO,KAAK,cAAc;GAC1B,KAAK,YAAY,OAAO,MAAM;GAC9B,KAAK,IAAI,WAAW,MAAM;EAC5B;CACF;;;;CAKA,AAAU,eAAuB;EAE/B,OAAO,OAAO,KAAK,KAAK,IAAI,EAAE;CAChC;;;;;;;;;;;CAYA,MAAa,QACX,QACA,SAC+B;EAC/B,MAAM,YAAY,MAAM,KAAK,eAAe,QAAQ,IAAI;EAExD,MAAM,OAA6B,CAAC;EAEpC,KAAK,MAAM,CAAC,WAAW,WAAW,KAAK,aAAa;GAClD,IAAI,aAAa,CAAC,UAAU,IAAI,SAAS,GACvC;GAGF,MAAM,QAAS,MAAM,KAAK,IAAI,SAAS;GAGvC,IAAI,UAAU,MACZ;GAGF,MAAM,QAAQ,iBAAiB,QAAQ,MAAM;GAE7C,IAAI,QAAQ,cAAc,UAAa,QAAQ,QAAQ,WACrD;GAGF,KAAK,KAAK;IAAE,KAAK;IAAW;IAAO;GAAM,CAAC;EAC5C;EAEA,KAAK,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;EAErC,IAAI,QAAQ,QAAQ,KAAK,KAAK,SAAS,QAAQ,MAC7C,KAAK,SAAS,QAAQ;EAGxB,OAAO;CACT;;;;CAKA,MAAa,aAAa;EAExB,IAAI,KAAK,iBAAiB;GACxB,cAAc,KAAK,eAAe;GAClC,KAAK,kBAAkB;EACzB;EAEA,MAAM,MAAM,WAAW;CACzB;AACF"}
@@ -0,0 +1,17 @@
1
+ import { MemoryCacheDriver } from "./memory-cache-driver.mjs";
2
+ import { CacheDriver, CacheKey, MemoryExtendedCacheOptions } from "../types.mjs";
3
+
4
+ //#region ../../@warlock.js/cache/src/drivers/memory-extended-cache-driver.d.ts
5
+ declare class MemoryExtendedCacheDriver extends MemoryCacheDriver implements CacheDriver<MemoryExtendedCacheDriver, MemoryExtendedCacheOptions> {
6
+ /**
7
+ * {@inheritdoc}
8
+ */
9
+ name: string;
10
+ /**
11
+ * {@inheritdoc}
12
+ */
13
+ get(key: CacheKey): Promise<any>;
14
+ }
15
+ //#endregion
16
+ export { MemoryExtendedCacheDriver };
17
+ //# sourceMappingURL=memory-extended-cache-driver.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-extended-cache-driver.d.mts","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/memory-extended-cache-driver.ts"],"mappings":";;;;cAKa,yBAAA,SACH,iBAAA,YACG,WAAA,CAAY,yBAAA,EAA2B,0BAAA;;AAFpD;;EAOS,IAAA;EALgB;;;EAUV,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;AAAA"}
@@ -0,0 +1,34 @@
1
+ import { parseTtl } from "../utils.mjs";
2
+ import { MemoryCacheDriver } from "./memory-cache-driver.mjs";
3
+ import { get } from "@mongez/reinforcements";
4
+
5
+ //#region ../../@warlock.js/cache/src/drivers/memory-extended-cache-driver.ts
6
+ var MemoryExtendedCacheDriver = class extends MemoryCacheDriver {
7
+ constructor(..._args) {
8
+ super(..._args);
9
+ this.name = "memoryExtended";
10
+ }
11
+ /**
12
+ * {@inheritdoc}
13
+ */
14
+ async get(key) {
15
+ const parsedKey = this.parseKey(key);
16
+ this.log("fetching", parsedKey);
17
+ const value = get(this.data, parsedKey);
18
+ if (!value) {
19
+ this.log("notFound", parsedKey);
20
+ return null;
21
+ }
22
+ const rawTtl = value.ttl ?? this.options.ttl;
23
+ const ttl = rawTtl !== void 0 ? parseTtl(rawTtl) : void 0;
24
+ if (ttl) {
25
+ this.setTemporaryData(key, parsedKey, ttl);
26
+ value.expiresAt = this.getExpiresAt(ttl);
27
+ }
28
+ return this.parseCachedData(parsedKey, value);
29
+ }
30
+ };
31
+
32
+ //#endregion
33
+ export { MemoryExtendedCacheDriver };
34
+ //# sourceMappingURL=memory-extended-cache-driver.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-extended-cache-driver.mjs","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/memory-extended-cache-driver.ts"],"sourcesContent":["import { get } from \"@mongez/reinforcements\";\r\nimport type { CacheData, CacheDriver, CacheKey, MemoryExtendedCacheOptions } from \"../types\";\r\nimport { parseTtl } from \"../utils\";\r\nimport { MemoryCacheDriver } from \"./memory-cache-driver\";\r\n\r\nexport class MemoryExtendedCacheDriver\r\n extends MemoryCacheDriver\r\n implements CacheDriver<MemoryExtendedCacheDriver, MemoryExtendedCacheOptions>\r\n{\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"memoryExtended\";\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async get(key: CacheKey) {\r\n const parsedKey = this.parseKey(key);\r\n\r\n this.log(\"fetching\", parsedKey);\r\n\r\n const value: CacheData = get(this.data, parsedKey);\r\n\r\n if (!value) {\r\n this.log(\"notFound\", parsedKey);\r\n return null;\r\n }\r\n\r\n const rawTtl = value.ttl ?? this.options.ttl;\r\n const ttl = rawTtl !== undefined ? parseTtl(rawTtl) : undefined;\r\n\r\n if (ttl) {\r\n // reset the expiration time\r\n this.setTemporaryData(key, parsedKey, ttl);\r\n value.expiresAt = this.getExpiresAt(ttl);\r\n }\r\n\r\n return this.parseCachedData(parsedKey, value);\r\n }\r\n}\r\n"],"mappings":";;;;;AAKA,IAAa,4BAAb,cACU,kBAEV;;;cAIgB;;;;;CAKd,MAAa,IAAI,KAAe;EAC9B,MAAM,YAAY,KAAK,SAAS,GAAG;EAEnC,KAAK,IAAI,YAAY,SAAS;EAE9B,MAAM,QAAmB,IAAI,KAAK,MAAM,SAAS;EAEjD,IAAI,CAAC,OAAO;GACV,KAAK,IAAI,YAAY,SAAS;GAC9B,OAAO;EACT;EAEA,MAAM,SAAS,MAAM,OAAO,KAAK,QAAQ;EACzC,MAAM,MAAM,WAAW,SAAY,SAAS,MAAM,IAAI;EAEtD,IAAI,KAAK;GAEP,KAAK,iBAAiB,KAAK,WAAW,GAAG;GACzC,MAAM,YAAY,KAAK,aAAa,GAAG;EACzC;EAEA,OAAO,KAAK,gBAAgB,WAAW,KAAK;CAC9C;AACF"}
@@ -0,0 +1,137 @@
1
+ import { BaseCacheDriver } from "./base-cache-driver.mjs";
2
+ import { CacheCall, CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheTtl, MockCacheOptions } from "../types.mjs";
3
+
4
+ //#region ../../@warlock.js/cache/src/drivers/mock-cache-driver.d.ts
5
+ /**
6
+ * In-memory cache driver with introspection helpers, intended for use as a
7
+ * test double in downstream packages.
8
+ *
9
+ * **Role.** Drop-in replacement for any real driver in test setups, with
10
+ * extra surface that makes behavioral assertions easy: every public op gets
11
+ * recorded into {@link MockCacheDriver.callLog}, and {@link wasCalled} /
12
+ * {@link getStored} / {@link reset} let tests verify side effects without
13
+ * pulling in a real Redis / Postgres / file system.
14
+ *
15
+ * **Responsibility.**
16
+ * - Owns: in-memory storage backed by a `Map`, the `callLog`, TTL handling
17
+ * via `parseCachedData`, `onConflict` policies, and tag-index storage.
18
+ * - Does NOT own: similarity retrieval (vectors are recorded into the
19
+ * `callLog` but `similar()` throws — use `MemoryCacheDriver` for tests
20
+ * that need real nearest-neighbor scoring), connection lifecycle
21
+ * (no-op `connect`/`disconnect`), or eviction (no `maxSize`).
22
+ *
23
+ * Register it like any other driver — sub-paths are not part of the
24
+ * package's export convention; the same single barrel ships it next to
25
+ * the production drivers.
26
+ *
27
+ * @example
28
+ * import { cache, MockCacheDriver } from "@warlock.js/cache";
29
+ *
30
+ * beforeEach(async () => {
31
+ * cache.setCacheConfigurations({
32
+ * default: "mock",
33
+ * drivers: { mock: MockCacheDriver },
34
+ * options: { mock: {} },
35
+ * });
36
+ * await cache.init();
37
+ * });
38
+ *
39
+ * it("invalidates the user cache after update", async () => {
40
+ * await userService.update(42, { name: "Jane" });
41
+ *
42
+ * const driver = cache.currentDriver as MockCacheDriver;
43
+ * expect(driver.wasCalled("remove", "users.42")).toBe(true);
44
+ * });
45
+ */
46
+ declare class MockCacheDriver extends BaseCacheDriver<MockCacheDriver, MockCacheOptions> implements CacheDriver<MockCacheDriver, MockCacheOptions> {
47
+ /**
48
+ * {@inheritdoc}
49
+ */
50
+ name: string;
51
+ /**
52
+ * {@inheritdoc}
53
+ */
54
+ options: MockCacheOptions;
55
+ /**
56
+ * Storage backing the mock — keyed by post-`parseKey` string. Public-readonly
57
+ * so tests can introspect raw entries when {@link getStored} isn't enough.
58
+ */
59
+ readonly storage: Map<string, CacheData>;
60
+ /**
61
+ * Ordered record of every public operation routed through this driver.
62
+ * Pushed to before each op runs; tests assert via {@link wasCalled} or by
63
+ * inspecting the array directly.
64
+ */
65
+ readonly callLog: CacheCall[];
66
+ /**
67
+ * Standard driver setup. Mirrors the null driver — no connection, no
68
+ * resources to release.
69
+ */
70
+ connect(): Promise<void>;
71
+ /**
72
+ * Standard driver teardown.
73
+ */
74
+ disconnect(): Promise<void>;
75
+ /**
76
+ * Wipe everything under `namespace`. Matches `MemoryCacheDriver` semantics
77
+ * — the namespace itself and any key with the namespace prefix is removed.
78
+ */
79
+ removeNamespace(namespace: string): Promise<void>;
80
+ /**
81
+ * Standard `set` with full `onConflict` support. Honors scope-default TTL
82
+ * via {@link BaseCacheDriver.resolveSetOptions}.
83
+ */
84
+ set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;
85
+ /**
86
+ * Standard `get` with TTL handling. Emits `hit` / `miss` events to keep
87
+ * downstream metrics tests realistic.
88
+ */
89
+ get<T = any>(key: CacheKey): Promise<T | null>;
90
+ /**
91
+ * Read the raw {@link CacheData} wrapper from the in-memory `Map`,
92
+ * including `staleAt` metadata. Returns `null` for missing or expired
93
+ * entries — `swr()` consumes this to branch on freshness.
94
+ */
95
+ protected getEntry(key: CacheKey): Promise<CacheData | null>;
96
+ /**
97
+ * Standard `remove` — drops the entry and emits the `removed` event.
98
+ */
99
+ remove(key: CacheKey): Promise<void>;
100
+ /**
101
+ * Standard `flush` — wipes the entire mock store + tag index. Does NOT
102
+ * touch the call log; use {@link reset} to clear that as well.
103
+ */
104
+ flush(): Promise<void>;
105
+ /**
106
+ * Was a given operation invoked? When `key` is provided, the match is
107
+ * post-`parseKey` so callers pass the same key shape they used at the
108
+ * call site — strings or objects, both resolve to the same parsed key.
109
+ *
110
+ * @example
111
+ * driver.wasCalled("set"); // any set
112
+ * driver.wasCalled("set", "users.42"); // set on this specific key
113
+ * driver.wasCalled("set", { id: 42 }); // same — object key normalized
114
+ */
115
+ wasCalled(operation: string, key?: CacheKey): boolean;
116
+ /**
117
+ * Return the raw stored value for `key`, bypassing TTL handling and clone
118
+ * protection. Useful when a test wants to assert on the persisted shape
119
+ * (or assert that an entry expired without going through `get`).
120
+ *
121
+ * Returns `undefined` when the key isn't present.
122
+ */
123
+ getStored<T = any>(key: CacheKey): T | undefined;
124
+ /**
125
+ * Wipe everything — storage, tag index, and the call log. Pair with
126
+ * Vitest's `beforeEach` to get clean isolation between tests.
127
+ */
128
+ reset(): void;
129
+ /**
130
+ * Append a row to {@link callLog}. Internal helper called by every
131
+ * recorded op before the actual work runs.
132
+ */
133
+ protected recordCall(operation: string, key: string | undefined, args?: unknown[]): void;
134
+ }
135
+ //#endregion
136
+ export { MockCacheDriver };
137
+ //# sourceMappingURL=mock-cache-driver.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-cache-driver.d.mts","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/mock-cache-driver.ts"],"mappings":";;;;;;AAqDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,eAAA,SACH,eAAA,CAAgB,eAAA,EAAiB,gBAAA,aAC9B,WAAA,CAAY,eAAA,EAAiB,gBAAA;EAKjC;;;EAAA,IAAA;EAWkB;;;EANlB,OAAA,EAAS,gBAAA;EAmBH;;;;EAAA,SAbG,OAAA,EAAS,GAAA,SAAY,SAAA;EA8BR;;;;;EAAA,SAvBb,OAAA,EAAS,SAAA;EA+CR;;;;EAzCJ,OAAA,CAAA,GAAW,OAAA;EAuFP;;;EA/EJ,UAAA,CAAA,GAAc,OAAA;EA+EuB;;;;EAtErC,eAAA,CAAgB,SAAA,WAAoB,OAAA;EAyGA;;;;EApFpC,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EAkHU;;;;EArEA,GAAA,SAAA,CAAa,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,CAAA;EAyFd;;;;;EAAA,UAtDpB,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;EAwF1C;;;EAtEM,MAAA,CAAO,GAAA,EAAK,QAAA,GAAW,OAAA;EAkFlC;;AAAoB;;EAlET,KAAA,CAAA,GAAS,OAAA;;;;;;;;;;;EAoBf,SAAA,CAAU,SAAA,UAAmB,GAAA,GAAM,QAAA;;;;;;;;EAmBnC,SAAA,SAAA,CAAmB,GAAA,EAAK,QAAA,GAAW,CAAA;;;;;EAenC,KAAA,CAAA;;;;;YASG,UAAA,CACR,SAAA,UACA,GAAA,sBACA,IAAA;AAAA"}