Skip to content

Commit

Permalink
chore: Update typescript peer dependency to version 5.5.4
Browse files Browse the repository at this point in the history
  • Loading branch information
tomerh2001 committed Sep 12, 2024
1 parent 88b1b98 commit 3d4b1c3
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 147 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"xo": "^0.59.3"
},
"peerDependencies": {
"typescript": "^5.6.2"
"typescript": "^5.5.4"
},
"scripts": {
"build": "tsc",
Expand Down
53 changes: 45 additions & 8 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ export type SingleOrArray<T> = T | T[];
* Represents a logger object.
*/
export type Logger = {
trace: (...args: any) => any;
debug: (...args: any) => any;
info: (...args: any) => any;
warn: (...args: any) => any;
error: (...args: any) => any;
}
trace: (...arguments_: any) => any;
debug: (...arguments_: any) => any;
info: (...arguments_: any) => any;
warn: (...arguments_: any) => any;
error: (...arguments_: any) => any;
};

/**
* Represents any function that takes any number of arguments and returns any value.
Expand Down Expand Up @@ -52,8 +52,8 @@ export type ArgumentPaths<F extends CacheableFunction> = SingleOrArray<Paths<Par
/**
* Represents the options for initializing a cached function.
*/
export type CachedFunctionInitializerOptions = {logger?: Partial<Logger>;} &
({store: FactoryStore; config: FactoryConfig} | {store: Store});
export type CachedFunctionInitializerOptions = {logger?: Partial<Logger>} &
({store: FactoryStore; config: FactoryConfig} | {store: Store});

/**
* Options for a cached function.
Expand All @@ -73,4 +73,41 @@ export type CachedFunctionOptions<F extends CacheableFunction> = Partial<CachedF
force?: boolean;
/** Don't use the cache at all - when `true`, calls to functions wrapped by `cachedFunction` will essentially just call the original function. */
noCache?: boolean;
/**
* This can be used to namespace the keys in the cache.
*
* Will be inserted into the key object as `{namespace: value}`.
*/
namespace?: string;
/**
* If set to `true`, the function will return the raw value (either from the cache or the function call),
* instead of the `CachedFunctionResult` object.
*/
returnRawValue?: boolean;
};

export type CacheStatus = 'hit' | 'miss';
export type CachedFunctionResult<T> = {
/**
* The options used to cache the function.
*/
options: CachedFunctionOptions<CacheableFunction>;
/**
* The status of the cache.
* - `'hit'` - The cache was found.
* - `'miss'` - The cache was not found.
*/
status?: CacheStatus;
/**
* The cache key used to store the value.
*/
key?: string;
/**
* The result returned by the function/cache.
*/
result: T;
/**
* Whether the cache `set` operation was called.
*/
created: boolean;
};
84 changes: 59 additions & 25 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,20 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import _ from 'lodash';
import {
type Cache, caching, type Store,
} from 'cache-manager';
import {type Cache, caching, type Store} from 'cache-manager';
import {
type CachedFunctionInitializerOptions, type CachedFunctionOptions, type CacheableFunction, type ArgumentPaths,
Logger,
type Logger,
type CachedFunctionResult,
} from './index.d';

let cache: Cache | undefined;
let logger: Logger = {
info(...args: any) {},
debug(...args: any) {},
trace(...args: any) {},
warn(...args: any) {},
error(...args: any) {},
info(...arguments_: any) {}, // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
debug(...arguments_: any) {}, // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
trace(...arguments_: any) {}, // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
warn(...arguments_: any) {}, // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
error(...arguments_: any) {}, // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
};

/**
Expand All @@ -41,9 +40,9 @@ export async function getOrInitializeCache<S extends Store>(options?: CachedFunc
logger = options.logger as Logger;
}

logger?.trace('Initializing cache');
logger.trace('Initializing cache');
cache ||= await ('config' in options! ? caching(options.store, options.config) : caching(options!.store));
logger?.trace('Cache initialized');
logger.trace('Cache initialized');

return cache as Cache<S>;
}
Expand All @@ -53,7 +52,7 @@ export async function getOrInitializeCache<S extends Store>(options?: CachedFunc
*/
export function resetCache() {
cache = undefined;
logger?.warn('You have called resetCache, which is deprecated and basically does nothing. To close any open connections, please retrieve the cache object from getOrInitializeCache and close it directly.');
logger.warn('You have called resetCache, which is deprecated and basically does nothing. To close any open connections, please retrieve the cache object from getOrInitializeCache and close it directly.');
}

