@warlock.js/cache 4.8.2 → 4.9.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/cjs/index.cjs +20 -20
- package/cjs/index.cjs.map +1 -1
- package/esm/cache-manager.d.mts +1 -1
- package/esm/cache-manager.d.mts.map +1 -1
- package/esm/cache-manager.mjs +1 -1
- package/esm/cache-manager.mjs.map +1 -1
- package/esm/cached/auto-key.d.mts +1 -1
- package/esm/cached/auto-key.d.mts.map +1 -1
- package/esm/cached/auto-key.mjs +1 -1
- package/esm/cached/auto-key.mjs.map +1 -1
- package/esm/cached/cached.d.mts +1 -1
- package/esm/cached/cached.d.mts.map +1 -1
- package/esm/cached/cached.mjs +1 -1
- package/esm/cached/cached.mjs.map +1 -1
- package/esm/cached/normalize-args.d.mts +1 -1
- package/esm/cached/normalize-args.d.mts.map +1 -1
- package/esm/cached/normalize-args.mjs +1 -1
- package/esm/cached/normalize-args.mjs.map +1 -1
- package/esm/drivers/base-cache-driver.d.mts +1 -1
- package/esm/drivers/base-cache-driver.d.mts.map +1 -1
- package/esm/drivers/base-cache-driver.mjs +1 -1
- package/esm/drivers/base-cache-driver.mjs.map +1 -1
- package/esm/drivers/file-cache-driver.d.mts +1 -1
- package/esm/drivers/file-cache-driver.d.mts.map +1 -1
- package/esm/drivers/file-cache-driver.mjs +1 -1
- package/esm/drivers/file-cache-driver.mjs.map +1 -1
- package/esm/drivers/lru-memory-cache-driver.d.mts +1 -1
- package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/lru-memory-cache-driver.mjs +1 -1
- package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/memory-cache-driver.d.mts +1 -1
- package/esm/drivers/memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/memory-cache-driver.mjs +1 -1
- package/esm/drivers/memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/memory-extended-cache-driver.d.mts +1 -1
- package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -1
- package/esm/drivers/memory-extended-cache-driver.mjs +1 -1
- package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -1
- package/esm/drivers/mock-cache-driver.d.mts +1 -1
- package/esm/drivers/mock-cache-driver.d.mts.map +1 -1
- package/esm/drivers/mock-cache-driver.mjs +1 -1
- package/esm/drivers/mock-cache-driver.mjs.map +1 -1
- package/esm/drivers/null-cache-driver.d.mts +1 -1
- package/esm/drivers/null-cache-driver.d.mts.map +1 -1
- package/esm/drivers/null-cache-driver.mjs +1 -1
- package/esm/drivers/null-cache-driver.mjs.map +1 -1
- package/esm/drivers/pg-cache-driver.d.mts +1 -1
- package/esm/drivers/pg-cache-driver.d.mts.map +1 -1
- package/esm/drivers/pg-cache-driver.mjs +1 -1
- package/esm/drivers/pg-cache-driver.mjs.map +1 -1
- package/esm/drivers/redis-cache-driver.d.mts +1 -1
- package/esm/drivers/redis-cache-driver.d.mts.map +1 -1
- package/esm/drivers/redis-cache-driver.mjs +1 -1
- package/esm/drivers/redis-cache-driver.mjs.map +1 -1
- package/esm/list/memory-cache-list.d.mts +1 -1
- package/esm/list/memory-cache-list.d.mts.map +1 -1
- package/esm/list/memory-cache-list.mjs +1 -1
- package/esm/list/memory-cache-list.mjs.map +1 -1
- package/esm/metrics.d.mts +1 -1
- package/esm/metrics.d.mts.map +1 -1
- package/esm/metrics.mjs +1 -1
- package/esm/metrics.mjs.map +1 -1
- package/esm/scoped-cache.d.mts +1 -1
- package/esm/scoped-cache.d.mts.map +1 -1
- package/esm/scoped-cache.mjs +1 -1
- package/esm/scoped-cache.mjs.map +1 -1
- package/esm/tagged-cache.d.mts +1 -1
- package/esm/tagged-cache.d.mts.map +1 -1
- package/esm/tagged-cache.mjs +1 -1
- package/esm/tagged-cache.mjs.map +1 -1
- package/esm/tagged-scoped-cache.d.mts +1 -1
- package/esm/tagged-scoped-cache.d.mts.map +1 -1
- package/esm/tagged-scoped-cache.mjs +1 -1
- package/esm/tagged-scoped-cache.mjs.map +1 -1
- package/esm/types.d.mts +1 -1
- package/esm/types.d.mts.map +1 -1
- package/esm/types.mjs +1 -1
- package/esm/types.mjs.map +1 -1
- package/esm/utils.d.mts +1 -1
- package/esm/utils.d.mts.map +1 -1
- package/esm/utils.mjs +1 -1
- package/esm/utils.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-cache-driver.mjs","names":[],"sources":["../../../../../../../@warlock.js/cache/src/drivers/file-cache-driver.ts"],"sourcesContent":["import {\r\n ensureDirectoryAsync,\r\n getJsonFileAsync,\r\n putJsonFileAsync,\r\n removeDirectoryAsync,\r\n} from \"@warlock.js/fs\";\r\nimport path from \"path\";\r\nimport type {\r\n CacheData,\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheSetResult,\r\n CacheTtl,\r\n FileCacheOptions,\r\n} from \"../types\";\r\nimport { CacheConfigurationError, CacheUnsupportedError } from \"../types\";\r\nimport { BaseCacheDriver } from \"./base-cache-driver\";\r\n\r\nexport class FileCacheDriver\r\n extends BaseCacheDriver<FileCacheDriver, FileCacheOptions>\r\n implements CacheDriver<FileCacheDriver, FileCacheOptions>\r\n{\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"file\";\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public setOptions(options: FileCacheOptions) {\r\n if (!options.directory) {\r\n throw new CacheConfigurationError(\r\n \"File driver requires 'directory' option to be configured.\",\r\n );\r\n }\r\n\r\n return super.setOptions(options);\r\n }\r\n\r\n /**\r\n * Get the cache directory\r\n */\r\n public get directory() {\r\n const directory = this.options.directory;\r\n\r\n if (typeof directory === \"function\") {\r\n return directory();\r\n }\r\n\r\n throw new CacheConfigurationError(\r\n \"Cache directory is not defined, please define it in the file driver options\",\r\n );\r\n }\r\n\r\n /**\r\n * Get file name\r\n */\r\n public get fileName() {\r\n const fileName = this.options.fileName;\r\n\r\n if (typeof fileName === \"function\") {\r\n return fileName();\r\n }\r\n\r\n return \"cache.json\";\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 try {\r\n await removeDirectoryAsync(path.resolve(this.directory, namespace));\r\n\r\n this.log(\"cleared\", namespace);\r\n } catch (error) {\r\n //\r\n }\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 if (vector) {\r\n throw new CacheUnsupportedError(\r\n \"'file' driver does not support similarity retrieval — use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).\",\r\n );\r\n }\r\n\r\n this.log(\"caching\", parsedKey);\r\n\r\n const existing = onConflict === \"upsert\" ? null : await this.get(key);\r\n const exists = existing !== null;\r\n\r\n if (onConflict === \"create\" && exists) {\r\n const result: CacheSetResult = { wasSet: false, existing };\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 const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n await ensureDirectoryAsync(fileDirectory);\r\n\r\n await putJsonFileAsync(path.resolve(fileDirectory, this.fileName), data);\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 * {@inheritdoc}\r\n *\r\n * File driver does not yet ship with a file-lock primitive, so concurrent\r\n * writers could clobber each other. Rather than ship an unsafe default, we\r\n * throw — consumers can fall back to memory/redis for `update` until a\r\n * proper file lock lands (tracked in `domains/cache/backlog.md`).\r\n */\r\n public async update(): Promise<never> {\r\n throw new CacheUnsupportedError(\r\n \"`update()` is not supported on the file driver. Use the memory or redis driver, or wait for the file-lock primitive (see domains/cache/backlog.md).\",\r\n );\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async merge(): Promise<never> {\r\n throw new CacheUnsupportedError(\r\n \"`merge()` is not supported on the file driver. Use the memory or redis driver.\",\r\n );\r\n }\r\n\r\n /**\r\n * Read the raw {@link CacheData} wrapper from disk, including `staleAt`\r\n * metadata. Returns `null` for missing or expired files — `swr()`\r\n * consumes this to branch on freshness.\r\n */\r\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\r\n const parsedKey = this.parseKey(key);\r\n const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n try {\r\n const entry = (await getJsonFileAsync(path.resolve(fileDirectory, this.fileName))) as\r\n | CacheData\r\n | undefined;\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 } catch {\r\n return null;\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 fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n try {\r\n const value = await getJsonFileAsync(path.resolve(fileDirectory, this.fileName));\r\n\r\n const result = await this.parseCachedData(parsedKey, value as CacheData);\r\n\r\n if (result === null) {\r\n // Expired\r\n await this.emit(\"miss\", { key: parsedKey });\r\n } else {\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 } catch (error) {\r\n this.log(\"notFound\", parsedKey);\r\n // Emit miss event\r\n await this.emit(\"miss\", { key: parsedKey });\r\n // Await the cleanup so it fully settles before returning. Leaving this\r\n // fire-and-forget let the async directory removal race a follow-up write\r\n // to the same key (e.g. the existence probe inside a `set({ onConflict })`\r\n // call), surfacing as an ENOENT mkdir/rm collision on Windows.\r\n await this.remove(key);\r\n return null;\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 this.log(\"removing\", parsedKey);\r\n\r\n const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n try {\r\n await removeDirectoryAsync(fileDirectory);\r\n\r\n this.log(\"removed\", parsedKey);\r\n // Emit removed event\r\n await this.emit(\"removed\", { key: parsedKey });\r\n } catch (error) {\r\n //\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\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 await removeDirectoryAsync(this.directory);\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 * {@inheritdoc}\r\n */\r\n public async connect() {\r\n this.log(\"connecting\");\r\n await ensureDirectoryAsync(this.directory);\r\n this.log(\"connected\");\r\n await this.emit(\"connected\");\r\n }\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"file-cache-driver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/file-cache-driver.ts"],"sourcesContent":["import {\r\n ensureDirectoryAsync,\r\n getJsonFileAsync,\r\n putJsonFileAsync,\r\n removeDirectoryAsync,\r\n} from \"@warlock.js/fs\";\r\nimport path from \"path\";\r\nimport type {\r\n CacheData,\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheSetResult,\r\n CacheTtl,\r\n FileCacheOptions,\r\n} from \"../types\";\r\nimport { CacheConfigurationError, CacheUnsupportedError } from \"../types\";\r\nimport { BaseCacheDriver } from \"./base-cache-driver\";\r\n\r\nexport class FileCacheDriver\r\n extends BaseCacheDriver<FileCacheDriver, FileCacheOptions>\r\n implements CacheDriver<FileCacheDriver, FileCacheOptions>\r\n{\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"file\";\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public setOptions(options: FileCacheOptions) {\r\n if (!options.directory) {\r\n throw new CacheConfigurationError(\r\n \"File driver requires 'directory' option to be configured.\",\r\n );\r\n }\r\n\r\n return super.setOptions(options);\r\n }\r\n\r\n /**\r\n * Get the cache directory\r\n */\r\n public get directory() {\r\n const directory = this.options.directory;\r\n\r\n if (typeof directory === \"function\") {\r\n return directory();\r\n }\r\n\r\n throw new CacheConfigurationError(\r\n \"Cache directory is not defined, please define it in the file driver options\",\r\n );\r\n }\r\n\r\n /**\r\n * Get file name\r\n */\r\n public get fileName() {\r\n const fileName = this.options.fileName;\r\n\r\n if (typeof fileName === \"function\") {\r\n return fileName();\r\n }\r\n\r\n return \"cache.json\";\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 try {\r\n await removeDirectoryAsync(path.resolve(this.directory, namespace));\r\n\r\n this.log(\"cleared\", namespace);\r\n } catch (error) {\r\n //\r\n }\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 if (vector) {\r\n throw new CacheUnsupportedError(\r\n \"'file' driver does not support similarity retrieval — use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).\",\r\n );\r\n }\r\n\r\n this.log(\"caching\", parsedKey);\r\n\r\n const existing = onConflict === \"upsert\" ? null : await this.get(key);\r\n const exists = existing !== null;\r\n\r\n if (onConflict === \"create\" && exists) {\r\n const result: CacheSetResult = { wasSet: false, existing };\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 const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n await ensureDirectoryAsync(fileDirectory);\r\n\r\n await putJsonFileAsync(path.resolve(fileDirectory, this.fileName), data);\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 * {@inheritdoc}\r\n *\r\n * File driver does not yet ship with a file-lock primitive, so concurrent\r\n * writers could clobber each other. Rather than ship an unsafe default, we\r\n * throw — consumers can fall back to memory/redis for `update` until a\r\n * proper file lock lands (tracked in `domains/cache/backlog.md`).\r\n */\r\n public async update(): Promise<never> {\r\n throw new CacheUnsupportedError(\r\n \"`update()` is not supported on the file driver. Use the memory or redis driver, or wait for the file-lock primitive (see domains/cache/backlog.md).\",\r\n );\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async merge(): Promise<never> {\r\n throw new CacheUnsupportedError(\r\n \"`merge()` is not supported on the file driver. Use the memory or redis driver.\",\r\n );\r\n }\r\n\r\n /**\r\n * Read the raw {@link CacheData} wrapper from disk, including `staleAt`\r\n * metadata. Returns `null` for missing or expired files — `swr()`\r\n * consumes this to branch on freshness.\r\n */\r\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\r\n const parsedKey = this.parseKey(key);\r\n const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n try {\r\n const entry = (await getJsonFileAsync(path.resolve(fileDirectory, this.fileName))) as\r\n | CacheData\r\n | undefined;\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 } catch {\r\n return null;\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 fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n try {\r\n const value = await getJsonFileAsync(path.resolve(fileDirectory, this.fileName));\r\n\r\n const result = await this.parseCachedData(parsedKey, value as CacheData);\r\n\r\n if (result === null) {\r\n // Expired\r\n await this.emit(\"miss\", { key: parsedKey });\r\n } else {\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 } catch (error) {\r\n this.log(\"notFound\", parsedKey);\r\n // Emit miss event\r\n await this.emit(\"miss\", { key: parsedKey });\r\n // Await the cleanup so it fully settles before returning. Leaving this\r\n // fire-and-forget let the async directory removal race a follow-up write\r\n // to the same key (e.g. the existence probe inside a `set({ onConflict })`\r\n // call), surfacing as an ENOENT mkdir/rm collision on Windows.\r\n await this.remove(key);\r\n return null;\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 this.log(\"removing\", parsedKey);\r\n\r\n const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n try {\r\n await removeDirectoryAsync(fileDirectory);\r\n\r\n this.log(\"removed\", parsedKey);\r\n // Emit removed event\r\n await this.emit(\"removed\", { key: parsedKey });\r\n } catch (error) {\r\n //\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\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 await removeDirectoryAsync(this.directory);\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 * {@inheritdoc}\r\n */\r\n public async connect() {\r\n this.log(\"connecting\");\r\n await ensureDirectoryAsync(this.directory);\r\n this.log(\"connected\");\r\n await this.emit(\"connected\");\r\n }\r\n}\r\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
2
|
import { CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheSimilarHit, CacheSimilarOptions, CacheTtl, LRUMemoryCacheOptions } from "../types.mjs";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
4
|
+
//#region ../cache/src/drivers/lru-memory-cache-driver.d.ts
|
|
5
5
|
declare class CacheNode {
|
|
6
6
|
key: string;
|
|
7
7
|
value: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lru-memory-cache-driver.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"lru-memory-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/lru-memory-cache-driver.ts"],"mappings":";;;;cAcM,SAAA;EAeK,GAAA;EACA,KAAA;EAfF,IAAA,EAAM,SAAA;EACN,IAAA,EAAM,SAAS;EACf,SAAA;EAYE;;;;EAPF,OAAA;EANM;;;;EAWN,MAAA;cAEE,GAAA,UACA,KAAA,OACP,GAAA;EAAA,IAOS,SAAA;AAAA;;;AAAS;AAWtB;;;cAAa,oBAAA,SACH,eAAA,CAAgB,oBAAA,EAAsB,qBAAA,aACnC,WAAA,CAAY,oBAAA,EAAsB,qBAAA;EADC;;;EAMvC,IAAA;EAKU;;;EAAA,UAAP,KAAA,EAAO,GAAA,SAAY,SAAA;EA8EiB;;;EAAA,UAzEpC,IAAA,EAAM,SAAA;EAmHb;;;EAAA,UA9GO,IAAA,EAAM,SAAA;EAoOc;;;EAAA,UA/NpB,eAAA,GAAkB,MAAA,CAAO,OAAA;EAiPL;;;;EA6GnB;;;EA/UJ,IAAA;EA+XgB;;;EAvXhB,YAAA;EAhDe;;;;;;;;EAwFT,eAAA,CAAgB,SAAA,WAAiB,OAAA;EA9E7B;;;EAoHJ,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EA9GO;;;EAAA,UA4LA,QAAA,CAAS,IAAA,EAAM,SAAA;EAvLU;;;EAAA,UA+LzB,UAAA,CAAW,IAAA,EAAM,SAAA;EAhId;;;EAAA,UAwIH,OAAA,CAAQ,IAAA,EAAM,SAAA;EAjGjB;;;EAAA,UA2GG,UAAA;EAzGkB;;;;;;EAAA,UAuHZ,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;EAhCtB;;;EAkDd,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;EA1CZ;;;EAoGL,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;EA5ER;;;;;;;EAqGZ,KAAA,IAAK,OAAA;EAzBO;;;;;;;;;EAiDZ,OAAA,UACX,MAAA,YACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,eAAA,CAAgB,CAAA;EAAxB;;;EAAA,IAwCQ,QAAA;EAOE;;;EAAA,UAAA,IAAU,OAAA;AAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cosineSimilarity } from "../utils.mjs";
|
|
2
2
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
4
|
+
//#region ../cache/src/drivers/lru-memory-cache-driver.ts
|
|
5
5
|
var CacheNode = class {
|
|
6
6
|
constructor(key, value, ttl) {
|
|
7
7
|
this.key = key;
|
|
@@ -1 +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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"}
|
|
1
|
+
{"version":3,"file":"lru-memory-cache-driver.mjs","names":[],"sources":["../../../../../../../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":";;;;AAcA,IAAM,YAAN,MAAgB;CAcd,AAAO,YACL,AAAO,KACP,AAAO,OACP,KACA;EAHO;EACA;cAfuB;cACA;EAiB9B,IAAI,OAAO,QAAQ,UACjB,KAAK,YAAY,KAAK,IAAI,IAAI,MAAM;CAExC;CAEA,IAAW,YAAqB;EAC9B,OAAO,KAAK,cAAc,UAAa,KAAK,YAAY,KAAK,IAAI;CACnE;AACF;;;;;;;AAQA,IAAa,uBAAb,cACU,gBAEV;;;;CA6BE,AAAO,cAAc;EACnB,MAAM;cA1BM;+BAK4B,IAAI,IAAI;cAKtB,IAAI,UAAU,IAAI,IAAI;cAKtB,IAAI,UAAU,IAAI,IAAI;EAahD,KAAK,KAAK;EACV,KAAK,aAAa;CACpB;;;;CAKA,AAAO,OAAO;EACZ,KAAK,KAAK,OAAO,KAAK;EACtB,KAAK,KAAK,OAAO,KAAK;CACxB;;;;CAKA,AAAO,eAAe;EAEpB,IAAI,KAAK,iBACP,cAAc,KAAK,eAAe;EAGpC,KAAK,kBAAkB,YAAY,YAAY;GAC7C,MAAM,MAAM,KAAK,IAAI;GACrB,MAAM,cAAwB,CAAC;GAE/B,KAAK,MAAM,CAAC,KAAK,SAAS,KAAK,OAC7B,IAAI,KAAK,aAAa,KAAK,aAAa,KACtC,YAAY,KAAK,GAAG;GAIxB,KAAK,MAAM,OAAO,aAAa;IAC7B,MAAM,OAAO,KAAK,MAAM,IAAI,GAAG;IAC/B,IAAI,MAAM;KACR,KAAK,WAAW,IAAI;KACpB,KAAK,MAAM,OAAO,GAAG;KACrB,KAAK,IAAI,WAAW,GAAG;KAEvB,MAAM,KAAK,KAAK,WAAW,EAAE,IAAI,CAAC;IACpC;GACF;EACF,GAAG,GAAI;EAGP,KAAK,gBAAgB,MAAM;CAC7B;;;;;;;;;CAUA,MAAa,gBAAgB,WAAmB;EAC9C,MAAM,kBAAkB,KAAK,SAAS,SAAS;EAE/C,KAAK,IAAI,YAAY,mBAAmB,OAAO;EAE/C,MAAM,UAAoB,CAAC;EAE3B,IAAI,oBAAoB,IACtB,KAAK,MAAM,OAAO,KAAK,MAAM,KAAK,GAChC,QAAQ,KAAK,GAAG;OAEb;GACL,MAAM,SAAS,kBAAkB;GAEjC,KAAK,MAAM,OAAO,KAAK,MAAM,KAAK,GAChC,IAAI,QAAQ,mBAAmB,IAAI,WAAW,MAAM,GAClD,QAAQ,KAAK,GAAG;EAGtB;EAEA,KAAK,MAAM,OAAO,SAAS;GACzB,MAAM,OAAO,KAAK,MAAM,IAAI,GAAG;GAC/B,IAAI,MAAM;IACR,KAAK,WAAW,IAAI;IACpB,KAAK,MAAM,OAAO,GAAG;GACvB;GACA,MAAM,KAAK,KAAK,WAAW,EAAE,IAAI,CAAC;EACpC;EAEA,KAAK,IAAI,WAAW,mBAAmB,OAAO;EAE9C,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,IAAI,eAAe,KAAK,MAAM,IAAI,SAAS;EAC3C,IAAI,gBAAgB,aAAa,WAAW;GAC1C,KAAK,WAAW,YAAY;GAC5B,KAAK,MAAM,OAAO,SAAS;GAC3B,eAAe;EACjB;EAEA,MAAM,SAAS,QAAQ,YAAY;EAEnC,IAAI,eAAe,YAAY,QAK7B,OAAO;GAHL,QAAQ;GACR,UAAU,aAAc;EAEd;EAGd,IAAI,eAAe,YAAY,CAAC,QAE9B,OAAO;GAD0B,QAAQ;GAAO,UAAU;EAC9C;EAGd,IAAI,cAAc;GAChB,aAAa,QAAQ;GACrB,IAAI,OAAO,QAAQ,UACjB,aAAa,YAAY,KAAK,IAAI,IAAI,MAAM;QAE5C,aAAa,YAAY;GAE3B,aAAa,UAAU;GACvB,IAAI,QACF,aAAa,SAAS,OAAO,MAAM;GAGrC,KAAK,SAAS,YAAY;EAC5B,OAAO;GACL,MAAM,UAAU,IAAI,UAAU,WAAW,OAAO,GAAG;GACnD,QAAQ,UAAU;GAClB,IAAI,QACF,QAAQ,SAAS,OAAO,MAAM;GAGhC,KAAK,MAAM,IAAI,WAAW,OAAO;GAEjC,KAAK,QAAQ,OAAO;GACpB,IAAI,KAAK,MAAM,OAAO,KAAK,UACzB,KAAK,WAAW;EAEpB;EAEA,IAAI,QAAQ,KAAK,SAAS,GACxB,MAAM,KAAK,UAAU,WAAW,IAAI;EAGtC,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,AAAU,SAAS,MAAiB;EAClC,KAAK,WAAW,IAAI;EACpB,KAAK,QAAQ,IAAI;CACnB;;;;CAKA,AAAU,WAAW,MAAiB;EACpC,KAAK,KAAM,OAAO,KAAK;EACvB,KAAK,KAAM,OAAO,KAAK;CACzB;;;;CAKA,AAAU,QAAQ,MAAiB;EACjC,KAAK,OAAO,KAAK,KAAK;EACtB,KAAK,OAAO,KAAK;EACjB,KAAK,KAAK,KAAM,OAAO;EACvB,KAAK,KAAK,OAAO;CACnB;;;;CAKA,AAAU,aAAa;EACrB,MAAM,OAAO,KAAK,KAAK;EAEvB,KAAK,WAAW,IAAI;EAEpB,KAAK,MAAM,OAAO,KAAK,GAAG;CAC5B;;;;;;;CAQA,MAAgB,SAAS,KAA0C;EACjE,MAAM,YAAY,KAAK,SAAS,GAAG;EACnC,MAAM,OAAO,KAAK,MAAM,IAAI,SAAS;EAErC,IAAI,CAAC,QAAQ,KAAK,WAChB,OAAO;EAGT,OAAO;GACL,MAAM,KAAK;GACX,WAAW,KAAK;GAChB,SAAS,KAAK;EAChB;CACF;;;;CAKA,MAAa,IAAI,KAAe;EAC9B,MAAM,YAAY,KAAK,SAAS,GAAG;EAEnC,KAAK,IAAI,YAAY,SAAS;EAE9B,MAAM,OAAO,KAAK,MAAM,IAAI,SAAS;EAErC,IAAI,CAAC,MAAM;GACT,KAAK,IAAI,YAAY,SAAS;GAE9B,MAAM,KAAK,KAAK,QAAQ,EAAE,KAAK,UAAU,CAAC;GAC1C,OAAO;EACT;EAGA,IAAI,KAAK,WAAW;GAClB,KAAK,WAAW,IAAI;GACpB,KAAK,MAAM,OAAO,SAAS;GAC3B,KAAK,IAAI,WAAW,SAAS;GAE7B,MAAM,KAAK,KAAK,WAAW,EAAE,KAAK,UAAU,CAAC;GAE7C,MAAM,KAAK,KAAK,QAAQ,EAAE,KAAK,UAAU,CAAC;GAC1C,OAAO;EACT;EAEA,KAAK,SAAS,IAAI;EAElB,KAAK,IAAI,WAAW,SAAS;EAE7B,MAAM,QAAQ,KAAK;EAGnB,IAAI,UAAU,QAAQ,UAAU,QAC9B,OAAO;EAGT,MAAM,OAAO,OAAO;EACpB,IAAI,SAAS,YAAY,SAAS,YAAY,SAAS,WAAW;GAEhE,MAAM,KAAK,KAAK,OAAO;IAAE,KAAK;IAAW;GAAM,CAAC;GAChD,OAAO;EACT;EAEA,IAAI;GACF,MAAM,cAAc,gBAAgB,KAAK;GAEzC,MAAM,KAAK,KAAK,OAAO;IAAE,KAAK;IAAW,OAAO;GAAY,CAAC;GAC7D,OAAO;EACT,SAAS,OAAO;GACd,KAAK,SAAS,oCAAoC,aAAa,KAAK;GACpE,MAAM;EACR;CACF;;;;CAKA,MAAa,OAAO,KAAe;EACjC,MAAM,YAAY,KAAK,SAAS,GAAG;EAEnC,KAAK,IAAI,YAAY,SAAS;EAE9B,MAAM,OAAO,KAAK,MAAM,IAAI,SAAS;EAErC,IAAI,MAAM;GACR,KAAK,WAAW,IAAI;GACpB,KAAK,MAAM,OAAO,SAAS;EAC7B;EAEA,KAAK,IAAI,WAAW,SAAS;EAG7B,MAAM,KAAK,KAAK,WAAW,EAAE,KAAK,UAAU,CAAC;CAC/C;;;;;;;;CASA,MAAa,QAAQ;EACnB,KAAK,IAAI,UAAU;EAEnB,IAAI,KAAK,QAAQ,cACf,MAAM,KAAK,gBAAgB,EAAE;OACxB;GACL,KAAK,MAAM,MAAM;GACjB,KAAK,KAAK;EACZ;EAEA,KAAK,IAAI,SAAS;EAElB,MAAM,KAAK,KAAK,SAAS;CAC3B;;;;;;;;;;CAWA,MAAa,QACX,QACA,SAC+B;EAC/B,MAAM,YAAY,MAAM,KAAK,eAAe,QAAQ,IAAI;EAExD,MAAM,OAA6B,CAAC;EAEpC,KAAK,MAAM,CAAC,WAAW,SAAS,KAAK,OAAO;GAC1C,IAAI,CAAC,KAAK,QAAQ;GAClB,IAAI,KAAK,WAAW;GACpB,IAAI,aAAa,CAAC,UAAU,IAAI,SAAS,GAAG;GAE5C,MAAM,QAAQ,iBAAiB,QAAQ,KAAK,MAAM;GAElD,IAAI,QAAQ,cAAc,UAAa,QAAQ,QAAQ,WACrD;GAIF,IAAI,QAAa,KAAK;GACtB,IAAI,UAAU,QAAQ,UAAU,QAAW;IACzC,MAAM,IAAI,OAAO;IACjB,IAAI,MAAM,YAAY,MAAM,YAAY,MAAM,WAC5C,QAAQ,gBAAgB,KAAK;GAEjC;GAEA,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,IAAW,WAAW;EACpB,OAAO,KAAK,QAAQ,YAAY;CAClC;;;;CAKA,MAAa,aAAa;EAExB,IAAI,KAAK,iBAAiB;GACxB,cAAc,KAAK,eAAe;GAClC,KAAK,kBAAkB;EACzB;EAEA,MAAM,MAAM,WAAW;CACzB;AACF"}
|
|
@@ -2,7 +2,7 @@ import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
|
2
2
|
import { CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheSimilarHit, CacheSimilarOptions, CacheTtl, MemoryCacheOptions } from "../types.mjs";
|
|
3
3
|
import { GenericObject } from "@mongez/reinforcements";
|
|
4
4
|
|
|
5
|
-
//#region
|
|
5
|
+
//#region ../cache/src/drivers/memory-cache-driver.d.ts
|
|
6
6
|
declare class MemoryCacheDriver extends BaseCacheDriver<MemoryCacheDriver, MemoryCacheOptions> implements CacheDriver<MemoryCacheDriver, MemoryCacheOptions> {
|
|
7
7
|
/**
|
|
8
8
|
* {@inheritdoc}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-cache-driver.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"memory-cache-driver.d.mts","names":[],"sources":["../../../../../../../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;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,IAAK,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,IAAc,OAAA;EAxCpB;;;EAAA,UAiEA,YAAA;EAjEmD;;;;;;;;;;EAgFhD,OAAA,UACX,MAAA,YACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,eAAA,CAAgB,CAAA;EADhB;;;EAuCE,UAAA,IAAU,OAAA;AAAA"}
|
|
@@ -2,7 +2,7 @@ import { cosineSimilarity } from "../utils.mjs";
|
|
|
2
2
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
3
3
|
import { get, set, unset } from "@mongez/reinforcements";
|
|
4
4
|
|
|
5
|
-
//#region
|
|
5
|
+
//#region ../cache/src/drivers/memory-cache-driver.ts
|
|
6
6
|
var MemoryCacheDriver = class extends BaseCacheDriver {
|
|
7
7
|
/**
|
|
8
8
|
* {@inheritdoc}
|
|
@@ -1 +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":"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"}
|
|
1
|
+
{"version":3,"file":"memory-cache-driver.mjs","names":[],"sources":["../../../../../../../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":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MemoryCacheDriver } from "./memory-cache-driver.mjs";
|
|
2
2
|
import { CacheDriver, CacheKey, MemoryExtendedCacheOptions } from "../types.mjs";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
4
|
+
//#region ../cache/src/drivers/memory-extended-cache-driver.d.ts
|
|
5
5
|
declare class MemoryExtendedCacheDriver extends MemoryCacheDriver implements CacheDriver<MemoryExtendedCacheDriver, MemoryExtendedCacheOptions> {
|
|
6
6
|
/**
|
|
7
7
|
* {@inheritdoc}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-extended-cache-driver.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"memory-extended-cache-driver.d.mts","names":[],"sources":["../../../../../../../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"}
|
|
@@ -2,7 +2,7 @@ import { parseTtl } from "../utils.mjs";
|
|
|
2
2
|
import { MemoryCacheDriver } from "./memory-cache-driver.mjs";
|
|
3
3
|
import { get } from "@mongez/reinforcements";
|
|
4
4
|
|
|
5
|
-
//#region
|
|
5
|
+
//#region ../cache/src/drivers/memory-extended-cache-driver.ts
|
|
6
6
|
var MemoryExtendedCacheDriver = class extends MemoryCacheDriver {
|
|
7
7
|
constructor(..._args) {
|
|
8
8
|
super(..._args);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-extended-cache-driver.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"memory-extended-cache-driver.mjs","names":[],"sources":["../../../../../../../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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
2
|
import { CacheCall, CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheTtl, MockCacheOptions } from "../types.mjs";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
4
|
+
//#region ../cache/src/drivers/mock-cache-driver.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* In-memory cache driver with introspection helpers, intended for use as a
|
|
7
7
|
* test double in downstream packages.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-cache-driver.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"mock-cache-driver.d.mts","names":[],"sources":["../../../../../../../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,IAAW,OAAA;EAuFP;;;EA/EJ,UAAA,IAAc,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,UAAa,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,IAAS,OAAA;;;;;;;;;;;EAoBf,SAAA,CAAU,SAAA,UAAmB,GAAA,GAAM,QAAA;;;;;;;;EAmBnC,SAAA,UAAmB,GAAA,EAAK,QAAA,GAAW,CAAA;;;;;EAenC,KAAA;;;;;YASG,UAAA,CACR,SAAA,UACA,GAAA,sBACA,IAAA;AAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../cache/src/drivers/mock-cache-driver.ts
|
|
4
4
|
/**
|
|
5
5
|
* In-memory cache driver with introspection helpers, intended for use as a
|
|
6
6
|
* test double in downstream packages.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-cache-driver.mjs","names":[],"sources":["../../../../../../../@warlock.js/cache/src/drivers/mock-cache-driver.ts"],"sourcesContent":["import type {\n CacheCall,\n CacheData,\n CacheDriver,\n CacheKey,\n CacheSetOptions,\n CacheSetResult,\n CacheTtl,\n MockCacheOptions,\n} from \"../types\";\nimport { BaseCacheDriver } from \"./base-cache-driver\";\n\n/**\n * In-memory cache driver with introspection helpers, intended for use as a\n * test double in downstream packages.\n *\n * **Role.** Drop-in replacement for any real driver in test setups, with\n * extra surface that makes behavioral assertions easy: every public op gets\n * recorded into {@link MockCacheDriver.callLog}, and {@link wasCalled} /\n * {@link getStored} / {@link reset} let tests verify side effects without\n * pulling in a real Redis / Postgres / file system.\n *\n * **Responsibility.**\n * - Owns: in-memory storage backed by a `Map`, the `callLog`, TTL handling\n * via `parseCachedData`, `onConflict` policies, and tag-index storage.\n * - Does NOT own: similarity retrieval (vectors are recorded into the\n * `callLog` but `similar()` throws — use `MemoryCacheDriver` for tests\n * that need real nearest-neighbor scoring), connection lifecycle\n * (no-op `connect`/`disconnect`), or eviction (no `maxSize`).\n *\n * Register it like any other driver — sub-paths are not part of the\n * package's export convention; the same single barrel ships it next to\n * the production drivers.\n *\n * @example\n * import { cache, MockCacheDriver } from \"@warlock.js/cache\";\n *\n * beforeEach(async () => {\n * cache.setCacheConfigurations({\n * default: \"mock\",\n * drivers: { mock: MockCacheDriver },\n * options: { mock: {} },\n * });\n * await cache.init();\n * });\n *\n * it(\"invalidates the user cache after update\", async () => {\n * await userService.update(42, { name: \"Jane\" });\n *\n * const driver = cache.currentDriver as MockCacheDriver;\n * expect(driver.wasCalled(\"remove\", \"users.42\")).toBe(true);\n * });\n */\nexport class MockCacheDriver\n extends BaseCacheDriver<MockCacheDriver, MockCacheOptions>\n implements CacheDriver<MockCacheDriver, MockCacheOptions>\n{\n /**\n * {@inheritdoc}\n */\n public name = \"mock\";\n\n /**\n * {@inheritdoc}\n */\n public options: MockCacheOptions = {};\n\n /**\n * Storage backing the mock — keyed by post-`parseKey` string. Public-readonly\n * so tests can introspect raw entries when {@link getStored} isn't enough.\n */\n public readonly storage: Map<string, CacheData> = new Map();\n\n /**\n * Ordered record of every public operation routed through this driver.\n * Pushed to before each op runs; tests assert via {@link wasCalled} or by\n * inspecting the array directly.\n */\n public readonly callLog: CacheCall[] = [];\n\n /**\n * Standard driver setup. Mirrors the null driver — no connection, no\n * resources to release.\n */\n public async connect(): Promise<void> {\n this.recordCall(\"connect\", undefined);\n await super.connect();\n }\n\n /**\n * Standard driver teardown.\n */\n public async disconnect(): Promise<void> {\n this.recordCall(\"disconnect\", undefined);\n await super.disconnect();\n }\n\n /**\n * Wipe everything under `namespace`. Matches `MemoryCacheDriver` semantics\n * — the namespace itself and any key with the namespace prefix is removed.\n */\n public async removeNamespace(namespace: string): Promise<void> {\n const parsed = this.parseKey(namespace);\n\n this.recordCall(\"removeNamespace\", parsed, [namespace]);\n this.log(\"clearing\", parsed);\n\n const prefix = parsed + \".\";\n\n for (const key of [...this.storage.keys()]) {\n if (key === parsed || key.startsWith(prefix)) {\n this.storage.delete(key);\n }\n }\n\n this.log(\"cleared\", parsed);\n }\n\n /**\n * Standard `set` with full `onConflict` support. Honors scope-default TTL\n * via {@link BaseCacheDriver.resolveSetOptions}.\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, staleAt } = this.resolveSetOptions(ttlOrOptions);\n\n this.recordCall(\"set\", parsedKey, [value, ttlOrOptions]);\n this.log(\"caching\", parsedKey);\n\n const existing = onConflict === \"upsert\" ? null : await this.get(key);\n const exists = existing !== null;\n\n if (onConflict === \"create\" && exists) {\n const result: CacheSetResult = { wasSet: false, existing };\n\n return result;\n }\n\n if (onConflict === \"update\" && !exists) {\n const result: CacheSetResult = { wasSet: false, existing: null };\n\n return result;\n }\n\n const data = this.prepareDataForStorage(value, ttl, staleAt);\n this.storage.set(parsedKey, data);\n\n if (tags && tags.length > 0) {\n await this.applyTags(parsedKey, tags);\n }\n\n this.log(\"cached\", parsedKey);\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\n return result;\n }\n\n return value;\n }\n\n /**\n * Standard `get` with TTL handling. Emits `hit` / `miss` events to keep\n * downstream metrics tests realistic.\n */\n public async get<T = any>(key: CacheKey): Promise<T | null> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"get\", parsedKey);\n this.log(\"fetching\", parsedKey);\n\n const data = this.storage.get(parsedKey);\n\n if (!data) {\n this.log(\"notFound\", parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n const value = await this.parseCachedData(parsedKey, data);\n\n if (value === null) {\n // expired — the parse helper already logged + queued the cleanup\n this.storage.delete(parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n await this.emit(\"hit\", { key: parsedKey, value });\n\n return value as T;\n }\n\n /**\n * Read the raw {@link CacheData} wrapper from the in-memory `Map`,\n * including `staleAt` metadata. Returns `null` for missing or expired\n * entries — `swr()` consumes this to branch on freshness.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const parsedKey = this.parseKey(key);\n const entry = this.storage.get(parsedKey);\n\n if (!entry) {\n return null;\n }\n\n if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {\n return null;\n }\n\n return entry;\n }\n\n /**\n * Standard `remove` — drops the entry and emits the `removed` event.\n */\n public async remove(key: CacheKey): Promise<void> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"remove\", parsedKey);\n this.log(\"removing\", parsedKey);\n\n this.storage.delete(parsedKey);\n\n this.log(\"removed\", parsedKey);\n await this.emit(\"removed\", { key: parsedKey });\n }\n\n /**\n * Standard `flush` — wipes the entire mock store + tag index. Does NOT\n * touch the call log; use {@link reset} to clear that as well.\n */\n public async flush(): Promise<void> {\n this.recordCall(\"flush\", undefined);\n this.log(\"flushing\");\n\n this.storage.clear();\n\n this.log(\"flushed\");\n await this.emit(\"flushed\");\n }\n\n /**\n * Was a given operation invoked? When `key` is provided, the match is\n * post-`parseKey` so callers pass the same key shape they used at the\n * call site — strings or objects, both resolve to the same parsed key.\n *\n * @example\n * driver.wasCalled(\"set\"); // any set\n * driver.wasCalled(\"set\", \"users.42\"); // set on this specific key\n * driver.wasCalled(\"set\", { id: 42 }); // same — object key normalized\n */\n public wasCalled(operation: string, key?: CacheKey): boolean {\n if (key === undefined) {\n return this.callLog.some((call) => call.operation === operation);\n }\n\n const parsedKey = this.parseKey(key);\n\n return this.callLog.some(\n (call) => call.operation === operation && call.key === parsedKey,\n );\n }\n\n /**\n * Return the raw stored value for `key`, bypassing TTL handling and clone\n * protection. Useful when a test wants to assert on the persisted shape\n * (or assert that an entry expired without going through `get`).\n *\n * Returns `undefined` when the key isn't present.\n */\n public getStored<T = any>(key: CacheKey): T | undefined {\n const parsedKey = this.parseKey(key);\n const entry = this.storage.get(parsedKey);\n\n if (!entry) {\n return undefined;\n }\n\n return entry.data as T;\n }\n\n /**\n * Wipe everything — storage, tag index, and the call log. Pair with\n * Vitest's `beforeEach` to get clean isolation between tests.\n */\n public reset(): void {\n this.storage.clear();\n this.callLog.length = 0;\n }\n\n /**\n * Append a row to {@link callLog}. Internal helper called by every\n * recorded op before the actual work runs.\n */\n protected recordCall(\n operation: string,\n key: string | undefined,\n args: unknown[] = [],\n ): void {\n this.callLog.push({\n operation,\n key,\n args,\n timestamp: Date.now(),\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mock-cache-driver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/mock-cache-driver.ts"],"sourcesContent":["import type {\n CacheCall,\n CacheData,\n CacheDriver,\n CacheKey,\n CacheSetOptions,\n CacheSetResult,\n CacheTtl,\n MockCacheOptions,\n} from \"../types\";\nimport { BaseCacheDriver } from \"./base-cache-driver\";\n\n/**\n * In-memory cache driver with introspection helpers, intended for use as a\n * test double in downstream packages.\n *\n * **Role.** Drop-in replacement for any real driver in test setups, with\n * extra surface that makes behavioral assertions easy: every public op gets\n * recorded into {@link MockCacheDriver.callLog}, and {@link wasCalled} /\n * {@link getStored} / {@link reset} let tests verify side effects without\n * pulling in a real Redis / Postgres / file system.\n *\n * **Responsibility.**\n * - Owns: in-memory storage backed by a `Map`, the `callLog`, TTL handling\n * via `parseCachedData`, `onConflict` policies, and tag-index storage.\n * - Does NOT own: similarity retrieval (vectors are recorded into the\n * `callLog` but `similar()` throws — use `MemoryCacheDriver` for tests\n * that need real nearest-neighbor scoring), connection lifecycle\n * (no-op `connect`/`disconnect`), or eviction (no `maxSize`).\n *\n * Register it like any other driver — sub-paths are not part of the\n * package's export convention; the same single barrel ships it next to\n * the production drivers.\n *\n * @example\n * import { cache, MockCacheDriver } from \"@warlock.js/cache\";\n *\n * beforeEach(async () => {\n * cache.setCacheConfigurations({\n * default: \"mock\",\n * drivers: { mock: MockCacheDriver },\n * options: { mock: {} },\n * });\n * await cache.init();\n * });\n *\n * it(\"invalidates the user cache after update\", async () => {\n * await userService.update(42, { name: \"Jane\" });\n *\n * const driver = cache.currentDriver as MockCacheDriver;\n * expect(driver.wasCalled(\"remove\", \"users.42\")).toBe(true);\n * });\n */\nexport class MockCacheDriver\n extends BaseCacheDriver<MockCacheDriver, MockCacheOptions>\n implements CacheDriver<MockCacheDriver, MockCacheOptions>\n{\n /**\n * {@inheritdoc}\n */\n public name = \"mock\";\n\n /**\n * {@inheritdoc}\n */\n public options: MockCacheOptions = {};\n\n /**\n * Storage backing the mock — keyed by post-`parseKey` string. Public-readonly\n * so tests can introspect raw entries when {@link getStored} isn't enough.\n */\n public readonly storage: Map<string, CacheData> = new Map();\n\n /**\n * Ordered record of every public operation routed through this driver.\n * Pushed to before each op runs; tests assert via {@link wasCalled} or by\n * inspecting the array directly.\n */\n public readonly callLog: CacheCall[] = [];\n\n /**\n * Standard driver setup. Mirrors the null driver — no connection, no\n * resources to release.\n */\n public async connect(): Promise<void> {\n this.recordCall(\"connect\", undefined);\n await super.connect();\n }\n\n /**\n * Standard driver teardown.\n */\n public async disconnect(): Promise<void> {\n this.recordCall(\"disconnect\", undefined);\n await super.disconnect();\n }\n\n /**\n * Wipe everything under `namespace`. Matches `MemoryCacheDriver` semantics\n * — the namespace itself and any key with the namespace prefix is removed.\n */\n public async removeNamespace(namespace: string): Promise<void> {\n const parsed = this.parseKey(namespace);\n\n this.recordCall(\"removeNamespace\", parsed, [namespace]);\n this.log(\"clearing\", parsed);\n\n const prefix = parsed + \".\";\n\n for (const key of [...this.storage.keys()]) {\n if (key === parsed || key.startsWith(prefix)) {\n this.storage.delete(key);\n }\n }\n\n this.log(\"cleared\", parsed);\n }\n\n /**\n * Standard `set` with full `onConflict` support. Honors scope-default TTL\n * via {@link BaseCacheDriver.resolveSetOptions}.\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, staleAt } = this.resolveSetOptions(ttlOrOptions);\n\n this.recordCall(\"set\", parsedKey, [value, ttlOrOptions]);\n this.log(\"caching\", parsedKey);\n\n const existing = onConflict === \"upsert\" ? null : await this.get(key);\n const exists = existing !== null;\n\n if (onConflict === \"create\" && exists) {\n const result: CacheSetResult = { wasSet: false, existing };\n\n return result;\n }\n\n if (onConflict === \"update\" && !exists) {\n const result: CacheSetResult = { wasSet: false, existing: null };\n\n return result;\n }\n\n const data = this.prepareDataForStorage(value, ttl, staleAt);\n this.storage.set(parsedKey, data);\n\n if (tags && tags.length > 0) {\n await this.applyTags(parsedKey, tags);\n }\n\n this.log(\"cached\", parsedKey);\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\n return result;\n }\n\n return value;\n }\n\n /**\n * Standard `get` with TTL handling. Emits `hit` / `miss` events to keep\n * downstream metrics tests realistic.\n */\n public async get<T = any>(key: CacheKey): Promise<T | null> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"get\", parsedKey);\n this.log(\"fetching\", parsedKey);\n\n const data = this.storage.get(parsedKey);\n\n if (!data) {\n this.log(\"notFound\", parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n const value = await this.parseCachedData(parsedKey, data);\n\n if (value === null) {\n // expired — the parse helper already logged + queued the cleanup\n this.storage.delete(parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n await this.emit(\"hit\", { key: parsedKey, value });\n\n return value as T;\n }\n\n /**\n * Read the raw {@link CacheData} wrapper from the in-memory `Map`,\n * including `staleAt` metadata. Returns `null` for missing or expired\n * entries — `swr()` consumes this to branch on freshness.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const parsedKey = this.parseKey(key);\n const entry = this.storage.get(parsedKey);\n\n if (!entry) {\n return null;\n }\n\n if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {\n return null;\n }\n\n return entry;\n }\n\n /**\n * Standard `remove` — drops the entry and emits the `removed` event.\n */\n public async remove(key: CacheKey): Promise<void> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"remove\", parsedKey);\n this.log(\"removing\", parsedKey);\n\n this.storage.delete(parsedKey);\n\n this.log(\"removed\", parsedKey);\n await this.emit(\"removed\", { key: parsedKey });\n }\n\n /**\n * Standard `flush` — wipes the entire mock store + tag index. Does NOT\n * touch the call log; use {@link reset} to clear that as well.\n */\n public async flush(): Promise<void> {\n this.recordCall(\"flush\", undefined);\n this.log(\"flushing\");\n\n this.storage.clear();\n\n this.log(\"flushed\");\n await this.emit(\"flushed\");\n }\n\n /**\n * Was a given operation invoked? When `key` is provided, the match is\n * post-`parseKey` so callers pass the same key shape they used at the\n * call site — strings or objects, both resolve to the same parsed key.\n *\n * @example\n * driver.wasCalled(\"set\"); // any set\n * driver.wasCalled(\"set\", \"users.42\"); // set on this specific key\n * driver.wasCalled(\"set\", { id: 42 }); // same — object key normalized\n */\n public wasCalled(operation: string, key?: CacheKey): boolean {\n if (key === undefined) {\n return this.callLog.some((call) => call.operation === operation);\n }\n\n const parsedKey = this.parseKey(key);\n\n return this.callLog.some(\n (call) => call.operation === operation && call.key === parsedKey,\n );\n }\n\n /**\n * Return the raw stored value for `key`, bypassing TTL handling and clone\n * protection. Useful when a test wants to assert on the persisted shape\n * (or assert that an entry expired without going through `get`).\n *\n * Returns `undefined` when the key isn't present.\n */\n public getStored<T = any>(key: CacheKey): T | undefined {\n const parsedKey = this.parseKey(key);\n const entry = this.storage.get(parsedKey);\n\n if (!entry) {\n return undefined;\n }\n\n return entry.data as T;\n }\n\n /**\n * Wipe everything — storage, tag index, and the call log. Pair with\n * Vitest's `beforeEach` to get clean isolation between tests.\n */\n public reset(): void {\n this.storage.clear();\n this.callLog.length = 0;\n }\n\n /**\n * Append a row to {@link callLog}. Internal helper called by every\n * recorded op before the actual work runs.\n */\n protected recordCall(\n operation: string,\n key: string | undefined,\n args: unknown[] = [],\n ): void {\n this.callLog.push({\n operation,\n key,\n args,\n timestamp: Date.now(),\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
|
2
2
|
import { CacheDriver, CacheKey, CacheSetOptions, CacheTtl, NullCacheDriverOptions } from "../types.mjs";
|
|
3
3
|
import { GenericObject } from "@mongez/reinforcements";
|
|
4
4
|
|
|
5
|
-
//#region
|
|
5
|
+
//#region ../cache/src/drivers/null-cache-driver.d.ts
|
|
6
6
|
declare class NullCacheDriver extends BaseCacheDriver<NullCacheDriver, NullCacheDriverOptions> implements CacheDriver<NullCacheDriver, NullCacheDriverOptions> {
|
|
7
7
|
/**
|
|
8
8
|
* Options list
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"null-cache-driver.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"null-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/null-cache-driver.ts"],"mappings":";;;;;cAWa,eAAA,SACH,eAAA,CAAgB,eAAA,EAAiB,sBAAA,aAC9B,WAAA,CAAY,eAAA,EAAiB,sBAAA;;AAF1C;;EAOS,OAAA,EAAS,aAAA;EANQ;;;EAWjB,IAAA;EALS;;;EAUT,IAAA,EAAM,aAAA;EA4BS;;;EAAA,IAvBX,MAAA;EA4CkB;;;cArCV,OAAA,GAAS,aAAA;EA4DH;;;EApDlB,UAAA,CAAW,OAAA,EAAS,aAAA;EAgFP;;;EAxEb,QAAA,CAAS,IAAA,EAAM,QAAA;EA3CA;;;EAkDT,eAAA,CAAgB,SAAA,WAAiB,OAAA;EAlDnC;;;EA6DE,GAAA,CACX,GAAA,EAAK,QAAA,EACL,MAAA,OACA,aAAA,GAAgB,QAAA,GAAW,eAAA,GAC1B,OAAA;EA5Da;;;EAuEH,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;EAxDnB;;;EAmEE,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;EApD1B;;;EA6DM,KAAA,IAAK,OAAA;EArDI;;;;;;EAiET,OAAA,IAAW,OAAA;EA9CtB;;;EAqDW,OAAA,IAAO,OAAA;AAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
2
|
import { log } from "@warlock.js/logger";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
4
|
+
//#region ../cache/src/drivers/null-cache-driver.ts
|
|
5
5
|
var NullCacheDriver = class extends BaseCacheDriver {
|
|
6
6
|
/**
|
|
7
7
|
* {@inheritdoc}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"null-cache-driver.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"null-cache-driver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/null-cache-driver.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport type {\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheTtl,\r\n NullCacheDriverOptions,\r\n} from \"../types\";\r\nimport { BaseCacheDriver } from \"./base-cache-driver\";\r\n\r\nexport class NullCacheDriver\r\n extends BaseCacheDriver<NullCacheDriver, NullCacheDriverOptions>\r\n implements CacheDriver<NullCacheDriver, NullCacheDriverOptions>\r\n{\r\n /**\r\n * Options list\r\n */\r\n public options: GenericObject = {};\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"null\";\r\n\r\n /**\r\n * Cached data\r\n */\r\n public data: GenericObject = {};\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public get client() {\r\n return this;\r\n }\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor(options: GenericObject = {}) {\r\n super();\r\n this.setOptions(options);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public setOptions(options: GenericObject) {\r\n this.options = options;\r\n return this;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public parseKey(_key: CacheKey) {\r\n return \"\";\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async removeNamespace(namespace: string) {\r\n log.info(\"cache\", \"clearing namespace\", namespace);\r\n\r\n log.success(\"cache\", \"namespace 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 log.info(\"cache\", \"setting key\", key);\r\n\r\n log.success(\"cache\", \"key set\", key);\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 log.info(\"cache\", \"fetching\", key);\r\n\r\n log.success(\"cache\", \"fetched\", key);\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remove(key: CacheKey) {\r\n log.info(\"cache\", \"removing\", key);\r\n\r\n log.success(\"cache\", \"removed\", key);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async flush() {\r\n log.info(\"cache\", \"flushing\", \"all\");\r\n\r\n log.success(\"cache\", \"flushed\", \"all\");\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * The null driver is a black-hole — `similar()` mirrors `get()` and returns\r\n * an empty result set rather than throwing.\r\n */\r\n public async similar(): Promise<any[]> {\r\n return [];\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async connect() {\r\n log.success(\"cache\", \"connected\", \"Connected to null cache driver\");\r\n }\r\n}\r\n"],"mappings":";;;;AAWA,IAAa,kBAAb,cACU,gBAEV;;;;CAmBE,IAAW,SAAS;EAClB,OAAO;CACT;;;;CAKA,AAAO,YAAY,UAAyB,CAAC,GAAG;EAC9C,MAAM;iBAvBwB,CAAC;cAKnB;cAKe,CAAC;EAc5B,KAAK,WAAW,OAAO;CACzB;;;;CAKA,AAAO,WAAW,SAAwB;EACxC,KAAK,UAAU;EACf,OAAO;CACT;;;;CAKA,AAAO,SAAS,MAAgB;EAC9B,OAAO;CACT;;;;CAKA,MAAa,gBAAgB,WAAmB;EAC9C,IAAI,KAAK,SAAS,sBAAsB,SAAS;EAEjD,IAAI,QAAQ,SAAS,qBAAqB,SAAS;EAEnD,OAAO;CACT;;;;CAKA,MAAa,IACX,KACA,QACA,eACc;EACd,IAAI,KAAK,SAAS,eAAe,GAAG;EAEpC,IAAI,QAAQ,SAAS,WAAW,GAAG;EAEnC,OAAO;CACT;;;;CAKA,MAAa,IAAI,KAAe;EAC9B,IAAI,KAAK,SAAS,YAAY,GAAG;EAEjC,IAAI,QAAQ,SAAS,WAAW,GAAG;EAEnC,OAAO;CACT;;;;CAKA,MAAa,OAAO,KAAe;EACjC,IAAI,KAAK,SAAS,YAAY,GAAG;EAEjC,IAAI,QAAQ,SAAS,WAAW,GAAG;CACrC;;;;CAKA,MAAa,QAAQ;EACnB,IAAI,KAAK,SAAS,YAAY,KAAK;EAEnC,IAAI,QAAQ,SAAS,WAAW,KAAK;CACvC;;;;;;;CAQA,MAAa,UAA0B;EACrC,OAAO,CAAC;CACV;;;;CAKA,MAAa,UAAU;EACrB,IAAI,QAAQ,SAAS,aAAa,gCAAgC;CACpE;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
2
|
import { CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheSimilarHit, CacheSimilarOptions, CacheTtl, PgCacheOptions, PgClientLike } from "../types.mjs";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
4
|
+
//#region ../cache/src/drivers/pg-cache-driver.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* Postgres cache driver with optional pgvector similarity support.
|
|
7
7
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-cache-driver.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"pg-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/pg-cache-driver.ts"],"mappings":";;;;;;AAkDA;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,aAAA,SACH,eAAA,CAAgB,YAAA,EAAc,cAAA,aAC3B,WAAA,CAAY,YAAA,EAAc,cAAA;EA2eV;;;EAtepB,IAAA;EALI;;;;EAAA,UAWD,WAAA;EAZ4B;;;;;EAmB/B,UAAA,CAAW,OAAA,EAAS,cAAA;EAApB;;;;;;EAAA,UAsCS,iBAAA,IAAqB,OAAA;EAqCvB;;;;;EAAA,UARJ,YAAA,CAAa,MAAA;EAuChB;;;;EAAA,cA/BO,KAAA;EA2FD;;;;EAAA,cAnFC,QAAA,IAAY,YAAA;EAsFE;;;;EAAA,UA9ElB,cAAA,CAAe,GAAA,YAAe,IAAA;EA6MvB;;;;;;;EA9LV,MAAA;EAkSkB;;;EA3PZ,OAAA,IAAO,OAAA;EA4QS;;;;;EA/PhB,UAAA,IAAU,OAAA;EAyTrB;;;EAjTW,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EA+SQ;;;EAjLE,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;;;;;;YAwDd,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;;;;EA4CpC,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;;;;;;;EAiBpB,eAAA,CAAgB,SAAA,WAAiB,OAAA;;;;;;;;EA4BjC,KAAA,IAAK,OAAA;;;;;;;;;;;;;;;;;EA6BL,OAAA,UACX,MAAA,YACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,eAAA,CAAgB,CAAA;AAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CacheConfigurationError, CacheUnsupportedError } from "../types.mjs";
|
|
2
2
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
4
|
+
//#region ../cache/src/drivers/pg-cache-driver.ts
|
|
5
5
|
/**
|
|
6
6
|
* Allowed characters in a Postgres identifier (table name). We accept the
|
|
7
7
|
* conservative ASCII subset and reject anything else — interpolating an
|