Skip to content

Commit 375468d

Browse files
committed
chore: Refactor cachedFunction to use selectorToCacheKey
1 parent 64bf9ab commit 375468d

File tree

5 files changed

+59
-24
lines changed

5 files changed

+59
-24
lines changed

src/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export type CachedFunctionInitializerOptions =
99
{store: FactoryStore; config: FactoryConfig} |
1010
{store: Store};
1111

12-
export type CachedFunctionOptions<F extends AnyFunction> = CachedFunctionInitializerOptions & {
12+
export type CachedFunctionOptions<F extends AnyFunction> = Partial<CachedFunctionInitializerOptions> & {
1313
selector: ArgumentPaths<F>;
1414
ttl?: number;
1515
};

src/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
import _ from 'lodash';
2-
import {type Cache, caching} from 'cache-manager';
2+
import {type Cache, caching, type Store} from 'cache-manager';
33
import type {CachedFunctionInitializerOptions, CachedFunctionOptions} from './index.d';
44
import type {AnyFunction, ArgumentPaths} from './paths.d';
55

66
let cache: Cache | undefined;
77

8-
export async function initializeCache(options?: CachedFunctionInitializerOptions) {
8+
export async function getOrInitializeCache<S extends Store>(options?: CachedFunctionInitializerOptions) {
99
if (!cache && !options) {
1010
throw new Error('cache is not initialized and no options provided');
1111
}
1212

13-
if (options && !('store' in options)) {
14-
throw new Error('store is required');
13+
if (!cache && options && !('store' in options)) {
14+
throw new Error('Store is not provided in options but is required to initialize the cache');
1515
}
1616

1717
cache ||= await ('config' in options! ? caching(options.store, options.config) : caching(options!.store));
18-
return cache;
18+
19+
return cache as Cache<S>;
1920
}
2021

2122
export function resetCache() {
@@ -43,8 +44,7 @@ export function selectorToCacheKey<F extends AnyFunction>(arguments_: Parameters
4344
export function cachedFunction<F extends AnyFunction>(function_: F, options: CachedFunctionOptions<F>) {
4445
return async (...arguments_: Parameters<F>): Promise<ReturnType<F>> => {
4546
const cacheKey = selectorToCacheKey(arguments_, options.selector);
46-
47-
const cache = await initializeCache(options);
47+
const cache = await getOrInitializeCache(options as CachedFunctionInitializerOptions);
4848

4949
const cacheValue = await cache.get<ReturnType<F>>(cacheKey);
5050
if (cacheValue !== undefined) {

src/paths.d.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,9 @@ export type AnyFunction = (...arguments_: any[]) => any;
2020
*
2121
* @template T - The type of the object.
2222
*/
23-
export type Paths<T> = T extends Record<string, unknown>
24-
? {[K in keyof T]: `${Exclude<K, symbol>}${'' | `.${Paths<T[K]>}`}`}[keyof T]
25-
: T extends Array<infer U>
26-
? Paths<U> extends string
27-
? `${number}` | `${number}.${Paths<U>}`
28-
: never
29-
: never;
23+
type Paths<T> = T extends Record<string, unknown> ? {[K in keyof T]:
24+
`${Exclude<K, symbol>}${'' | `.${Paths<T[K]>}`}`
25+
}[keyof T] : never;
3026

3127
/**
3228
* Represents the paths of the arguments of a given function type.

tests/index.test.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
} from 'bun:test';
55
import {memoryStore} from 'cache-manager';
66
import {
7-
initializeCache, selectorToCacheKey, cachedFunction, resetCache,
7+
getOrInitializeCache, selectorToCacheKey, cachedFunction, resetCache,
88
} from '../src/index';
99
import type {CachedFunctionInitializerOptions, CachedFunctionOptions} from '../src/index.d';
1010

@@ -19,7 +19,7 @@ describe('initializeCache', () => {
1919
config: {max: 100, ttl: 60},
2020
};
2121

22-
const result = await initializeCache(options);
22+
const result = await getOrInitializeCache(options);
2323

2424
expect(result).toBeDefined();
2525
expect(result.store).toBeDefined();
@@ -30,7 +30,7 @@ describe('initializeCache', () => {
3030
store: memoryStore(),
3131
};
3232

33-
const result = await initializeCache(options);
33+
const result = await getOrInitializeCache(options);
3434

3535
expect(result).toBeDefined();
3636
expect(result.store).toBeDefined();
@@ -41,8 +41,8 @@ describe('initializeCache', () => {
4141
store: memoryStore(),
4242
};
4343

44-
const firstInit = await initializeCache(options);
45-
const secondInit = await initializeCache(options);
44+
const firstInit = await getOrInitializeCache(options);
45+
const secondInit = await getOrInitializeCache(options);
4646

4747
expect(firstInit).toBe(secondInit);
4848
});
@@ -52,11 +52,12 @@ describe('initializeCache', () => {
5252
config: {max: 100, ttl: 60},
5353
} as any;
5454

55-
await expect(initializeCache(options)).rejects.toThrow('store is required');
55+
await expect(getOrInitializeCache(options)).rejects.toThrow('Store is not provided in options but is required to initialize the cache');
5656
});
5757

5858
it('should throw an error if cache is not initialized and no options are provided', async () => {
59-
await expect(initializeCache()).rejects.toThrow('cache is not initialized and no options provided');
59+
resetCache();
60+
await expect(getOrInitializeCache()).rejects.toThrow('cache is not initialized and no options provided');
6061
});
6162

6263
it('should initialize cache only once, even with multiple config calls', async () => {
@@ -65,8 +66,8 @@ describe('initializeCache', () => {
6566
config: {max: 100, ttl: 60},
6667
};
6768

68-
const firstCall = await initializeCache(options);
69-
const secondCall = await initializeCache();
69+
const firstCall = await getOrInitializeCache(options);
70+
const secondCall = await getOrInitializeCache();
7071

7172
expect(firstCall).toBe(secondCall);
7273
});

tests/redis.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {random} from 'lodash';
2+
import {type RedisStore, redisStore} from 'cache-manager-ioredis-yet';
3+
import {cachedFunction, getOrInitializeCache, resetCache} from '../src';
4+
5+
type Person = {
6+
id?: string;
7+
name: string;
8+
age: number;
9+
address: {
10+
street: string;
11+
city: string;
12+
};
13+
};
14+
15+
async function createPerson(person: Person) {
16+
person.id = random(0, 100_000).toString();
17+
console.log('Person created!!!!!');
18+
return person;
19+
}
20+
21+
const cache = await getOrInitializeCache<RedisStore>({
22+
store: await redisStore(),
23+
});
24+
const cachedCreatePerson = cachedFunction(createPerson, {
25+
selector: '0.id',
26+
ttl: 10_000,
27+
});
28+
29+
const person = await cachedCreatePerson({
30+
name: 'Tomer Horowitz',
31+
age: 23,
32+
address: {
33+
street: '123 Main St.',
34+
city: 'Springfield',
35+
},
36+
});
37+
console.log(person.id, person.name);
38+

0 commit comments

Comments
 (0)