@travetto/cache 2.1.4 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,13 +19,13 @@ npm install @travetto/model-{provider}
19
19
 
20
20
  Currently, the following are packages that provide [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/service/expiry.ts#L11):
21
21
 
22
- * [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") - [FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#L47), [MemoryModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/memory.ts#L50)
23
- * [DynamoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-dynamodb#readme "DynamoDB backing for the travetto model module.") - [DynamoDBModelService](https://github.com/travetto/travetto/tree/main/module/model-dynamodb/src/service.ts#L57)
24
- * [Elasticsearch Model Source](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch#readme "Elasticsearch backing for the travetto model module, with real-time modeling support for Elasticsearch mappings.") - [ElasticsearchModelService](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch/src/service.ts#L40)
25
- * [MongoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-mongo#readme "Mongo backing for the travetto model module.") - [MongoModelService](https://github.com/travetto/travetto/tree/main/module/model-mongo/src/service.ts#L44)
22
+ * [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") - [FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#L49), [MemoryModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/memory.ts#L54)
23
+ * [DynamoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-dynamodb#readme "DynamoDB backing for the travetto model module.") - [DynamoDBModelService](https://github.com/travetto/travetto/tree/main/module/model-dynamodb/src/service.ts#L58)
24
+ * [Elasticsearch Model Source](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch#readme "Elasticsearch backing for the travetto model module, with real-time modeling support for Elasticsearch mappings.") - [ElasticsearchModelService](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch/src/service.ts#L42)
25
+ * [MongoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-mongo#readme "Mongo backing for the travetto model module.") - [MongoModelService](https://github.com/travetto/travetto/tree/main/module/model-mongo/src/service.ts#L49)
26
26
  * [Redis Model Support](https://github.com/travetto/travetto/tree/main/module/model-redis#readme "Redis backing for the travetto model module.") - [RedisModelService](https://github.com/travetto/travetto/tree/main/module/model-redis/src/service.ts#L26)
27
- * [S3 Model Support](https://github.com/travetto/travetto/tree/main/module/model-s3#readme "S3 backing for the travetto model module.") - [S3ModelService](https://github.com/travetto/travetto/tree/main/module/model-s3/src/service.ts#L32)
28
- * [SQL Model Service](https://github.com/travetto/travetto/tree/main/module/model-sql#readme "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.") - [SQLModelService](https://github.com/travetto/travetto/tree/main/module/model-sql/src/service.ts#L36)
27
+ * [S3 Model Support](https://github.com/travetto/travetto/tree/main/module/model-s3#readme "S3 backing for the travetto model module.") - [S3ModelService](https://github.com/travetto/travetto/tree/main/module/model-s3/src/service.ts#L34)
28
+ * [SQL Model Service](https://github.com/travetto/travetto/tree/main/module/model-sql#readme "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.") - [SQLModelService](https://github.com/travetto/travetto/tree/main/module/model-sql/src/service.ts#L38)
29
29
 
30
30
  ## Decorators
31
31
  The caching framework provides method decorators that enables simple use cases. One of the requirements to use the caching decorators is that the method arguments, and return values need to be serializable into [JSON](https://www.json.org). Any other data types are not currently supported and would require either manual usage of the caching services directly, or specification of serialization/deserialization routines in the cache config.
@@ -39,10 +39,10 @@ Additionally, to use the decorators you will need to have a [CacheService](https
39
39
  import { MemoryModelService } from '@travetto/model';
40
40
  import { Cache, CacheService } from '@travetto/cache';
41
41
 
42
- async function request(url: string) {
43
- let value;
42
+ async function request(url: string): Promise<string> {
43
+ let value: string;
44
44
  // ...fetch content
45
- return value;
45
+ return value!;
46
46
  }
47
47
 
48
48
  export class Worker {
@@ -52,7 +52,7 @@ export class Worker {
52
52
  );
53
53
 
54
54
  @Cache('myCache', '1s')
55
- async calculateExpensiveResult(expression: string) {
55
+ async calculateExpensiveResult(expression: string): Promise<string> {
56
56
  const value = await request(`https://google.com?q=${expression}`);
57
57
  return value;
58
58
  }
@@ -74,9 +74,9 @@ The [@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/dec
74
74
  * `serialize` the function to execute before storing a cacheable value. This allows for any custom data modification needed to persist as a string properly.
75
75
  * `reinstate` the function to execute on return of a cached value. This allows for any necessary operations to conform to expected output (e.g. re-establishing class instances, etc.). This method should not be used often, as the return values of the methods should naturally serialize to/from `JSON` and the values should be usable either way.
76
76
 
77
- ### [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L37)
77
+ ### [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L41)
78
78
 
79
- Additionally, there is support for planned eviction via the [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L37) decorator. On successful execution of a method with this decorator, the matching keySpace/key value will be evicted from the cache. This requires coordination between multiple methods, to use the same `keySpace` and `key` to compute the expected key.
79
+ Additionally, there is support for planned eviction via the [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L41) decorator. On successful execution of a method with this decorator, the matching keySpace/key value will be evicted from the cache. This requires coordination between multiple methods, to use the same `keySpace` and `key` to compute the expected key.
80
80
 
81
81
  **Code: Using decorators to cache/evict user access**
82
82
  ```typescript
@@ -95,17 +95,17 @@ export class UserService {
95
95
  };
96
96
 
97
97
  @Cache('myCache', '5m', { keySpace: 'user.id' })
98
- async getUser(id: string) {
98
+ async getUser(id: string): Promise<User> {
99
99
  return this.database.lookupUser(id);
100
100
  }
101
101
 
102
102
  @EvictCache('myCache', { keySpace: 'user.id', params: user => [user.id] })
103
- async updateUser(user: User) {
103
+ async updateUser(user: User): Promise<void> {
104
104
  this.database.updateUser(user);
105
105
  }
106
106
 
107
107
  @EvictCache('myCache', { keySpace: 'user.id' })
108
- async deleteUser(userId: string) {
108
+ async deleteUser(userId: string): Promise<void> {
109
109
  this.database.deleteUser(userId);
110
110
  }
111
111
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@travetto/cache",
3
3
  "displayName": "Caching",
4
- "version": "2.1.4",
4
+ "version": "2.2.1",
5
5
  "description": "Caching functionality with decorators for declarative use.",
6
6
  "keywords": [
7
7
  "typescript",
@@ -27,9 +27,9 @@
27
27
  "directory": "module/cache"
28
28
  },
29
29
  "dependencies": {
30
- "@travetto/transformer": "^2.1.3",
31
- "@travetto/di": "^2.1.4",
32
- "@travetto/model": "^2.1.4"
30
+ "@travetto/transformer": "^2.2.1",
31
+ "@travetto/di": "^2.2.1",
32
+ "@travetto/model": "^2.2.1"
33
33
  },
34
34
  "docDependencies": {
35
35
  "@travetto/model-dynamodb": true,
package/src/decorator.ts CHANGED
@@ -13,7 +13,9 @@ import { CacheAware, CacheConfigⲐ, EvictConfigⲐ } from './internal/types';
13
13
  */
14
14
  export function Cache<F extends string, U extends Record<F, CacheService>>(field: F, maxAge: number | TimeSpan, config?: Omit<CacheConfig, 'maxAge'>): MethodDecorator;
15
15
  export function Cache<F extends string, U extends Record<F, CacheService>>(field: F, cfg?: CacheConfig): MethodDecorator;
16
- export function Cache<F extends string, U extends Record<F, CacheService>>(field: F, cfg?: number | TimeSpan | CacheConfig, config: Exclude<CacheConfig, 'maxAge'> = {}) {
16
+ export function Cache<F extends string, U extends Record<F, CacheService>>(
17
+ field: F, cfg?: number | TimeSpan | CacheConfig, config: Exclude<CacheConfig, 'maxAge'> = {}
18
+ ): MethodDecorator {
17
19
  if (cfg !== undefined) {
18
20
  if (typeof cfg === 'string' || typeof cfg === 'number') {
19
21
  config.maxAge = Util.timeToMs(cfg);
@@ -21,10 +23,12 @@ export function Cache<F extends string, U extends Record<F, CacheService>>(field
21
23
  config = cfg;
22
24
  }
23
25
  }
24
- return function <R extends Promise<unknown>>(target: U & CacheAware, propertyKey: string, descriptor: MethodDescriptor<R>) {
26
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
27
+ const dec = function <R extends Promise<unknown>>(target: U & CacheAware, propertyKey: string, descriptor: MethodDescriptor<R>): void {
25
28
  config.keySpace ??= `${target.constructor.name}.${propertyKey}`;
26
- (target[CacheConfigⲐ] ??= {})[propertyKey] = config as CacheConfig;
27
- };
29
+ (target[CacheConfigⲐ] ??= {})[propertyKey] = config;
30
+ } as MethodDecorator;
31
+ return dec;
28
32
  }
29
33
 
30
34
  /**
@@ -35,7 +39,7 @@ export function Cache<F extends string, U extends Record<F, CacheService>>(field
35
39
  * @augments `@trv:cache/Evict`
36
40
  */
37
41
  export function EvictCache<F extends string, U extends Record<F, CacheService>>(field: F, config: CoreCacheConfig = {}) {
38
- return function <R extends Promise<unknown>>(target: U & CacheAware, propertyKey: string, descriptor: MethodDescriptor<R>) {
42
+ return function <R extends Promise<unknown>>(target: U & CacheAware, propertyKey: string, descriptor: MethodDescriptor<R>): void {
39
43
  config.keySpace ??= `${target.constructor.name}.${propertyKey}`;
40
44
  (target[EvictConfigⲐ] ??= {})[propertyKey] = config;
41
45
  };
package/src/service.ts CHANGED
@@ -34,18 +34,18 @@ export class CacheService {
34
34
  this.#modelService = modelService;
35
35
  }
36
36
 
37
- async postConstruct() {
37
+ async postConstruct(): Promise<void> {
38
38
  if (isStorageSupported(this.#modelService) && EnvUtil.isDynamic()) {
39
39
  await this.#modelService.createModel?.(CacheRecord);
40
40
  }
41
41
  }
42
42
 
43
43
  /**
44
- * Get an item thowing an error if missing or expired. Allows for extending expiry based on access
44
+ * Get an item throwing an error if missing or expired. Allows for extending expiry based on access
45
45
  * @param id Record identifier
46
- * @param extendOnAccess should the expiry be extended on acces
46
+ * @param extendOnAccess should the expiry be extended on access
47
47
  */
48
- async get(id: string, extendOnAccess = true) {
48
+ async get(id: string, extendOnAccess = true): Promise<unknown> {
49
49
  const { expiresAt, issuedAt } = await this.#modelService.get(CacheRecord, id);
50
50
 
51
51
  const delta = expiresAt.getTime() - Date.now();
@@ -77,7 +77,7 @@ export class CacheService {
77
77
  * @param maxAge Max age in ms
78
78
  * @returns
79
79
  */
80
- async set(id: string, entry: unknown, maxAge?: number) {
80
+ async set(id: string, entry: unknown, maxAge?: number): Promise<unknown> {
81
81
  const entryText = CacheUtil.toSafeJSON(entry);
82
82
 
83
83
  const store = await this.#modelService.upsert(CacheRecord,
@@ -96,14 +96,14 @@ export class CacheService {
96
96
  * Remove an item by id
97
97
  * @param id
98
98
  */
99
- async delete(id: string) {
99
+ async delete(id: string): Promise<void> {
100
100
  await this.#modelService.delete(CacheRecord, id);
101
101
  }
102
102
 
103
103
  /**
104
104
  * Purge the cache store of all data, if supported
105
105
  */
106
- async purge() {
106
+ async purge(): Promise<void> {
107
107
  if (isStorageSupported(this.#modelService) && this.#modelService.truncateModel) {
108
108
  await this.#modelService.truncateModel(CacheRecord);
109
109
  } else {
@@ -114,9 +114,9 @@ export class CacheService {
114
114
  /**
115
115
  * Get an item optionally, returning undefined if missing. Allows for extending expiry based on access
116
116
  * @param id Record identifier
117
- * @param extendOnAccess should the expiry be extended on acces
117
+ * @param extendOnAccess should the expiry be extended on access
118
118
  */
119
- async getOptional(id: string, extendOnAccess = true) {
119
+ async getOptional(id: string, extendOnAccess = true): Promise<unknown | undefined> {
120
120
  let res: unknown;
121
121
 
122
122
  try {
@@ -137,7 +137,7 @@ export class CacheService {
137
137
  * @param fn Function to execute
138
138
  * @param params input parameters
139
139
  */
140
- async cache(target: CacheAware, method: string, fn: Function, params: unknown[]) {
140
+ async cache(target: CacheAware, method: string, fn: Function, params: unknown[]): Promise<unknown | undefined> {
141
141
  const config = target[CacheConfigⲐ]![method];
142
142
 
143
143
  const id = CacheUtil.generateKey(config, params);
@@ -164,7 +164,7 @@ export class CacheService {
164
164
  * @param fn Function to execute
165
165
  * @param params Input params to the function
166
166
  */
167
- async evict(target: CacheAware, method: string, fn: Function, params: unknown[]) {
167
+ async evict(target: CacheAware, method: string, fn: Function, params: unknown[]): Promise<unknown> {
168
168
  const config = target[EvictConfigⲐ]![method];
169
169
  const id = CacheUtil.generateKey(config, params);
170
170
  const val = await fn.apply(target, params);
package/src/util.ts CHANGED
@@ -13,9 +13,9 @@ export class CacheUtil {
13
13
  * @param value The value to make safe for storage
14
14
  * @param all Should functions and regex be included
15
15
  */
16
- static toSafeJSON(value: unknown, all = false) {
16
+ static toSafeJSON(value: unknown, all = false): string {
17
17
  const replacer = all ?
18
- ((key: string, val: unknown) => (Util.isFunction(val) || val instanceof RegExp) ? (val as RegExp)?.source : val) :
18
+ ((key: string, val: unknown): unknown => (val && val instanceof RegExp) ? val.source : (Util.isFunction(val) ? val.toString() : val)) :
19
19
  undefined;
20
20
 
21
21
  return Buffer.from(JSON.stringify(value, replacer)).toString('base64');
@@ -25,14 +25,14 @@ export class CacheUtil {
25
25
  * Read safe JSON back into an object
26
26
  * @param value The value to read as safe JSON
27
27
  */
28
- static fromSafeJSON(value: string | undefined) {
28
+ static fromSafeJSON(value: string | undefined): unknown {
29
29
  return value ? JSON.parse(Buffer.from(value, 'base64').toString('utf8')) : undefined;
30
30
  }
31
31
 
32
32
  /**
33
33
  * Generate key given config, cache source and input params
34
34
  */
35
- static generateKey(config: CoreCacheConfig, params: unknown[]) {
35
+ static generateKey(config: CoreCacheConfig, params: unknown[]): string {
36
36
  const input = config.params?.(params) ?? params;
37
37
  const keyParams = config.key?.(...input) ?? input;
38
38
  const key = `${config.keySpace!}_${this.toSafeJSON(keyParams)}`;
@@ -13,7 +13,7 @@ export class CacheTransformer {
13
13
  * When `@Cache` and `@Evict` are present
14
14
  */
15
15
  @OnMethod('Cache', 'Evict')
16
- static instrumentCache(state: TransformerState, node: ts.MethodDeclaration, dm?: DecoratorMeta) {
16
+ static instrumentCache(state: TransformerState, node: ts.MethodDeclaration, dm?: DecoratorMeta): ts.MethodDeclaration {
17
17
 
18
18
  const isCache = !!state.findDecorator(this, node, 'Cache');
19
19
  const dec = dm?.dec;
@@ -21,7 +21,7 @@ export class CacheTransformer {
21
21
  // If valid function
22
22
  if (dec && ts.isCallExpression(dec.expression)) {
23
23
  const params = dec.expression.arguments;
24
- const id = params[0] as ts.Identifier;
24
+ const mainExpression = params[0];
25
25
 
26
26
  const op = isCache ? 'cache' : 'evict';
27
27
 
@@ -50,7 +50,7 @@ export class CacheTransformer {
50
50
  state.factory.createReturnStatement(
51
51
  state.factory.createCallExpression(
52
52
  state.factory.createPropertyAccessExpression(
53
- state.factory.createElementAccessExpression(state.factory.createThis(), id), op
53
+ state.factory.createElementAccessExpression(state.factory.createThis(), mainExpression), op
54
54
  ),
55
55
  undefined,
56
56
  [
@@ -60,7 +60,8 @@ export class CacheTransformer {
60
60
  state.factory.createArrayLiteralExpression([
61
61
  state.factory.createSpreadElement(state.createIdentifier('arguments'))
62
62
  ])
63
- ] as (readonly ts.Expression[]))
63
+ ]
64
+ )
64
65
  )
65
66
  ])
66
67
  );