-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
179 additions
and
223 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
export type { CacheManagerStore } from 'cache-manager' | ||
export type { Store } from './types' | ||
export type { Store, StoreConfig, Stores } from 'cache-manager' | ||
export { redisStore } from './redis' | ||
export * from './manager' | ||
export { memoryStore } from './memory' | ||
export type { MemoryConfig } from './memory' | ||
export type { MemoryStore, MemoryCache, MemoryConfig } from './memory' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,113 @@ | ||
import { Keyv } from 'keyv' | ||
import type { CacheableMemoryOptions } from 'cacheable' | ||
import { KeyvCacheableMemory } from 'cacheable' | ||
import { LRUCache } from 'lru-cache' | ||
import cloneDeep from 'lodash.clonedeep' | ||
import type { Cache, Config, Store } from 'cache-manager' | ||
|
||
export type MemoryConfig = CacheableMemoryOptions & { | ||
namespace?: string | ||
function clone<T>(object: T): T { | ||
if (typeof object === 'object' && object !== null) | ||
return cloneDeep(object) | ||
|
||
return object | ||
} | ||
|
||
export function memoryStore(options?: MemoryConfig) { | ||
const store = new KeyvCacheableMemory(options) | ||
const keyv = new Keyv({ store }, { namespace: '' }) | ||
return keyv | ||
type LRU = LRUCache<string, any> | ||
|
||
type Pre = LRUCache.OptionsTTLLimit<string, any, unknown> | ||
type Options = Omit<Pre, 'ttlAutopurge'> & Partial<Pick<Pre, 'ttlAutopurge'>> | ||
export type MemoryConfig = { | ||
max?: number | ||
sizeCalculation?: (value: unknown, key: string) => number | ||
shouldCloneBeforeSet?: boolean | ||
} & Options & | ||
Config | ||
|
||
export type MemoryStore = Store & { | ||
dump: LRU['dump'] | ||
load: LRU['load'] | ||
calculatedSize: LRU['calculatedSize'] | ||
get size(): number | ||
} | ||
export type MemoryCache = Cache<MemoryStore> | ||
|
||
/** | ||
* Wrapper for lru-cache. | ||
*/ | ||
export function memoryStore(arguments_?: MemoryConfig): MemoryStore { | ||
const shouldCloneBeforeSet = arguments_?.shouldCloneBeforeSet !== false // Clone by default | ||
const isCacheable = arguments_?.isCacheable ?? (value => value !== undefined) | ||
|
||
const lruOptions = { | ||
ttlAutopurge: true, | ||
...arguments_, | ||
max: arguments_?.max ?? 500, | ||
ttl: arguments_?.ttl === undefined ? 0 : arguments_.ttl, | ||
} | ||
|
||
const lruCache = new LRUCache(lruOptions) | ||
|
||
return { | ||
async del(key) { | ||
lruCache.delete(key) | ||
}, | ||
get: async <T>(key: string) => lruCache.get(key) as T, | ||
keys: async (pattern?: string) => { | ||
const keys = [...lruCache.keys()] | ||
if (!pattern) | ||
return keys | ||
|
||
const regex = new RegExp(`(?<!.)${pattern}`) | ||
return keys.filter(key => regex.test(key)) | ||
}, | ||
|
||
mget: async (...arguments_) => arguments_.map(x => lruCache.get(x)), | ||
async mset(arguments_, ttl?) { | ||
const opt = { ttl: ttl ?? lruOptions.ttl } as const | ||
for (const [key, value] of arguments_) { | ||
if (!isCacheable(value)) | ||
throw new Error(`no cacheable value ${JSON.stringify(value)}`) | ||
|
||
if (shouldCloneBeforeSet) | ||
lruCache.set(key, clone(value), opt) | ||
else | ||
lruCache.set(key, value, opt) | ||
} | ||
}, | ||
async mdel(...arguments_) { | ||
for (const key of arguments_) | ||
lruCache.delete(key) | ||
}, | ||
async reset() { | ||
lruCache.clear() | ||
}, | ||
ttl: async key => lruCache.getRemainingTTL(key), | ||
async set(key, value, opt) { | ||
if (!isCacheable(value)) | ||
throw new Error(`no cacheable value ${JSON.stringify(value)}`) | ||
|
||
if (shouldCloneBeforeSet) | ||
value = clone(value) | ||
|
||
const ttl = opt ?? lruOptions.ttl | ||
|
||
lruCache.set(key, value, { ttl }) | ||
}, | ||
get calculatedSize() { | ||
return lruCache.calculatedSize | ||
}, | ||
/** | ||
* This method is not available in the caching modules. | ||
*/ | ||
get size() { | ||
return lruCache.size | ||
}, | ||
/** | ||
* This method is not available in the caching modules. | ||
*/ | ||
dump: () => lruCache.dump(), | ||
/** | ||
* This method is not available in the caching modules. | ||
*/ | ||
load(...arguments_: Parameters<LRU['load']>) { | ||
lruCache.load(...arguments_) | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,24 @@ | ||
import type { KeyvRedisOptions, RedisClientConnectionType, RedisClientOptions, RedisClusterOptions } from '@keyv/redis' | ||
import KeyvRedis, { Keyv } from '@keyv/redis' | ||
|
||
export interface RedisConnect { | ||
host?: string | ||
port?: number | ||
username?: string | ||
password?: string | ||
db?: number | ||
} | ||
import type { RedisOptions } from 'ioredis' | ||
import { Redis } from 'ioredis' | ||
import type { Config } from 'cache-manager' | ||
import { RedisClusterConfig, redisInsStore } from 'cache-manager-ioredis-yet' | ||
|
||
export function redisStore( | ||
connect?: string | RedisClientOptions | RedisClusterOptions | RedisClientConnectionType | RedisConnect, | ||
options?: KeyvRedisOptions, | ||
options?: (RedisOptions | { clusterConfig: RedisClusterConfig }) & Config, | ||
) { | ||
if (typeof connect === 'object' && connect) { | ||
if (Reflect.get(connect, 'host')) { | ||
const c = connect as RedisConnect | ||
let connectStr = 'redis://' | ||
if (c.username) | ||
connectStr += `${c.username}:${c.password}@` | ||
connectStr += `${c.host}:${c.port}` | ||
if (c.db) | ||
connectStr += `/${c.db}` | ||
connect = connectStr | ||
} | ||
} | ||
options ||= {} | ||
const redisCache | ||
= 'clusterConfig' in options | ||
? new Redis.Cluster( | ||
options.clusterConfig.nodes, | ||
options.clusterConfig.options, | ||
) | ||
: new Redis(options) | ||
|
||
return redisInsStore(redisCache, options) | ||
} | ||
|
||
const keyv = new Keyv(new KeyvRedis(connect, options), { | ||
namespace: '', | ||
}) | ||
return keyv | ||
export { | ||
RedisClusterConfig, | ||
redisInsStore, | ||
} |
Oops, something went wrong.