Skip to content

Commit

Permalink
feat: pojo methods syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien-R44 committed Apr 7, 2024
1 parent ff32759 commit 81bd75e
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 68 deletions.
95 changes: 75 additions & 20 deletions packages/bentocache/src/bento_cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import type {
HasOptions,
ClearOptions,
GetSetFactory,
GetOrSetPojoOptions,
GetPojoOptions,
SetPojoOptions,
HasPojoOptions,
DeletePojoOptions,
DeleteManyPojoOptions,
} from './types/main.js'

export class BentoCache<KnownCaches extends Record<string, BentoStore>> implements CacheProvider {
Expand Down Expand Up @@ -131,56 +137,94 @@ export class BentoCache<KnownCaches extends Record<string, BentoStore>> implemen
/**
* Get a value from the cache
*/
get<T = any>(key: string): Promise<T | undefined | null>
get<T = any>(key: string, defaultValue?: Factory<T>, options?: GetOptions): Promise<T>
async get<T = any>(key: string, defaultValue?: Factory<T>, rawOptions?: GetOptions): Promise<T> {
return this.use().get<T>(key, defaultValue, rawOptions)
async get<T = any>(
keyOrOptions: string | GetPojoOptions<T>,
defaultValue?: Factory<T>,
rawOptions?: GetOptions,
): Promise<T> {
if (typeof keyOrOptions === 'string') {
return this.use().get<T>(keyOrOptions, defaultValue, rawOptions)
}

return this.use().get<T>(keyOrOptions)
}

/**
* Put a value in the cache
* Returns true if the value was set, false otherwise
*/
async set(key: string, value: any, options?: SetOptions) {
return this.use().set(key, value, options)
async set(keyOrOptions: string | SetPojoOptions, value?: any, options?: SetOptions) {
if (typeof keyOrOptions === 'string') {
return this.use().set(keyOrOptions, value, options)
}

return this.use().set(keyOrOptions)
}

/**
* Put a value in the cache forever
* Returns true if the value was set, false otherwise
*/
async setForever(key: string, value: any, options?: SetOptions) {
return this.use().setForever(key, value, options)
async setForever(keyOrOptions: string | SetPojoOptions, value?: any, options?: SetOptions) {
if (typeof keyOrOptions === 'string') {
return this.use().setForever(keyOrOptions, value, options)
}

return this.use().setForever(keyOrOptions)
}

/**
* Retrieve an item from the cache if it exists, otherwise store the value
* provided by the factory and return it
*/
async getOrSet<T>(key: string, factory: GetSetFactory<T>, options?: GetOrSetOptions): Promise<T> {
return this.use().getOrSet(key, factory, options)
async getOrSet<T>(
keyOrOptions: string | GetOrSetPojoOptions<T>,
factory?: GetSetFactory<T>,
options?: GetOrSetOptions,
): Promise<T> {
if (typeof keyOrOptions === 'string') {
return this.use().getOrSet(keyOrOptions, factory!, options)
}

return this.use().getOrSet(keyOrOptions)
}

/**
* Retrieve an item from the cache if it exists, otherwise store the value
* provided by the factory forever and return it
*/
getOrSetForever<T>(key: string, cb: GetSetFactory<T>, opts?: GetOrSetOptions): Promise<T> {
return this.use().getOrSetForever(key, cb, opts)
getOrSetForever<T>(
key: string | GetOrSetPojoOptions<T>,
cb?: GetSetFactory<T>,
opts?: GetOrSetOptions,
): Promise<T> {
if (typeof key === 'string') {
return this.use().getOrSetForever(key, cb!, opts)
}

return this.use().getOrSetForever(key)
}

/**
* Check if a key exists in the cache
*/
async has(key: string, options?: HasOptions) {
return this.use().has(key, options)
async has(keyOrOptions: string | HasPojoOptions, options?: HasOptions) {
if (typeof keyOrOptions === 'string') {
return this.use().has(keyOrOptions, options)
}

return this.use().has(keyOrOptions)
}

/**
* Check if key is missing in the cache
*/
async missing(key: string, options?: HasOptions) {
return this.use().missing(key, options)
async missing(keyOrOptions: string | HasPojoOptions, options?: HasOptions) {
if (typeof keyOrOptions === 'string') {
return this.use().missing(keyOrOptions, options)
}

return this.use().missing(keyOrOptions)
}

/**
Expand All @@ -196,15 +240,26 @@ export class BentoCache<KnownCaches extends Record<string, BentoStore>> implemen
* Delete a key from the cache
* Returns true if the key was deleted, false otherwise
*/
async delete(key: string, options?: DeleteOptions) {
return this.use().delete(key, options)
async delete(keyOrOptions: string | DeletePojoOptions, options?: DeleteOptions) {
if (typeof keyOrOptions === 'string') {
return this.use().delete(keyOrOptions, options)
}

return this.use().delete(keyOrOptions)
}

/**
* Delete multiple keys from the cache
*/
async deleteMany(keys: string[], options?: DeleteOptions): Promise<boolean> {
return this.use().deleteMany(keys, options)
async deleteMany(
keysOrOptions: string[] | DeleteManyPojoOptions,
options?: DeleteOptions,
): Promise<boolean> {
if (Array.isArray(keysOrOptions)) {
return this.use().deleteMany(keysOrOptions, options)
}

return this.use().deleteMany(keysOrOptions)
}

/**
Expand Down
101 changes: 75 additions & 26 deletions packages/bentocache/src/cache/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import type {
HasOptions,
ClearOptions,
GetSetFactory,
GetOrSetPojoOptions,
GetPojoOptions,
SetPojoOptions,
HasPojoOptions,
DeletePojoOptions,
DeleteManyPojoOptions,
} from '../types/main.js'

export class Cache implements CacheProvider {
Expand Down Expand Up @@ -44,14 +50,26 @@ export class Cache implements CacheProvider {
return new Cache(this.name, this.#stack.namespace(namespace))
}

get<T = any>(key: string): Promise<T | undefined | null>
get<T = any>(key: string, defaultValue?: Factory<T>, options?: GetOptions): Promise<T>
async get<T = any>(
key: string,
keyOrOptions: string | GetPojoOptions<T>,
defaultValue?: Factory<T>,
rawOptions?: GetOptions,
): Promise<T | undefined | null> {
const options = this.#stack.defaultOptions.cloneWith(rawOptions)
let key: string
let providedOptions: GetOptions
let defaultValueFn: Factory<T>

if (typeof keyOrOptions === 'string') {
key = keyOrOptions
providedOptions = rawOptions ?? {}
defaultValueFn = this.#resolveDefaultValue(defaultValue)
} else {
key = keyOrOptions.key
providedOptions = keyOrOptions
defaultValueFn = this.#resolveDefaultValue(keyOrOptions.defaultValue)
}

const options = this.#stack.defaultOptions.cloneWith(providedOptions)
const localItem = this.#stack.l1?.get(key, options)

if (localItem !== undefined && !localItem.isLogicallyExpired()) {
Expand All @@ -69,7 +87,7 @@ export class Cache implements CacheProvider {

if (!options.isGracePeriodEnabled) {
this.#stack.emit(new events.CacheMiss(key, this.name))
return this.#resolveDefaultValue(defaultValue)
return this.#resolveDefaultValue(defaultValueFn)
}

if (remoteItem) {
Expand All @@ -88,47 +106,68 @@ export class Cache implements CacheProvider {
* Set a value in the cache
* Returns true if the value was set, false otherwise
*/
async set(key: string, value: any, rawOptions?: SetOptions) {
const options = this.#stack.defaultOptions.cloneWith(rawOptions)
return this.#cacheWriter.set(key, value, options)
async set(keyOrOptions: string | SetPojoOptions, value?: any, rawOptions?: SetOptions) {
if (typeof keyOrOptions === 'string') {
const options = this.#stack.defaultOptions.cloneWith(rawOptions)
return this.#cacheWriter.set(keyOrOptions, value, options)
}

const options = this.#stack.defaultOptions.cloneWith(keyOrOptions)
return this.#cacheWriter.set(keyOrOptions.key, keyOrOptions.value, options)
}

/**
* Set a value in the cache forever
* Returns true if the value was set, false otherwise
*/
async setForever<T>(key: string, value: T, rawOptions?: SetOptions) {
const options = this.#stack.defaultOptions.cloneWith({ ttl: null, ...rawOptions })
return this.#cacheWriter.set(key, value, options)
async setForever<T>(keyOrOptions: string | SetPojoOptions, value?: T, rawOptions?: SetOptions) {
return this.set(keyOrOptions, value, { ttl: null, ...rawOptions })
}

/**
* Retrieve an item from the cache if it exists, otherwise store the value
* provided by the factory and return it
*/
async getOrSet<T>(key: string, factory: GetSetFactory<T>, options?: GetOrSetOptions): Promise<T> {
const cacheOptions = this.#stack.defaultOptions.cloneWith(options)
return this.#getSetHandler.handle(key, factory, cacheOptions)
async getOrSet<T>(
keyOrOptions: string | GetOrSetPojoOptions<T>,
factory?: GetSetFactory<T>,
options?: GetOrSetOptions,
): Promise<T> {
if (typeof keyOrOptions === 'string') {
const cacheOptions = this.#stack.defaultOptions.cloneWith(options)
return this.#getSetHandler.handle(keyOrOptions, factory, cacheOptions)
}

const cacheOptions = this.#stack.defaultOptions.cloneWith(keyOrOptions)
return this.#getSetHandler.handle(keyOrOptions.key, keyOrOptions.factory, cacheOptions)
}

/**
* Retrieve an item from the cache if it exists, otherwise store the value
* provided by the factory forever and return it
*/
async getOrSetForever<T>(
key: string,
factory: GetSetFactory<T>,
keyOrOptions: string | GetOrSetPojoOptions<T>,
factory?: GetSetFactory<T>,
options?: GetOrSetOptions,
): Promise<T> {
const cacheOptions = this.#stack.defaultOptions.cloneWith({ ttl: null, ...options })
return this.#getSetHandler.handle(key, factory, cacheOptions)
if (typeof keyOrOptions === 'string') {
const cacheOptions = this.#stack.defaultOptions.cloneWith({ ttl: null, ...options })
return this.#getSetHandler.handle(keyOrOptions, factory, cacheOptions)
}

const cacheOptions = this.#stack.defaultOptions.cloneWith({ ttl: null, ...keyOrOptions })
return this.#getSetHandler.handle(keyOrOptions.key, keyOrOptions.factory, cacheOptions)
}

/**
* Check if a key exists in the cache
*/
async has(key: string, options?: HasOptions) {
const cacheOptions = this.#stack.defaultOptions.cloneWith(options)
async has(keyOrOptions: string | HasPojoOptions, options?: HasOptions) {
const key = typeof keyOrOptions === 'string' ? keyOrOptions : keyOrOptions.key
const providedOptions = typeof keyOrOptions === 'string' ? options : keyOrOptions

const cacheOptions = this.#stack.defaultOptions.cloneWith(providedOptions)

const inRemote = await this.#stack.l2?.has(key, cacheOptions)
const inLocal = this.#stack.l1?.has(key)
Expand All @@ -139,8 +178,8 @@ export class Cache implements CacheProvider {
/**
* Check if key is missing in the cache
*/
async missing(key: string, options?: HasOptions) {
return !(await this.has(key, options))
async missing(keyOrOptions: string | HasPojoOptions, options?: HasOptions) {
return !(await this.has(keyOrOptions, options))
}

/**
Expand All @@ -157,8 +196,13 @@ export class Cache implements CacheProvider {
* Delete a key from the cache, emit cache:deleted event and
* publish invalidation through the bus
*/
async delete(key: string, rawOptions?: DeleteOptions): Promise<boolean> {
const options = this.#stack.defaultOptions.cloneWith(rawOptions)
async delete(
keyOrOptions: string | DeletePojoOptions,
rawOptions?: DeleteOptions,
): Promise<boolean> {
const isPojo = typeof keyOrOptions !== 'string'
const key = isPojo ? keyOrOptions.key : keyOrOptions
const options = this.#stack.defaultOptions.cloneWith(isPojo ? keyOrOptions : rawOptions)

this.#stack.l1?.delete(key, options)
await this.#stack.l2?.delete(key, options)
Expand All @@ -175,8 +219,13 @@ export class Cache implements CacheProvider {
* Then emit cache:deleted events for each key
* And finally publish invalidation through the bus
*/
async deleteMany(keys: string[], rawOptions?: DeleteOptions): Promise<boolean> {
const options = this.#stack.defaultOptions.cloneWith(rawOptions)
async deleteMany(
keysOrOptions: string[] | DeleteManyPojoOptions,
rawOptions?: DeleteOptions,
): Promise<boolean> {
const isPojo = !Array.isArray(keysOrOptions)
const options = this.#stack.defaultOptions.cloneWith(isPojo ? keysOrOptions : rawOptions)
const keys = isPojo ? keysOrOptions.keys : keysOrOptions

this.#stack.l1?.deleteMany(keys, options)
await this.#stack.l2?.deleteMany(keys, options)
Expand Down
30 changes: 28 additions & 2 deletions packages/bentocache/src/types/options/methods_options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { RawCommonOptions } from '../main.js'
import type { Factory, GetSetFactory, RawCommonOptions } from '../main.js'

/**
* Options accepted by the `getOrSet` method
Expand All @@ -8,29 +8,55 @@ export type GetOrSetOptions = Pick<
'earlyExpiration' | 'gracePeriod' | 'suppressL2Errors' | 'lockTimeout' | 'ttl' | 'timeouts'
>

/**
* Options accepted by the `getOrSet` method when passing an object
*/
export type GetOrSetPojoOptions<T> = { key: string; factory: GetSetFactory<T> } & GetOrSetOptions

/**
* Options accepted by the `set` method
*/
export type SetOptions = GetOrSetOptions

/*
* Options accepted by the `set` method when passing an object
*/
export type SetPojoOptions = { key: string; value: any } & SetOptions

/**
* Options accepted by the `get` method
*/
export type GetOptions = Pick<
RawCommonOptions,
'earlyExpiration' | 'gracePeriod' | 'suppressL2Errors' | 'ttl'
'earlyExpiration' | 'gracePeriod' | 'suppressL2Errors'
>

/**
* Options accepted by the `get` method when passing an object
*/
export type GetPojoOptions<T> = { key: string; defaultValue?: Factory<T> } & GetOptions

/**
* Options accepted by the `delete` method
*/
export type DeleteOptions = Pick<RawCommonOptions, 'suppressL2Errors'>

/**
* Options accepted by the `delete` method when passing an object
*/
export type DeletePojoOptions = { key: string } & DeleteOptions
export type DeleteManyPojoOptions = { keys: string[] } & DeleteOptions

/**
* Options accepted by the `has` method
*/
export type HasOptions = Pick<RawCommonOptions, 'suppressL2Errors'>

/**
* Options accepted by the `has` method when passing an object
*/
export type HasPojoOptions = { key: string } & HasOptions

/**
* Options accepted by the `clear` method
*/
Expand Down
Loading

0 comments on commit 81bd75e

Please sign in to comment.