/**
Expand All @@ -66,10 +65,10 @@ export function resetCache() {
* @throws {Error} If a path in the selector does not exist in the provided arguments.
* @throws {TypeError} If a path in the selector points to a function, which is not serializable.
*/
export function selectorToCacheKey<F extends CacheableFunction>(arguments_: Parameters<F>, selector: ArgumentPaths<F>) {
export function selectorToCacheKey<F extends CacheableFunction>(arguments_: Parameters<F>, selector: ArgumentPaths<F>, namespace?: string): string {
const selectors = _.castArray(selector);
if (selectors.length === 0) {
logger?.trace(arguments_, 'No selectors provided, using the entire arguments object as the cache key');
logger.trace(arguments_, 'No selectors provided, using the entire arguments object as the cache key');
return JSON.stringify(arguments_);
}

Expand All @@ -85,7 +84,12 @@ export function selectorToCacheKey<F extends CacheableFunction>(arguments_: Para

return value;
});

const result = _.zipObject(selectors, values);
if (namespace) {
result.namespace = namespace;
}

return JSON.stringify(result);
}

Expand All @@ -99,34 +103,64 @@ export function selectorToCacheKey<F extends CacheableFunction>(arguments_: Para
* @returns A promise that resolves to the result of the function.
*/
export function cachedFunction<F extends CacheableFunction>(function_: F, options?: CachedFunctionOptions<F>) {
return async (...arguments_: Parameters<F>): Promise<ReturnType<F>> => {
return async (...arguments_: Parameters<F>): Promise<ReturnType<F> | CachedFunctionResult<ReturnType<F>>> => {
const cacheOptions = _.merge({}, options ?? {}, function_.cacheOptions ?? {});
if (_.keys(cacheOptions).length === 0) {
throw new Error('No cache options provided, either use the @CacheOptions decorator or provide options to cachedFunction directly.');
}

if (!cacheOptions.noCache) {
logger.trace('Cache is disabled, calling the original function directly');
if (cacheOptions.noCache && cacheOptions.returnRawValue) {
logger.trace('Cache is disabled via `noCache=true`. Calling the original function directly and returning the raw value');
return function_(...arguments_) as ReturnType<F>;
}

if (cacheOptions.noCache && !cacheOptions.returnRawValue) {
logger.trace('Cache is disabled via `noCache=true`. Calling the original function directly and returning a `CachedFunctionResult` object');
return {
result: function_(...arguments_) as ReturnType<F>,
created: false,
options: cacheOptions,
};
}

const cacheKey = selectorToCacheKey(arguments_, cacheOptions.selector!);
const cache = await getOrInitializeCache(options as CachedFunctionInitializerOptions);

logger?.trace({cacheKey}, 'Checking cache');
logger.trace({cacheKey}, 'Checking cache');
const cacheValue = await cache.get<ReturnType<F>>(cacheKey);
if (!cacheOptions.force && cacheValue !== undefined) {
logger?.trace({cacheKey}, 'Cache hit');
if (!cacheOptions.force && !_.isNil(cacheValue) && cacheOptions.returnRawValue) {
logger.trace({cacheKey}, 'Cache hit');
return cacheValue;
}
logger?.trace({cacheKey}, 'Cache miss');

if (!cacheOptions.force && !_.isNil(cacheValue) && !cacheOptions.returnRawValue) {
logger.trace({cacheKey}, 'Cache hit');
return {
key: cacheKey,
result: cacheValue,
status: 'hit',
created: false,
options: cacheOptions,
};
}

logger.trace({cacheKey}, 'Cache miss');
const result = await function_(...arguments_) as ReturnType<F>;
logger?.trace({cacheKey}, 'Setting cache');
logger.trace({cacheKey}, 'Setting cache');
await cache.set(cacheKey, result, cacheOptions.ttl);
logger?.trace({cacheKey}, 'Cache set');
logger.trace({cacheKey}, 'Cache set');

if (cacheOptions.returnRawValue) {
return result;
}

return result;
return {
key: cacheKey,
result,
status: 'miss',
created: true,
options: cacheOptions,
};
};
}

Expand Down Expand Up @@ -169,7 +203,7 @@ export function CacheOptions<F extends CacheableFunction>(
descriptor: TypedPropertyDescriptor<F>,
): any => {
if (!descriptor.value) {
logger?.warn('CacheOptions decorator is only supported on methods');
logger.warn('CacheOptions decorator is only supported on methods');
return;
}

Expand Down
Loading

0 comments on commit 3d4b1c3

Please sign in to comment.