@warlock.js/cache 5.19.1 → 5.20.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.
- package/CHANGELOG.md +55 -0
- package/cjs/index.cjs +1439 -331
- package/cjs/index.cjs.map +1 -1
- package/esm/cache-manager.d.mts +4 -0
- package/esm/cache-manager.d.mts.map +1 -1
- package/esm/cache-manager.mjs +23 -5
- package/esm/cache-manager.mjs.map +1 -1
- package/esm/cached/auto-key.d.mts +14 -14
- package/esm/cached/auto-key.d.mts.map +1 -1
- package/esm/cached/auto-key.mjs +50 -34
- package/esm/cached/auto-key.mjs.map +1 -1
- package/esm/cached/cached.mjs +4 -0
- package/esm/cached/cached.mjs.map +1 -1
- package/esm/drivers/base-cache-driver.d.mts +84 -8
- package/esm/drivers/base-cache-driver.d.mts.map +1 -1
- package/esm/drivers/base-cache-driver.mjs +152 -42
- package/esm/drivers/base-cache-driver.mjs.map +1 -1
- package/esm/drivers/file-cache-driver.d.mts +15 -0
- package/esm/drivers/file-cache-driver.d.mts.map +1 -1
- package/esm/drivers/file-cache-driver.mjs +68 -18
- package/esm/drivers/file-cache-driver.mjs.map +1 -1
- package/esm/drivers/in-memory-tag-index.d.mts +44 -0
- package/esm/drivers/in-memory-tag-index.d.mts.map +1 -0
- package/esm/drivers/in-memory-tag-index.mjs +73 -0
- package/esm/drivers/in-memory-tag-index.mjs.map +1 -0
- package/esm/drivers/index.d.mts +1 -1
- package/esm/drivers/index.mjs +1 -1
- package/esm/drivers/lru-memory-cache-driver.d.mts +43 -0
- package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/lru-memory-cache-driver.mjs +96 -0
- package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/memory-cache-driver.d.mts +87 -30
- package/esm/drivers/memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/memory-cache-driver.mjs +214 -96
- package/esm/drivers/memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/memory-extended-cache-driver.d.mts +9 -5
- package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -1
- package/esm/drivers/memory-extended-cache-driver.mjs +10 -23
- package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -1
- package/esm/drivers/mock-cache-driver.d.mts +41 -0
- package/esm/drivers/mock-cache-driver.d.mts.map +1 -1
- package/esm/drivers/mock-cache-driver.mjs +105 -8
- package/esm/drivers/mock-cache-driver.mjs.map +1 -1
- package/esm/drivers/null-cache-driver.d.mts +8 -2
- package/esm/drivers/null-cache-driver.d.mts.map +1 -1
- package/esm/drivers/null-cache-driver.mjs +10 -2
- package/esm/drivers/null-cache-driver.mjs.map +1 -1
- package/esm/drivers/pg-cache-driver.d.mts +77 -3
- package/esm/drivers/pg-cache-driver.d.mts.map +1 -1
- package/esm/drivers/pg-cache-driver.mjs +229 -7
- package/esm/drivers/pg-cache-driver.mjs.map +1 -1
- package/esm/drivers/redis-cache-driver.d.mts +86 -1
- package/esm/drivers/redis-cache-driver.d.mts.map +1 -1
- package/esm/drivers/redis-cache-driver.mjs +252 -16
- package/esm/drivers/redis-cache-driver.mjs.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/index.mjs +2 -2
- package/esm/list/memory-cache-list.d.mts +11 -8
- package/esm/list/memory-cache-list.d.mts.map +1 -1
- package/esm/list/memory-cache-list.mjs +39 -37
- package/esm/list/memory-cache-list.mjs.map +1 -1
- package/esm/scoped-cache.d.mts +9 -3
- package/esm/scoped-cache.d.mts.map +1 -1
- package/esm/scoped-cache.mjs +19 -7
- package/esm/scoped-cache.mjs.map +1 -1
- package/esm/tagged-cache.d.mts +20 -0
- package/esm/tagged-cache.d.mts.map +1 -1
- package/esm/tagged-cache.mjs +62 -24
- package/esm/tagged-cache.mjs.map +1 -1
- package/esm/tagged-scoped-cache.d.mts +7 -3
- package/esm/tagged-scoped-cache.d.mts.map +1 -1
- package/esm/tagged-scoped-cache.mjs +19 -7
- package/esm/tagged-scoped-cache.mjs.map +1 -1
- package/esm/types.d.mts +41 -3
- package/esm/types.d.mts.map +1 -1
- package/esm/types.mjs.map +1 -1
- package/esm/utils.d.mts +9 -3
- package/esm/utils.d.mts.map +1 -1
- package/esm/utils.mjs +16 -6
- package/esm/utils.mjs.map +1 -1
- package/llms-full.txt +1685 -1666
- package/llms.txt +18 -18
- package/package.json +4 -4
- package/skills/apply-cache-patterns/SKILL.md +98 -97
- package/skills/configure-pg-cache/SKILL.md +6 -5
- package/skills/configure-set-options/SKILL.md +102 -96
- package/skills/handle-cache-errors/SKILL.md +92 -92
- package/skills/index.md +20 -0
- package/skills/observe-cache/SKILL.md +103 -103
- package/skills/pick-cache-driver/SKILL.md +129 -123
- package/skills/test-cache-code/SKILL.md +219 -219
- package/skills/use-cache-atomic/SKILL.md +57 -65
- package/skills/use-cache-bulk/SKILL.md +57 -57
- package/skills/use-cache-list/SKILL.md +85 -85
- package/skills/use-cache-lock/SKILL.md +113 -104
- package/skills/use-cache-namespace/SKILL.md +88 -88
- package/skills/use-cache-similarity/SKILL.md +94 -94
- package/skills/use-cache-tags/SKILL.md +7 -4
- package/skills/use-cache-update-merge/SKILL.md +84 -84
- package/skills/use-cache-utils/SKILL.md +89 -89
- package/skills/use-cached-hof/SKILL.md +100 -99
- package/skills/use-swr/SKILL.md +104 -104
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lru-memory-cache-driver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/lru-memory-cache-driver.ts"],"sourcesContent":["import type {\n CacheData,\n CacheDriver,\n CacheKey,\n CacheSetOptions,\n CacheSetResult,\n CacheSimilarHit,\n CacheSimilarOptions,\n CacheTtl,\n LRUMemoryCacheOptions,\n} from \"../types\";\nimport { cosineSimilarity } from \"../utils\";\nimport { BaseCacheDriver } from \"./base-cache-driver\";\n\nclass CacheNode {\n public next: CacheNode | null = null;\n public prev: CacheNode | null = null;\n public expiresAt?: number;\n /**\n * Freshness deadline (ms timestamp) — populated by `swr()`. Within\n * `expiresAt > now > staleAt` the entry is \"stale-but-revalidatable.\"\n */\n public staleAt?: number;\n /**\n * Optional embedding vector — populated when the entry was written with\n * `set({ vector })`. Scanned by `similar()`.\n */\n public vector?: number[];\n public constructor(\n public key: string,\n public value: any,\n ttl?: number,\n ) {\n if (ttl && ttl !== Infinity) {\n this.expiresAt = Date.now() + ttl * 1000;\n }\n }\n\n public get isExpired(): boolean {\n return this.expiresAt !== undefined && this.expiresAt < Date.now();\n }\n}\n\n/**\n * LRU Memory Cache Driver\n * The concept of LRU is to remove the least recently used data\n * whenever the cache is full\n * The question that resides here is how to tell the cache is full?\n */\nexport class LRUMemoryCacheDriver\n extends BaseCacheDriver<LRUMemoryCacheDriver, LRUMemoryCacheOptions>\n implements CacheDriver<LRUMemoryCacheDriver, LRUMemoryCacheOptions>\n{\n /**\n * {@inheritdoc}\n */\n public name = \"lru\";\n\n /**\n * Cache map\n */\n protected cache: Map<string, CacheNode> = new Map();\n\n /**\n * Head of the cache\n */\n protected head: CacheNode = new CacheNode(\"\", null);\n\n /**\n * Tail of the cache\n */\n protected tail: CacheNode = new CacheNode(\"\", null);\n\n /**\n * Cleanup interval reference\n */\n protected cleanupInterval?: NodeJS.Timeout;\n\n /**\n * {@inheritdoc}\n */\n public constructor() {\n super();\n\n this.init();\n this.startCleanup();\n }\n\n /**\n * Initialize the cache\n */\n public init() {\n this.head.next = this.tail;\n this.tail.prev = this.head;\n }\n\n /**\n * Start the cleanup process for expired items\n */\n public startCleanup() {\n // Clear existing interval if any\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n }\n\n this.cleanupInterval = setInterval(async () => {\n const now = Date.now();\n const expiredKeys: string[] = [];\n\n for (const [key, node] of this.cache) {\n if (node.expiresAt && node.expiresAt <= now) {\n expiredKeys.push(key);\n }\n }\n\n for (const key of expiredKeys) {\n const node = this.cache.get(key);\n if (node) {\n this.removeNode(node);\n this.cache.delete(key);\n this.log(\"expired\", key);\n // Emit expired event\n await this.emit(\"expired\", { key });\n }\n }\n }, 1000);\n\n // do not block the process from exiting\n this.cleanupInterval.unref();\n }\n\n /**\n * {@inheritdoc}\n *\n * Clears every entry whose key starts with the parsed namespace (followed\n * by a dot) or equals it exactly. Called with an empty namespace while a\n * `globalPrefix` is configured, clears everything under the prefix — which\n * is how `flush()` scopes cleanup per tenant.\n */\n public async removeNamespace(namespace: string) {\n const parsedNamespace = this.parseKey(namespace);\n\n this.log(\"clearing\", parsedNamespace || \"(all)\");\n\n const removed: string[] = [];\n\n if (parsedNamespace === \"\") {\n for (const key of this.cache.keys()) {\n removed.push(key);\n }\n } else {\n const prefix = parsedNamespace + \".\";\n\n for (const key of this.cache.keys()) {\n if (key === parsedNamespace || key.startsWith(prefix)) {\n removed.push(key);\n }\n }\n }\n\n for (const key of removed) {\n const node = this.cache.get(key);\n if (node) {\n this.removeNode(node);\n this.cache.delete(key);\n }\n await this.emit(\"removed\", { key });\n }\n\n this.log(\"cleared\", parsedNamespace || \"(all)\");\n\n return removed;\n }\n\n /**\n * {@inheritdoc}\n */\n public async set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n const parsedKey = this.parseKey(key);\n const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);\n\n this.log(\"caching\", parsedKey);\n\n // For conditional writes, check existence via get() so expired entries\n // are treated as missing (get() handles expiry cleanup). The raw `cache.get`\n // node lookup would let stale entries block onConflict: \"create\".\n let existingNode = this.cache.get(parsedKey);\n if (existingNode && existingNode.isExpired) {\n this.removeNode(existingNode);\n this.cache.delete(parsedKey);\n existingNode = undefined;\n }\n\n const exists = Boolean(existingNode);\n\n if (onConflict === \"create\" && exists) {\n const result: CacheSetResult = {\n wasSet: false,\n existing: existingNode!.value,\n };\n return result;\n }\n\n if (onConflict === \"update\" && !exists) {\n const result: CacheSetResult = { wasSet: false, existing: null };\n return result;\n }\n\n if (existingNode) {\n existingNode.value = value;\n if (ttl && ttl !== Infinity) {\n existingNode.expiresAt = Date.now() + ttl * 1000;\n } else {\n existingNode.expiresAt = undefined;\n }\n existingNode.staleAt = staleAt;\n if (vector) {\n existingNode.vector = vector.slice();\n }\n\n this.moveHead(existingNode);\n } else {\n const newNode = new CacheNode(parsedKey, value, ttl);\n newNode.staleAt = staleAt;\n if (vector) {\n newNode.vector = vector.slice();\n }\n\n this.cache.set(parsedKey, newNode);\n\n this.addNode(newNode);\n if (this.cache.size > this.capacity) {\n this.removeTail();\n }\n }\n\n if (tags && tags.length > 0) {\n await this.applyTags(key, tags);\n }\n\n this.log(\"cached\", parsedKey);\n\n await this.emit(\"set\", { key: parsedKey, value, ttl });\n\n if (onConflict === \"create\" || onConflict === \"update\") {\n const result: CacheSetResult = { wasSet: true, existing: null };\n return result;\n }\n\n return this;\n }\n\n /**\n * Move the node to the head\n */\n protected moveHead(node: CacheNode) {\n this.removeNode(node);\n this.addNode(node);\n }\n\n /**\n * Remove the node from the cache\n */\n protected removeNode(node: CacheNode) {\n node.prev!.next = node.next;\n node.next!.prev = node.prev;\n }\n\n /**\n * Add the node to the head\n */\n protected addNode(node: CacheNode) {\n node.next = this.head.next;\n node.prev = this.head;\n this.head.next!.prev = node;\n this.head.next = node;\n }\n\n /**\n * Remove the tail node\n */\n protected removeTail() {\n const node = this.tail.prev!;\n\n this.removeNode(node);\n\n this.cache.delete(node.key);\n }\n\n /**\n * Read the raw {@link CacheData} wrapper, including `staleAt` metadata.\n * Returns `null` for missing or expired nodes — `swr()` consumes this\n * to branch on freshness without going through `get()`'s clone-and-emit\n * path.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const parsedKey = this.parseKey(key);\n const node = this.cache.get(parsedKey);\n\n if (!node || node.isExpired) {\n return null;\n }\n\n return {\n data: node.value,\n expiresAt: node.expiresAt,\n staleAt: node.staleAt,\n };\n }\n\n /**\n * {@inheritdoc}\n */\n public async get(key: CacheKey) {\n const parsedKey = this.parseKey(key);\n\n this.log(\"fetching\", parsedKey);\n\n const node = this.cache.get(parsedKey);\n\n if (!node) {\n this.log(\"notFound\", parsedKey);\n // Emit miss event\n await this.emit(\"miss\", { key: parsedKey });\n return null;\n }\n\n // Check if expired\n if (node.isExpired) {\n this.removeNode(node);\n this.cache.delete(parsedKey);\n this.log(\"expired\", parsedKey);\n // Emit expired event\n await this.emit(\"expired\", { key: parsedKey });\n // Also emit miss since we're returning null\n await this.emit(\"miss\", { key: parsedKey });\n return null;\n }\n\n this.moveHead(node);\n\n this.log(\"fetched\", parsedKey);\n\n const value = node.value;\n\n // Apply cloning for immutability protection\n if (value === null || value === undefined) {\n return value;\n }\n\n const type = typeof value;\n if (type === \"string\" || type === \"number\" || type === \"boolean\") {\n // Emit hit event\n await this.emit(\"hit\", { key: parsedKey, value });\n return value;\n }\n\n try {\n const clonedValue = structuredClone(value);\n // Emit hit event\n await this.emit(\"hit\", { key: parsedKey, value: clonedValue });\n return clonedValue;\n } catch (error) {\n this.logError(`Failed to clone cached value for ${parsedKey}`, error);\n throw error;\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async remove(key: CacheKey) {\n const parsedKey = this.parseKey(key);\n\n this.log(\"removing\", parsedKey);\n\n const node = this.cache.get(parsedKey);\n\n if (node) {\n this.removeNode(node);\n this.cache.delete(parsedKey);\n }\n\n this.log(\"removed\", parsedKey);\n\n // Emit removed event\n await this.emit(\"removed\", { key: parsedKey });\n }\n\n /**\n * {@inheritdoc}\n *\n * When a `globalPrefix` is configured, `flush` scopes itself to that prefix\n * so multi-tenant caches don't accidentally wipe sibling tenants. Without\n * a prefix, clears everything.\n */\n public async flush() {\n this.log(\"flushing\");\n\n if (this.options.globalPrefix) {\n await this.removeNamespace(\"\");\n } else {\n this.cache.clear();\n this.init();\n }\n\n this.log(\"flushed\");\n\n await this.emit(\"flushed\");\n }\n\n /**\n * {@inheritdoc}\n *\n * Brute-force O(N) cosine similarity over every cached node that carries a\n * vector. Suitable for development and small in-memory knowledge bases —\n * not for production beyond ~10k entries.\n *\n * @warning Dev-only — O(N) per query.\n */\n public async similar<T = any>(\n vector: number[],\n options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]> {\n const tagFilter = await this.getKeysForTags(options.tags);\n\n const hits: CacheSimilarHit<T>[] = [];\n\n for (const [parsedKey, node] of this.cache) {\n if (!node.vector) continue;\n if (node.isExpired) continue;\n if (tagFilter && !tagFilter.has(parsedKey)) continue;\n\n const score = cosineSimilarity(vector, node.vector);\n\n if (options.threshold !== undefined && score < options.threshold) {\n continue;\n }\n\n // Clone object values to match get() semantics.\n let value: any = node.value;\n if (value !== null && value !== undefined) {\n const t = typeof value;\n if (t !== \"string\" && t !== \"number\" && t !== \"boolean\") {\n value = structuredClone(value);\n }\n }\n\n hits.push({ key: parsedKey, value, score });\n }\n\n hits.sort((a, b) => b.score - a.score);\n\n if (options.topK >= 0 && hits.length > options.topK) {\n hits.length = options.topK;\n }\n\n return hits;\n }\n\n /**\n * Get lru capacity\n */\n public get capacity() {\n return this.options.capacity || 1000;\n }\n\n /**\n * {@inheritdoc}\n */\n public async disconnect() {\n // Clear the cleanup interval to prevent memory leaks\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = undefined;\n }\n\n await super.disconnect();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lru-memory-cache-driver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/lru-memory-cache-driver.ts"],"sourcesContent":["import type {\n CacheData,\n CacheDriver,\n CacheKey,\n CacheSetOptions,\n CacheSetResult,\n CacheSimilarHit,\n CacheSimilarOptions,\n CacheTtl,\n LRUMemoryCacheOptions,\n} from \"../types\";\nimport { cosineSimilarity } from \"../utils\";\nimport { BaseCacheDriver } from \"./base-cache-driver\";\nimport { InMemoryTagIndex } from \"./in-memory-tag-index\";\n\nclass CacheNode {\n public next: CacheNode | null = null;\n public prev: CacheNode | null = null;\n public expiresAt?: number;\n /**\n * Freshness deadline (ms timestamp) — populated by `swr()`. Within\n * `expiresAt > now > staleAt` the entry is \"stale-but-revalidatable.\"\n */\n public staleAt?: number;\n /**\n * Optional embedding vector — populated when the entry was written with\n * `set({ vector })`. Scanned by `similar()`.\n */\n public vector?: number[];\n public constructor(\n public key: string,\n public value: any,\n ttl?: number,\n ) {\n if (ttl && ttl !== Infinity) {\n this.expiresAt = Date.now() + ttl * 1000;\n }\n }\n\n public get isExpired(): boolean {\n return this.expiresAt !== undefined && this.expiresAt < Date.now();\n }\n}\n\n/**\n * LRU Memory Cache Driver\n * The concept of LRU is to remove the least recently used data\n * whenever the cache is full\n * The question that resides here is how to tell the cache is full?\n */\nexport class LRUMemoryCacheDriver\n extends BaseCacheDriver<LRUMemoryCacheDriver, LRUMemoryCacheOptions>\n implements CacheDriver<LRUMemoryCacheDriver, LRUMemoryCacheOptions>\n{\n /**\n * {@inheritdoc}\n */\n public name = \"lru\";\n\n /**\n * Cache map\n */\n protected cache: Map<string, CacheNode> = new Map();\n\n /**\n * Tag index, kept out of the LRU list so capacity pressure can never evict it.\n */\n protected tagIndex: InMemoryTagIndex = new InMemoryTagIndex();\n\n /**\n * Head of the cache\n */\n protected head: CacheNode = new CacheNode(\"\", null);\n\n /**\n * Tail of the cache\n */\n protected tail: CacheNode = new CacheNode(\"\", null);\n\n /**\n * Cleanup interval reference\n */\n protected cleanupInterval?: NodeJS.Timeout;\n\n /**\n * {@inheritdoc}\n */\n public constructor() {\n super();\n\n this.init();\n this.startCleanup();\n }\n\n /**\n * Initialize the cache\n */\n public init() {\n this.head.next = this.tail;\n this.tail.prev = this.head;\n }\n\n /**\n * Start the cleanup process for expired items\n */\n public startCleanup() {\n // Clear existing interval if any\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n }\n\n this.cleanupInterval = setInterval(async () => {\n const now = Date.now();\n const expiredKeys: string[] = [];\n\n for (const [key, node] of this.cache) {\n if (node.expiresAt && node.expiresAt <= now) {\n expiredKeys.push(key);\n }\n }\n\n for (const key of expiredKeys) {\n const node = this.cache.get(key);\n if (node) {\n this.removeNode(node);\n this.cache.delete(key);\n this.log(\"expired\", key);\n // Emit expired event\n await this.emit(\"expired\", { key });\n }\n }\n }, 1000);\n\n // do not block the process from exiting\n this.cleanupInterval.unref();\n }\n\n /**\n * {@inheritdoc}\n *\n * Clears every entry whose key starts with the parsed namespace (followed\n * by a dot) or equals it exactly. Called with an empty namespace while a\n * `globalPrefix` is configured, clears everything under the prefix — which\n * is how `flush()` scopes cleanup per tenant.\n */\n public async removeNamespace(namespace: string) {\n const parsedNamespace = this.parseKey(namespace);\n\n this.log(\"clearing\", parsedNamespace || \"(all)\");\n\n const removed: string[] = [];\n\n this.tagIndex.removeNamespace(parsedNamespace);\n\n if (parsedNamespace === \"\") {\n for (const key of this.cache.keys()) {\n removed.push(key);\n }\n } else {\n const prefix = parsedNamespace + \".\";\n\n for (const key of this.cache.keys()) {\n if (key === parsedNamespace || key.startsWith(prefix)) {\n removed.push(key);\n }\n }\n }\n\n for (const key of removed) {\n const node = this.cache.get(key);\n if (node) {\n this.removeNode(node);\n this.cache.delete(key);\n }\n await this.emit(\"removed\", { key });\n }\n\n this.log(\"cleared\", parsedNamespace || \"(all)\");\n\n return removed;\n }\n\n /**\n * {@inheritdoc}\n */\n public async set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n const parsedKey = this.parseKey(key);\n const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);\n\n this.log(\"caching\", parsedKey);\n\n // For conditional writes, check existence via get() so expired entries\n // are treated as missing (get() handles expiry cleanup). The raw `cache.get`\n // node lookup would let stale entries block onConflict: \"create\".\n let existingNode = this.cache.get(parsedKey);\n if (existingNode && existingNode.isExpired) {\n this.removeNode(existingNode);\n this.cache.delete(parsedKey);\n existingNode = undefined;\n }\n\n const exists = Boolean(existingNode);\n\n if (onConflict === \"create\" && exists) {\n const result: CacheSetResult = {\n wasSet: false,\n existing: existingNode!.value,\n };\n return result;\n }\n\n if (onConflict === \"update\" && !exists) {\n const result: CacheSetResult = { wasSet: false, existing: null };\n return result;\n }\n\n if (existingNode) {\n existingNode.value = value;\n if (ttl && ttl !== Infinity) {\n existingNode.expiresAt = Date.now() + ttl * 1000;\n } else {\n existingNode.expiresAt = undefined;\n }\n existingNode.staleAt = staleAt;\n if (vector) {\n existingNode.vector = vector.slice();\n }\n\n this.moveHead(existingNode);\n } else {\n const newNode = new CacheNode(parsedKey, value, ttl);\n newNode.staleAt = staleAt;\n if (vector) {\n newNode.vector = vector.slice();\n }\n\n this.cache.set(parsedKey, newNode);\n\n this.addNode(newNode);\n if (this.cache.size > this.capacity) {\n this.removeTail();\n }\n }\n\n if (tags && tags.length > 0) {\n await this.applyTags(key, tags);\n }\n\n this.log(\"cached\", parsedKey);\n\n await this.emit(\"set\", { key: parsedKey, value, ttl });\n\n if (onConflict === \"create\" || onConflict === \"update\") {\n const result: CacheSetResult = { wasSet: true, existing: null };\n return result;\n }\n\n return this;\n }\n\n /**\n * Move the node to the head\n */\n protected moveHead(node: CacheNode) {\n this.removeNode(node);\n this.addNode(node);\n }\n\n /**\n * Remove the node from the cache\n */\n protected removeNode(node: CacheNode) {\n node.prev!.next = node.next;\n node.next!.prev = node.prev;\n }\n\n /**\n * Add the node to the head\n */\n protected addNode(node: CacheNode) {\n node.next = this.head.next;\n node.prev = this.head;\n this.head.next!.prev = node;\n this.head.next = node;\n }\n\n /**\n * Remove the tail node\n */\n protected removeTail() {\n const node = this.tail.prev!;\n\n this.removeNode(node);\n\n this.cache.delete(node.key);\n }\n\n /**\n * Read the raw {@link CacheData} wrapper, including `staleAt` metadata.\n * Returns `null` for missing or expired nodes — `swr()` consumes this\n * to branch on freshness without going through `get()`'s clone-and-emit\n * path.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const parsedKey = this.parseKey(key);\n const node = this.cache.get(parsedKey);\n\n if (!node || node.isExpired) {\n return null;\n }\n\n return {\n data: node.value,\n expiresAt: node.expiresAt,\n staleAt: node.staleAt,\n };\n }\n\n /**\n * Live node for `parsedKey`, or `undefined` when missing / expired (an\n * expired node is unlinked). Synchronous.\n */\n protected liveNode(parsedKey: string): CacheNode | undefined {\n const node = this.cache.get(parsedKey);\n\n if (!node) {\n return undefined;\n }\n\n if (node.isExpired) {\n this.removeNode(node);\n this.cache.delete(parsedKey);\n\n return undefined;\n }\n\n return node;\n }\n\n /**\n * {@inheritdoc}\n *\n * Read, add and write run synchronously (no `await` in between), so\n * concurrent increments in this process never lose an update. An existing\n * node keeps its expiry; a missing key starts from 0 with the default TTL.\n */\n public async increment(key: CacheKey, value: number = 1): Promise<number> {\n const parsedKey = this.parseKey(key);\n const node = this.liveNode(parsedKey);\n const current = node ? node.value : 0;\n\n if (typeof current !== \"number\") {\n throw new Error(`Cannot increment non-numeric value for key: ${parsedKey}`);\n }\n\n const newValue = current + value;\n\n if (node) {\n node.value = newValue;\n this.moveHead(node);\n } else {\n const newNode = new CacheNode(parsedKey, newValue, this.ttl);\n\n this.cache.set(parsedKey, newNode);\n this.addNode(newNode);\n\n if (this.cache.size > this.capacity) {\n this.removeTail();\n }\n }\n\n await this.emit(\"set\", { key: parsedKey, value: newValue, ttl: this.ttl });\n\n return newValue;\n }\n\n /**\n * {@inheritdoc}\n *\n * Read and unlink run synchronously, so a value is handed out exactly once\n * even when concurrent callers pull the same key.\n */\n public async pull(key: CacheKey): Promise<any | null> {\n const parsedKey = this.parseKey(key);\n const node = this.liveNode(parsedKey);\n\n if (!node) {\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n this.removeNode(node);\n this.cache.delete(parsedKey);\n\n const value =\n node.value !== null && typeof node.value === \"object\"\n ? structuredClone(node.value)\n : node.value;\n\n await this.emit(\"hit\", { key: parsedKey, value });\n await this.emit(\"removed\", { key: parsedKey });\n\n return value;\n }\n\n /**\n * {@inheritdoc}\n */\n public async get(key: CacheKey) {\n const parsedKey = this.parseKey(key);\n\n this.log(\"fetching\", parsedKey);\n\n const node = this.cache.get(parsedKey);\n\n if (!node) {\n this.log(\"notFound\", parsedKey);\n // Emit miss event\n await this.emit(\"miss\", { key: parsedKey });\n return null;\n }\n\n // Check if expired\n if (node.isExpired) {\n this.removeNode(node);\n this.cache.delete(parsedKey);\n this.log(\"expired\", parsedKey);\n // Emit expired event\n await this.emit(\"expired\", { key: parsedKey });\n // Also emit miss since we're returning null\n await this.emit(\"miss\", { key: parsedKey });\n return null;\n }\n\n this.moveHead(node);\n\n this.log(\"fetched\", parsedKey);\n\n const value = node.value;\n\n // Apply cloning for immutability protection\n if (value === null || value === undefined) {\n return value;\n }\n\n const type = typeof value;\n if (type === \"string\" || type === \"number\" || type === \"boolean\") {\n // Emit hit event\n await this.emit(\"hit\", { key: parsedKey, value });\n return value;\n }\n\n try {\n const clonedValue = structuredClone(value);\n // Emit hit event\n await this.emit(\"hit\", { key: parsedKey, value: clonedValue });\n return clonedValue;\n } catch (error) {\n this.logError(`Failed to clone cached value for ${parsedKey}`, error);\n throw error;\n }\n }\n\n /**\n * {@inheritdoc}\n */\n public async remove(key: CacheKey) {\n const parsedKey = this.parseKey(key);\n\n this.log(\"removing\", parsedKey);\n\n const node = this.cache.get(parsedKey);\n\n if (node) {\n this.removeNode(node);\n this.cache.delete(parsedKey);\n }\n\n this.log(\"removed\", parsedKey);\n\n // Emit removed event\n await this.emit(\"removed\", { key: parsedKey });\n }\n\n /**\n * {@inheritdoc}\n *\n * Synchronous in-process set, never evicted or expired with the entries.\n */\n public async tagAdd(tagKey: CacheKey, members: string[]): Promise<void> {\n this.tagIndex.add(this.parseKey(tagKey), members);\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagMembers(tagKey: CacheKey): Promise<string[]> {\n return this.tagIndex.members(this.parseKey(tagKey));\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagRemove(tagKey: CacheKey, members: string[]): Promise<void> {\n this.tagIndex.remove(this.parseKey(tagKey), members);\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagDelete(tagKey: CacheKey): Promise<void> {\n this.tagIndex.delete(this.parseKey(tagKey));\n }\n\n /**\n * {@inheritdoc}\n *\n * When a `globalPrefix` is configured, `flush` scopes itself to that prefix\n * so multi-tenant caches don't accidentally wipe sibling tenants. Without\n * a prefix, clears everything.\n */\n public async flush() {\n this.log(\"flushing\");\n\n if (this.options.globalPrefix) {\n await this.removeNamespace(\"\");\n } else {\n this.cache.clear();\n this.tagIndex.clear();\n this.init();\n }\n\n this.log(\"flushed\");\n\n await this.emit(\"flushed\");\n }\n\n /**\n * {@inheritdoc}\n *\n * Brute-force O(N) cosine similarity over every cached node that carries a\n * vector. Suitable for development and small in-memory knowledge bases —\n * not for production beyond ~10k entries.\n *\n * @warning Dev-only — O(N) per query.\n */\n public async similar<T = any>(\n vector: number[],\n options: CacheSimilarOptions,\n ): Promise<CacheSimilarHit<T>[]> {\n const tagFilter = await this.getKeysForTags(options.tags);\n\n const hits: CacheSimilarHit<T>[] = [];\n\n for (const [parsedKey, node] of this.cache) {\n if (!node.vector) continue;\n if (node.isExpired) continue;\n if (tagFilter && !tagFilter.has(parsedKey)) continue;\n\n const score = cosineSimilarity(vector, node.vector);\n\n if (options.threshold !== undefined && score < options.threshold) {\n continue;\n }\n\n // Clone object values to match get() semantics.\n let value: any = node.value;\n if (value !== null && value !== undefined) {\n const t = typeof value;\n if (t !== \"string\" && t !== \"number\" && t !== \"boolean\") {\n value = structuredClone(value);\n }\n }\n\n hits.push({ key: parsedKey, value, score });\n }\n\n hits.sort((a, b) => b.score - a.score);\n\n if (options.topK >= 0 && hits.length > options.topK) {\n hits.length = options.topK;\n }\n\n return hits;\n }\n\n /**\n * Get lru capacity\n */\n public get capacity() {\n return this.options.capacity || 1000;\n }\n\n /**\n * {@inheritdoc}\n */\n public async disconnect() {\n // Clear the cleanup interval to prevent memory leaks\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = undefined;\n }\n\n await super.disconnect();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
|
+
import { InMemoryTagIndex } from "./in-memory-tag-index.mjs";
|
|
2
3
|
import { CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheSimilarHit, CacheSimilarOptions, CacheTtl, MemoryCacheOptions } from "../types.mjs";
|
|
3
|
-
import { GenericObject } from "@mongez/reinforcements";
|
|
4
4
|
|
|
5
5
|
//#region ../cache/src/drivers/memory-cache-driver.d.ts
|
|
6
6
|
declare class MemoryCacheDriver extends BaseCacheDriver<MemoryCacheDriver, MemoryCacheOptions> implements CacheDriver<MemoryCacheDriver, MemoryCacheOptions> {
|
|
@@ -9,52 +9,110 @@ declare class MemoryCacheDriver extends BaseCacheDriver<MemoryCacheDriver, Memor
|
|
|
9
9
|
*/
|
|
10
10
|
name: string;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Flat storage keyed by the parsed key. Insertion order doubles as LRU
|
|
13
|
+
* order (least recently used first) — accessed entries are deleted and
|
|
14
|
+
* re-inserted.
|
|
13
15
|
*/
|
|
14
|
-
|
|
16
|
+
protected entries: Map<string, CacheData>;
|
|
15
17
|
/**
|
|
16
|
-
*
|
|
18
|
+
* Read-only object view of the stored entries, keyed by parsed key.
|
|
19
|
+
* Kept for introspection; the values are the live stored wrappers.
|
|
17
20
|
*/
|
|
18
|
-
|
|
19
|
-
key: string;
|
|
20
|
-
expiresAt: number;
|
|
21
|
-
}>;
|
|
21
|
+
get data(): Record<string, CacheData>;
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Expiry records for entries with a FINITE ttl only (parsed key => expiresAt ms).
|
|
24
|
+
* The sweep iterates just these.
|
|
24
25
|
*/
|
|
25
|
-
protected
|
|
26
|
+
protected expiry: Map<string, number>;
|
|
26
27
|
/**
|
|
27
|
-
*
|
|
28
|
+
* Cleanup interval reference
|
|
28
29
|
*/
|
|
29
|
-
protected
|
|
30
|
+
protected cleanupInterval?: NodeJS.Timeout;
|
|
30
31
|
/**
|
|
31
32
|
* Parallel vector index keyed by parsedKey. Populated by `set({ vector })`,
|
|
32
33
|
* scanned by `similar()`. Lifetime mirrors the main entry — cleared on
|
|
33
|
-
* `remove`, `flush`, expiry, namespace clear,
|
|
34
|
+
* `remove`, `flush`, expiry, namespace clear, overwrite without a vector,
|
|
35
|
+
* and LRU eviction.
|
|
34
36
|
*/
|
|
35
37
|
protected vectorIndex: Map<string, number[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Tag index, kept apart from `entries`: never evicted by `maxSize`, never
|
|
40
|
+
* swept by TTL, and updated synchronously.
|
|
41
|
+
*/
|
|
42
|
+
protected tagIndex: InMemoryTagIndex;
|
|
36
43
|
/**
|
|
37
44
|
* {@inheritdoc}
|
|
38
45
|
*/
|
|
39
46
|
constructor();
|
|
40
47
|
/**
|
|
41
|
-
* Start the cleanup process
|
|
48
|
+
* Start the cleanup process for entries that carry a finite ttl
|
|
42
49
|
*/
|
|
43
50
|
startCleanup(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Delete an entry and every side record (expiry, vector). Synchronous.
|
|
53
|
+
*/
|
|
54
|
+
protected dropEntry(parsedKey: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Raw, synchronous read of a live entry. An expired entry is deleted and
|
|
57
|
+
* reported as missing. Never slides TTLs, never emits, never touches LRU order.
|
|
58
|
+
*/
|
|
59
|
+
protected readLive(parsedKey: string): CacheData | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Mark an entry as most recently used (only tracked when `maxSize` is set).
|
|
62
|
+
*/
|
|
63
|
+
protected touch(parsedKey: string, entry: CacheData): void;
|
|
64
|
+
/**
|
|
65
|
+
* When true, every live read slides the entry's expiration forward by its
|
|
66
|
+
* ttl (the `memoryExtended` driver). Set in a subclass constructor rather
|
|
67
|
+
* than redeclared, so subclasses stay structurally compatible with this
|
|
68
|
+
* class (the `client` type relies on that).
|
|
69
|
+
*/
|
|
70
|
+
protected slidingExpiration: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Invoked on a live read hit, before the value is returned. Runs only from
|
|
73
|
+
* `get()` after the expiry check, so an expired entry is never resurrected,
|
|
74
|
+
* and never from the internal existence check used by `onConflict`.
|
|
75
|
+
*/
|
|
76
|
+
protected onRead(parsedKey: string, entry: CacheData): void;
|
|
44
77
|
/**
|
|
45
78
|
* {@inheritdoc}
|
|
46
79
|
*/
|
|
47
80
|
removeNamespace(namespace: string): Promise<this>;
|
|
48
81
|
/**
|
|
49
82
|
* {@inheritdoc}
|
|
83
|
+
*
|
|
84
|
+
* The conditional check and the write run with no `await` in between, so
|
|
85
|
+
* `onConflict` decisions are atomic within the process.
|
|
50
86
|
*/
|
|
51
87
|
set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;
|
|
88
|
+
/**
|
|
89
|
+
* {@inheritdoc}
|
|
90
|
+
*
|
|
91
|
+
* The check and the delete run synchronously (no `await` between them), so
|
|
92
|
+
* the compare-and-delete is atomic within the process.
|
|
93
|
+
*/
|
|
94
|
+
protected deleteIfEquals(key: CacheKey, expected: unknown): Promise<boolean>;
|
|
95
|
+
/**
|
|
96
|
+
* {@inheritdoc}
|
|
97
|
+
*
|
|
98
|
+
* Read, add and write run synchronously (no `await` in between), so
|
|
99
|
+
* concurrent increments in this process never lose an update. An existing
|
|
100
|
+
* entry keeps its expiry; a missing key starts from 0 with the default TTL.
|
|
101
|
+
*/
|
|
102
|
+
increment(key: CacheKey, value?: number): Promise<number>;
|
|
103
|
+
/**
|
|
104
|
+
* {@inheritdoc}
|
|
105
|
+
*
|
|
106
|
+
* Read and delete run synchronously, so a value is handed out exactly once
|
|
107
|
+
* even when concurrent callers pull the same key.
|
|
108
|
+
*/
|
|
109
|
+
pull(key: CacheKey): Promise<any | null>;
|
|
52
110
|
/**
|
|
53
111
|
* {@inheritdoc}
|
|
54
112
|
*/
|
|
55
113
|
get(key: CacheKey): Promise<any>;
|
|
56
114
|
/**
|
|
57
|
-
* Read the raw {@link CacheData} wrapper, including `staleAt` metadata.
|
|
115
|
+
* Read the raw {@link CacheData} wrapper (cloned), including `staleAt` metadata.
|
|
58
116
|
* Returns `null` for missing or expired entries so the SWR flow can branch
|
|
59
117
|
* cleanly. Does not emit `hit`/`miss` events — that's `get()`'s job.
|
|
60
118
|
*/
|
|
@@ -65,32 +123,31 @@ declare class MemoryCacheDriver extends BaseCacheDriver<MemoryCacheDriver, Memor
|
|
|
65
123
|
remove(key: CacheKey): Promise<void>;
|
|
66
124
|
/**
|
|
67
125
|
* {@inheritdoc}
|
|
126
|
+
*
|
|
127
|
+
* Synchronous in-process set — concurrent tagged writes never drop members.
|
|
68
128
|
*/
|
|
69
|
-
|
|
129
|
+
tagAdd(tagKey: CacheKey, members: string[]): Promise<void>;
|
|
70
130
|
/**
|
|
71
|
-
*
|
|
131
|
+
* {@inheritdoc}
|
|
72
132
|
*/
|
|
73
|
-
|
|
133
|
+
tagMembers(tagKey: CacheKey): Promise<string[]>;
|
|
74
134
|
/**
|
|
75
|
-
*
|
|
135
|
+
* {@inheritdoc}
|
|
76
136
|
*/
|
|
77
|
-
|
|
137
|
+
tagRemove(tagKey: CacheKey, members: string[]): Promise<void>;
|
|
78
138
|
/**
|
|
79
|
-
*
|
|
139
|
+
* {@inheritdoc}
|
|
80
140
|
*/
|
|
81
|
-
|
|
141
|
+
tagDelete(tagKey: CacheKey): Promise<void>;
|
|
82
142
|
/**
|
|
83
|
-
*
|
|
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).
|
|
143
|
+
* {@inheritdoc}
|
|
88
144
|
*/
|
|
89
|
-
|
|
145
|
+
flush(): Promise<void>;
|
|
90
146
|
/**
|
|
91
|
-
*
|
|
147
|
+
* Evict least recently used entries (Map insertion order) until the size is
|
|
148
|
+
* within `maxSize`. Never evicts `protectedKey` (the key just written).
|
|
92
149
|
*/
|
|
93
|
-
protected
|
|
150
|
+
protected enforceMaxSize(protectedKey: string): void;
|
|
94
151
|
/**
|
|
95
152
|
* {@inheritdoc}
|
|
96
153
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/memory-cache-driver.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"memory-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/memory-cache-driver.ts"],"mappings":";;;;;cAiCa,iBAAA,SACH,eAAA,CAAgB,iBAAA,EAAmB,kBAAA,aAChC,WAAA,CAAY,iBAAA,EAAmB,kBAAA;;AAF5C;;EAOS,IAAA;EANiB;;;;;EAAA,UAad,OAAA,EAAS,GAAA,SAAY,SAAA;EAMG;;;;EAAA,IAAvB,IAAA,IAAQ,MAAA,SAAe,SAAA;EA2Bd;;;;EAAA,UAnBV,MAAA,EAAQ,GAAA;EA0KX;;;EAAA,UArKG,eAAA,GAAkB,MAAA,CAAO,OAAA;EAqOC;;;;;;EAAA,UA7N1B,WAAA,EAAa,GAAA;EA6SO;;;;EAAA,UAvSpB,QAAA,EAAU,gBAAA;EAkVa;;;;EAyBU;;;EA7VpC,YAAA;EA2WmC;;;EAAA,UApUhC,SAAA,CAAU,SAAA;EAmYT;;;;EAAA,UAzXD,QAAA,CAAS,SAAA,WAAoB,SAAA;EA5GjB;;;EAAA,UA8HZ,KAAA,CAAM,SAAA,UAAmB,KAAA,EAAO,SAAA;EA/HC;;;;;;EAAA,UA4IjC,iBAAA;EA/HqB;;;;;EAAA,UAsIrB,MAAA,CAAO,SAAA,UAAmB,KAAA,EAAO,SAAA;EAnHjC;;;EAoIG,eAAA,CAAgB,SAAA,WAAiB,OAAA;EA5HvB;;;;;;EA4JV,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EA3FO;;;;;;EAAA,UAwJM,cAAA,CAAe,GAAA,EAAK,QAAA,EAAU,QAAA,YAAoB,OAAA;EAzHxD;;;;;;;EA6IG,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,YAAoB,OAAA;EArF7C;;;;;;EA0HA,IAAA,CAAK,GAAA,EAAK,QAAA,GAAW,OAAA;EAtH/B;;;EA6IU,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;EAhFgB;;;;;EAAA,UA8G9B,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;EA1FS;;;EAuG7C,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;EAlEC;;;;;EAoFrB,MAAA,CAAO,MAAA,EAAQ,QAAA,EAAU,OAAA,aAAoB,OAAA;EA/B5B;;;EAsCjB,UAAA,CAAW,MAAA,EAAQ,QAAA,GAAW,OAAA;EAzB9B;;;EAgCA,SAAA,CAAU,MAAA,EAAQ,QAAA,EAAU,OAAA,aAAoB,OAAA;EAdhD;;;EAqBA,SAAA,CAAU,MAAA,EAAQ,QAAA,GAAW,OAAA;EArBgB;;;EA4B7C,KAAA,IAAK,OAAA;EArByB;;;;EAAA,UA0CjC,cAAA,CAAe,YAAA;EAnCoC;;;;;;;;;;EAmEhD,OAAA,UACX,MAAA,YACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,eAAA,CAAgB,CAAA;EAFzB;;;EA2CW,UAAA,IAAU,OAAA;AAAA"}
|