Skip to content

Commit 91c2b9b

Browse files
committed
chore: Refactor cachedFunction to use selectorToCacheKey
1 parent 7caa479 commit 91c2b9b

File tree

3 files changed

+107
-4
lines changed

3 files changed

+107
-4
lines changed

src/index.d.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1+
import {
2+
type Store,
3+
type FactoryConfig,
4+
type FactoryStore,
5+
} from 'cache-manager';
16
import {type ArgumentPaths, type AnyFunction} from './paths.d';
27

3-
export type CachedFunctionOptions<F extends AnyFunction> = {
4-
selector: ArgumentPaths<F>;
5-
};
8+
export type CachedFunctionInitializerOptions =
9+
{store: FactoryStore; config: FactoryConfig} |
10+
{store: Store};
11+
12+
export type CachedFunctionOptions<F extends AnyFunction> =
13+
CachedFunctionInitializerOptions &
14+
{selector?: ArgumentPaths<F>; ttl?: number};

src/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11

22
import _ from 'lodash';
3-
import type {CachedFunctionOptions} from './index.d';
3+
import {type Cache, caching} from 'cache-manager';
4+
import type {CachedFunctionInitializerOptions, CachedFunctionOptions} from './index.d';
45
import type {AnyFunction, ArgumentPaths} from './paths.d';
56

7+
let cache: Cache | undefined;
8+
9+
export async function getOrInitializeCache(options: CachedFunctionInitializerOptions) {
10+
if (!('store' in options)) {
11+
throw new Error('store is required');
12+
}
13+
14+
cache ||= await ('config' in options ? caching(options.store, options.config) : caching(options.store));
15+
return cache;
16+
}
17+
618
export function selectorToCacheKey<F extends AnyFunction>(arguments_: Parameters<F>, selector: ArgumentPaths<F>) {
719
const selectors = _.castArray(selector);
820
const values = _.at(arguments_, selectors);

tests/cache.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import {
3+
describe, it, expect, beforeEach,
4+
} from 'bun:test';
5+
import {memoryStore} from 'cache-manager';
6+
import {getOrInitializeCache, selectorToCacheKey} from '../src/index';
7+
import type {CachedFunctionInitializerOptions} from '../src/index.d';
8+
9+
describe('getOrInitializeCache', () => {
10+
beforeEach(() => {
11+
global.cache = undefined;
12+
});
13+
14+
it('should initialize cache with config when options include config', async () => {
15+
const options: CachedFunctionInitializerOptions = {
16+
store: memoryStore(),
17+
config: {max: 100, ttl: 60},
18+
};
19+
20+
const result = await getOrInitializeCache(options);
21+
22+
expect(result).toBeDefined();
23+
expect(result.store).toBeDefined();
24+
});
25+
26+
it('should initialize cache without config when options do not include config', async () => {
27+
const options: CachedFunctionInitializerOptions = {
28+
store: memoryStore(),
29+
};
30+
31+
const result = await getOrInitializeCache(options);
32+
33+
expect(result).toBeDefined();
34+
expect(result.store).toBeDefined();
35+
});
36+
37+
it('should reuse the initialized cache', async () => {
38+
const options: CachedFunctionInitializerOptions = {
39+
store: memoryStore(),
40+
};
41+
42+
const firstInit = await getOrInitializeCache(options);
43+
const secondInit = await getOrInitializeCache(options);
44+
45+
expect(firstInit).toBe(secondInit);
46+
});
47+
48+
it('should throw an error if store is not provided', async () => {
49+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
50+
const options = {
51+
config: {max: 100, ttl: 60},
52+
} as any;
53+
54+
// eslint-disable-next-line @typescript-eslint/await-thenable, @typescript-eslint/no-confusing-void-expression
55+
await expect(getOrInitializeCache(options)).rejects.toThrow('store is required');
56+
});
57+
});
58+
59+
describe('selectorToCacheKey', () => {
60+
it('should generate a cache key for a single argument path', () => {
61+
const arguments_ = [{id: 1, name: 'test'}];
62+
const selector = '0.id';
63+
const key = selectorToCacheKey(arguments_, selector);
64+
65+
expect(key).toBe('{"0.id":1}');
66+
});
67+
68+
it('should generate a cache key for multiple argument paths', () => {
69+
const arguments_ = [{id: 1, name: 'test'}, {value: 'data'}];
70+
const key = selectorToCacheKey(arguments_, ['0.id', '1.value']);
71+
72+
expect(key).toBe('{"0.id":1,"1.value":"data"}');
73+
});
74+
75+
it('should handle empty selectors and arguments', () => {
76+
const arguments_: any = [];
77+
const selector: any = [];
78+
const key = selectorToCacheKey(arguments_, selector);
79+
80+
expect(key).toBe('{}');
81+
});
82+
});

0 commit comments

Comments
 (0)