@common-stack/store-redis 8.3.1-alpha.4 → 8.3.1-alpha.6

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.
@@ -11,6 +11,7 @@ export declare class IORedisClient implements IRedisClient {
11
11
  }): Promise<string>;
12
12
  delMulti(keys: string[]): Promise<any>;
13
13
  del(key: string): Promise<number>;
14
+ keys(pattern: string): Promise<string[]>;
14
15
  on(event: string, cb: (...args: any[]) => void): Redis;
15
16
  once(event: string, cb: (...args: any[]) => void): Redis;
16
17
  }
@@ -24,6 +24,9 @@ import Redis from'ioredis';class IORedisClient {
24
24
  async del(key) {
25
25
  return await this.redis.del(key);
26
26
  }
27
+ async keys(pattern) {
28
+ return await this.redis.keys(pattern);
29
+ }
27
30
  on(event, cb) {
28
31
  return this.redis.on(event, cb);
29
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ioredis.js","sources":["../../src/core/ioredis.ts"],"sourcesContent":["import Redis from 'ioredis';\nimport type { IRedisClient } from 'common/server';\n\nexport class IORedisClient implements IRedisClient {\n private redis: Redis;\n\n constructor(config: { url: string }) {\n this.redis = new Redis(config.url);\n }\n\n async get(key: string, callback?: (...args: any[]) => void): Promise<any | null> {\n if (callback) {\n return this.redis.get(key, callback);\n }\n const data = await this.redis.get(key);\n return data;\n }\n\n async set(key: string, value: string, options?: { ex?: number }): Promise<string> {\n if (options?.ex) {\n return await this.redis.set(key, value, 'EX', options?.ex);\n } else {\n return await this.redis.set(key, value);\n }\n }\n\n async delMulti(keys: string[]): Promise<any> {\n return await this.redis.del(keys);\n }\n\n async del(key: string): Promise<number> {\n return await this.redis.del(key);\n }\n\n on(event: string, cb: (...args: any[]) => void) {\n return this.redis.on(event, cb);\n }\n\n once(event: string, cb: (...args: any[]) => void) {\n return this.redis.once(event, cb);\n }\n}\n"],"names":[],"mappings":"iCAGa,aAAa,CAAA;AACd,IAAA,KAAK,CAAQ;AAErB,IAAA,WAAA,CAAY,MAAuB,EAAA;QAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;KACtC;AAED,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,QAAmC,EAAA;QACtD,IAAI,QAAQ,EAAE;YACV,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;SACxC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACvC,QAAA,OAAO,IAAI,CAAC;KACf;AAED,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,OAAyB,EAAA;AAC3D,QAAA,IAAI,OAAO,EAAE,EAAE,EAAE;AACb,YAAA,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;SAC9D;aAAM;YACH,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;SAC3C;KACJ;IAED,MAAM,QAAQ,CAAC,IAAc,EAAA;QACzB,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;KACrC;IAED,MAAM,GAAG,CAAC,GAAW,EAAA;QACjB,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;KACpC;IAED,EAAE,CAAC,KAAa,EAAE,EAA4B,EAAA;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;KACnC;IAED,IAAI,CAAC,KAAa,EAAE,EAA4B,EAAA;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;KACrC;AACJ"}
1
+ {"version":3,"file":"ioredis.js","sources":["../../src/core/ioredis.ts"],"sourcesContent":["import Redis from 'ioredis';\nimport type { IRedisClient } from 'common/server';\n\nexport class IORedisClient implements IRedisClient {\n private redis: Redis;\n\n constructor(config: { url: string }) {\n this.redis = new Redis(config.url);\n }\n\n async get(key: string, callback?: (...args: any[]) => void): Promise<any | null> {\n if (callback) {\n return this.redis.get(key, callback);\n }\n const data = await this.redis.get(key);\n return data;\n }\n\n async set(key: string, value: string, options?: { ex?: number }): Promise<string> {\n if (options?.ex) {\n return await this.redis.set(key, value, 'EX', options?.ex);\n } else {\n return await this.redis.set(key, value);\n }\n }\n\n async delMulti(keys: string[]): Promise<any> {\n return await this.redis.del(keys);\n }\n\n async del(key: string): Promise<number> {\n return await this.redis.del(key);\n }\n\n async keys(pattern: string): Promise<string[]> {\n return await this.redis.keys(pattern);\n }\n\n on(event: string, cb: (...args: any[]) => void) {\n return this.redis.on(event, cb);\n }\n\n once(event: string, cb: (...args: any[]) => void) {\n return this.redis.once(event, cb);\n }\n}\n"],"names":[],"mappings":"iCAGa,aAAa,CAAA;AACd,IAAA,KAAK,CAAQ;AAErB,IAAA,WAAA,CAAY,MAAuB,EAAA;QAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;KACtC;AAED,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,QAAmC,EAAA;QACtD,IAAI,QAAQ,EAAE;YACV,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;SACxC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACvC,QAAA,OAAO,IAAI,CAAC;KACf;AAED,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,OAAyB,EAAA;AAC3D,QAAA,IAAI,OAAO,EAAE,EAAE,EAAE;AACb,YAAA,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;SAC9D;aAAM;YACH,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;SAC3C;KACJ;IAED,MAAM,QAAQ,CAAC,IAAc,EAAA;QACzB,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;KACrC;IAED,MAAM,GAAG,CAAC,GAAW,EAAA;QACjB,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;KACpC;IAED,MAAM,IAAI,CAAC,OAAe,EAAA;QACtB,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;KACzC;IAED,EAAE,CAAC,KAAa,EAAE,EAA4B,EAAA;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;KACnC;IAED,IAAI,CAAC,KAAa,EAAE,EAA4B,EAAA;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;KACrC;AACJ"}
@@ -11,6 +11,7 @@ export declare class UpstashRedisClient implements IRedisClient {
11
11
  }): Promise<string>;
12
12
  delMulti(keys: string[]): Promise<any>;
13
13
  del(key: string): Promise<number>;
14
+ keys(pattern: string): Promise<string[]>;
14
15
  on(event: string, cb: (...args: any[]) => void): this;
15
16
  once(event: string, cb: (...args: any[]) => void): this;
16
17
  }
@@ -26,6 +26,9 @@ import {Redis}from'@upstash/redis/cloudflare';class UpstashRedisClient {
26
26
  async del(key) {
27
27
  return await this.redis.del(key);
28
28
  }
29
+ async keys(pattern) {
30
+ return await this.redis.keys(pattern);
31
+ }
29
32
  on(event, cb) {
30
33
  // Upstash Redis doesn't support event listeners like ioredis
31
34
  // This is a no-op implementation for interface compatibility
@@ -1 +1 @@
1
- {"version":3,"file":"upstash-redis.js","sources":["../../src/core/upstash-redis.ts"],"sourcesContent":["import { Redis } from '@upstash/redis/cloudflare';\nimport type { IRedisClient } from 'common/server';\n\nexport class UpstashRedisClient implements IRedisClient {\n private redis: Redis;\n\n constructor(config: { url: string; token?: string }) {\n this.redis = new Redis({ url: config.url, token: config.token });\n }\n\n async get(key: string, callback?: (...args: any[]) => void): Promise<any | null> {\n if (callback) {\n // Upstash doesn't support callbacks, emulate by calling callback with result\n const result = await this.redis.get(key);\n callback(null, result);\n return result;\n }\n return await this.redis.get(key);\n }\n\n async set(key: string, value: string, options?: { ex?: number }): Promise<string> {\n if (options?.ex) {\n return await this.redis.set(key, value, { ex: options?.ex });\n } else {\n return await this.redis.set(key, value);\n }\n }\n\n async delMulti(keys: string[]): Promise<any> {\n return await Promise.all(keys.map((key) => this.redis.del(key)));\n }\n\n async del(key: string): Promise<number> {\n return await this.redis.del(key);\n }\n\n on(event: string, cb: (...args: any[]) => void) {\n // Upstash Redis doesn't support event listeners like ioredis\n // This is a no-op implementation for interface compatibility\n console.warn('Upstash Redis does not support event listeners');\n return this;\n }\n\n once(event: string, cb: (...args: any[]) => void) {\n // Upstash Redis doesn't support event listeners like ioredis\n // This is a no-op implementation for interface compatibility\n console.warn('Upstash Redis does not support event listeners');\n return this;\n }\n}\n"],"names":[],"mappings":"oDAGa,kBAAkB,CAAA;AACnB,IAAA,KAAK,CAAQ;AAErB,IAAA,WAAA,CAAY,MAAuC,EAAA;QAC/C,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;KACpE;AAED,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,QAAmC,EAAA;QACtD,IAAI,QAAQ,EAAE;;YAEV,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACzC,YAAA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACvB,YAAA,OAAO,MAAM,CAAC;SACjB;QACD,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;KACpC;AAED,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,OAAyB,EAAA;AAC3D,QAAA,IAAI,OAAO,EAAE,EAAE,EAAE;AACb,YAAA,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;SAChE;aAAM;YACH,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;SAC3C;KACJ;IAED,MAAM,QAAQ,CAAC,IAAc,EAAA;QACzB,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;KACpE;IAED,MAAM,GAAG,CAAC,GAAW,EAAA;QACjB,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;KACpC;IAED,EAAE,CAAC,KAAa,EAAE,EAA4B,EAAA;;;AAG1C,QAAA,OAAO,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;AAC/D,QAAA,OAAO,IAAI,CAAC;KACf;IAED,IAAI,CAAC,KAAa,EAAE,EAA4B,EAAA;;;AAG5C,QAAA,OAAO,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;AAC/D,QAAA,OAAO,IAAI,CAAC;KACf;AACJ"}
1
+ {"version":3,"file":"upstash-redis.js","sources":["../../src/core/upstash-redis.ts"],"sourcesContent":["import { Redis } from '@upstash/redis/cloudflare';\nimport type { IRedisClient } from 'common/server';\n\nexport class UpstashRedisClient implements IRedisClient {\n private redis: Redis;\n\n constructor(config: { url: string; token?: string }) {\n this.redis = new Redis({ url: config.url, token: config.token });\n }\n\n async get(key: string, callback?: (...args: any[]) => void): Promise<any | null> {\n if (callback) {\n // Upstash doesn't support callbacks, emulate by calling callback with result\n const result = await this.redis.get(key);\n callback(null, result);\n return result;\n }\n return await this.redis.get(key);\n }\n\n async set(key: string, value: string, options?: { ex?: number }): Promise<string> {\n if (options?.ex) {\n return await this.redis.set(key, value, { ex: options?.ex });\n } else {\n return await this.redis.set(key, value);\n }\n }\n\n async delMulti(keys: string[]): Promise<any> {\n return await Promise.all(keys.map((key) => this.redis.del(key)));\n }\n\n async del(key: string): Promise<number> {\n return await this.redis.del(key);\n }\n\n async keys(pattern: string): Promise<string[]> {\n return await this.redis.keys(pattern);\n }\n\n on(event: string, cb: (...args: any[]) => void) {\n // Upstash Redis doesn't support event listeners like ioredis\n // This is a no-op implementation for interface compatibility\n console.warn('Upstash Redis does not support event listeners');\n return this;\n }\n\n once(event: string, cb: (...args: any[]) => void) {\n // Upstash Redis doesn't support event listeners like ioredis\n // This is a no-op implementation for interface compatibility\n console.warn('Upstash Redis does not support event listeners');\n return this;\n }\n}\n"],"names":[],"mappings":"oDAGa,kBAAkB,CAAA;AACnB,IAAA,KAAK,CAAQ;AAErB,IAAA,WAAA,CAAY,MAAuC,EAAA;QAC/C,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;KACpE;AAED,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,QAAmC,EAAA;QACtD,IAAI,QAAQ,EAAE;;YAEV,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACzC,YAAA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACvB,YAAA,OAAO,MAAM,CAAC;SACjB;QACD,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;KACpC;AAED,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,OAAyB,EAAA;AAC3D,QAAA,IAAI,OAAO,EAAE,EAAE,EAAE;AACb,YAAA,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;SAChE;aAAM;YACH,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;SAC3C;KACJ;IAED,MAAM,QAAQ,CAAC,IAAc,EAAA;QACzB,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;KACpE;IAED,MAAM,GAAG,CAAC,GAAW,EAAA;QACjB,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;KACpC;IAED,MAAM,IAAI,CAAC,OAAe,EAAA;QACtB,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;KACzC;IAED,EAAE,CAAC,KAAa,EAAE,EAA4B,EAAA;;;AAG1C,QAAA,OAAO,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;AAC/D,QAAA,OAAO,IAAI,CAAC;KACf;IAED,IAAI,CAAC,KAAa,EAAE,EAA4B,EAAA;;;AAG5C,QAAA,OAAO,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;AAC/D,QAAA,OAAO,IAAI,CAAC;KACf;AACJ"}
package/lib/module.d.ts CHANGED
@@ -1,6 +1,7 @@
1
+ import { Feature } from '@common-stack/server-core';
1
2
  import type { interfaces } from 'inversify';
2
3
  export declare const redisServiceGen: (container: interfaces.Container) => {
3
4
  redisCacheManager: IRedisCacheManager;
4
5
  };
5
- declare const _default: any;
6
+ declare const _default: Feature<any, any>;
6
7
  export default _default;
@@ -62,7 +62,9 @@ export declare class RedisCacheManager implements IRedisCacheManager {
62
62
  /**
63
63
  * Generate cache key for a GraphQL query
64
64
  *
65
- * Format: APP_NAME:tenantId:userId:queryName:queryHash:variablesHash
65
+ * Format:
66
+ * - With userId (PRIVATE): APP_NAME:tenantId:userId:queryName:queryHash:variablesHash
67
+ * - Without userId (PUBLIC): APP_NAME:tenantId:queryName:queryHash:variablesHash
66
68
  */
67
69
  private getCacheKey;
68
70
  /**
@@ -35,22 +35,30 @@ let RedisCacheManager = RedisCacheManager_1 = class RedisCacheManager {
35
35
  * @param shouldRemoveAll - If true, removes all related cache keys
36
36
  */
37
37
  async del(query, variables, ctx, shouldRemoveAll = false) {
38
- const cacheKey = this.getCacheKey(query, variables ?? {}, ctx);
39
- // If variables provided, delete exact match
38
+ // Clear both PUBLIC and PRIVATE versions of the cache
39
+ const privateKey = this.getCacheKey(query, variables ?? {}, ctx, { maxAge: 1, scope: 'PRIVATE' });
40
+ const publicKey = this.getCacheKey(query, variables ?? {}, ctx, { maxAge: 1, scope: 'PUBLIC' });
41
+ // If variables provided, delete exact match (both versions)
40
42
  if (variables && Object.keys(variables).length > 0) {
41
- this.log('debug', `Deleting ${cacheKey} from redis`);
42
- await this.redisClient.del(cacheKey);
43
+ this.log('debug', `Deleting ${privateKey} and ${publicKey} from redis`);
44
+ await this.redisClient.del(privateKey, publicKey);
43
45
  return;
44
46
  }
45
- // Build wildcard pattern
46
- const keysWithWildCard = shouldRemoveAll
47
+ // Build wildcard patterns for both PUBLIC and PRIVATE
48
+ const privatePattern = shouldRemoveAll
47
49
  ? this.getWildCardQueryKey(query, ctx)
48
- : `${cacheKey.substring(0, cacheKey.lastIndexOf(':'))}:*`;
49
- const cacheKeys = await this.redisClient.keys(keysWithWildCard);
50
- this.log('debug', `Found ${cacheKeys.length} keys against pattern ${keysWithWildCard}`);
51
- if (cacheKeys.length) {
52
- this.log('debug', `Deleting ${cacheKeys.length} keys from redis`);
53
- await this.redisClient.del(...cacheKeys);
50
+ : `${privateKey.substring(0, privateKey.lastIndexOf(':'))}:*`;
51
+ const publicPattern = shouldRemoveAll
52
+ ? this.getWildCardQueryKey(query, ctx)
53
+ : `${publicKey.substring(0, publicKey.lastIndexOf(':'))}:*`;
54
+ // Get keys matching both patterns
55
+ const privateCacheKeys = await this.redisClient.keys(privatePattern);
56
+ const publicCacheKeys = await this.redisClient.keys(publicPattern);
57
+ const allKeys = [...new Set([...privateCacheKeys, ...publicCacheKeys])]; // Deduplicate
58
+ this.log('debug', `Found ${allKeys.length} keys against patterns ${privatePattern} and ${publicPattern}`);
59
+ if (allKeys.length) {
60
+ this.log('debug', `Deleting ${allKeys.length} keys from redis`);
61
+ await this.redisClient.del(...allKeys);
54
62
  }
55
63
  }
56
64
  /**
@@ -62,8 +70,14 @@ let RedisCacheManager = RedisCacheManager_1 = class RedisCacheManager {
62
70
  * @returns Cached data or null if cache miss
63
71
  */
64
72
  async get(query, variables, ctx) {
65
- const cacheKey = this.getCacheKey(query, variables, ctx);
66
- const cacheResponse = await this.redisClient.get(cacheKey);
73
+ // Try PRIVATE key first (with userId)
74
+ let cacheKey = this.getCacheKey(query, variables, ctx, { maxAge: 1, scope: 'PRIVATE' });
75
+ let cacheResponse = await this.redisClient.get(cacheKey);
76
+ // If not found, try PUBLIC key (without userId)
77
+ if (!cacheResponse) {
78
+ cacheKey = this.getCacheKey(query, variables, ctx, { maxAge: 1, scope: 'PUBLIC' });
79
+ cacheResponse = await this.redisClient.get(cacheKey);
80
+ }
67
81
  if (cacheResponse) {
68
82
  try {
69
83
  const { data } = JSON.parse(JSON.parse(cacheResponse)?.value) ?? {};
@@ -89,7 +103,7 @@ let RedisCacheManager = RedisCacheManager_1 = class RedisCacheManager {
89
103
  * @param cachePolicy - Cache policy (TTL, scope)
90
104
  */
91
105
  async set(query, variables, data, ctx, cachePolicy = { maxAge: 86400, scope: 'PUBLIC' }) {
92
- const cacheKey = this.getCacheKey(query, variables, ctx);
106
+ const cacheKey = this.getCacheKey(query, variables, ctx, cachePolicy);
93
107
  const cacheTime = Date.now();
94
108
  // Ensure maxAge is not negative or zero
95
109
  const maxAge = Math.max(1, cachePolicy.maxAge);
@@ -129,16 +143,11 @@ let RedisCacheManager = RedisCacheManager_1 = class RedisCacheManager {
129
143
  /**
130
144
  * Generate cache key for a GraphQL query
131
145
  *
132
- * Format: APP_NAME:tenantId:userId:queryName:queryHash:variablesHash
146
+ * Format:
147
+ * - With userId (PRIVATE): APP_NAME:tenantId:userId:queryName:queryHash:variablesHash
148
+ * - Without userId (PUBLIC): APP_NAME:tenantId:queryName:queryHash:variablesHash
133
149
  */
134
- getCacheKey(query, variables, ctx) {
135
- // Generate the legacy key WITHOUT tenantId/userId since we'll add them via manual construction
136
- const legacyKey = generateQueryCacheKey({
137
- query,
138
- variables,
139
- logger: this.logger,
140
- });
141
- // Build key without namespace - format: APP_NAME:tenantId:userId:legacyKey
150
+ getCacheKey(query, variables, ctx, cachePolicy) {
142
151
  const appName = this.getAppName();
143
152
  // Validate tenantId - if it looks like a hash (all hex, 24+ chars), use 'default'
144
153
  let tenantId = ctx?.tenantId || 'default';
@@ -146,12 +155,20 @@ let RedisCacheManager = RedisCacheManager_1 = class RedisCacheManager {
146
155
  this.log('warn', `TenantId appears to be a hash (${tenantId.substring(0, 16)}...), using "default" instead`);
147
156
  tenantId = 'default';
148
157
  }
149
- const userId = ctx?.userId || 'anonymous';
150
- // Sanitize components (handles Auth0 userIds like "auth0|123")
151
- const sanitizedAppName = sanitizeRedisKeyComponent(appName);
152
- const sanitizedTenantId = sanitizeRedisKeyComponent(tenantId);
153
- const sanitizedUserId = sanitizeRedisKeyComponent(userId);
154
- return `${sanitizedAppName}:${sanitizedTenantId}:${sanitizedUserId}:${legacyKey}`;
158
+ // For PUBLIC scope, omit userId to share cache across all users
159
+ // For PRIVATE scope or when not specified, include userId (or 'anonymous')
160
+ const isPublicScope = cachePolicy?.scope === 'PUBLIC';
161
+ const userId = isPublicScope ? undefined : ctx?.userId || 'anonymous';
162
+ // Use generateQueryCacheKey with all parameters to avoid duplication
163
+ // This will properly sanitize userId (auth0|123 -> auth0-123) and build the complete key
164
+ return generateQueryCacheKey({
165
+ query,
166
+ variables,
167
+ appName,
168
+ tenantId,
169
+ userId,
170
+ logger: this.logger,
171
+ });
155
172
  }
156
173
  /**
157
174
  * Clear all cache entries for a tenant
@@ -191,9 +208,7 @@ let RedisCacheManager = RedisCacheManager_1 = class RedisCacheManager {
191
208
  */
192
209
  async getStats(tenantId) {
193
210
  const appName = this.getAppName();
194
- const pattern = tenantId
195
- ? `${appName}:${sanitizeRedisKeyComponent(tenantId)}:*`
196
- : `${appName}:*`;
211
+ const pattern = tenantId ? `${appName}:${sanitizeRedisKeyComponent(tenantId)}:*` : `${appName}:*`;
197
212
  const keys = await this.redisClient.keys(pattern);
198
213
  const totalKeys = keys.length;
199
214
  // Get memory usage for the Redis instance
@@ -1 +1 @@
1
- {"version":3,"file":"RedisCacheManager.js","sources":["../../src/services/RedisCacheManager.ts"],"sourcesContent":["/**\n * @file RedisCacheManager.ts\n * @description Redis-based cache manager for GraphQL query caching\n *\n * This implementation provides sophisticated caching for GraphQL operations with:\n * - Automatic query hashing for cache keys\n * - Multi-tenant and user isolation\n * - Wildcard-based cache invalidation\n * - TTL-based expiration\n * - Automatic key sanitization (handles Auth0 userIds with pipes)\n *\n * Migrated from @adminide-stack/platform-server to be shared across applications\n */\n\nimport { generateQueryCacheKey, sanitizeRedisKeyComponent, isHashLikeTenantId } from '../core/keyBuilder';\nimport type { Redis } from 'ioredis';\nimport { print } from 'graphql';\nimport type { DocumentNode } from 'graphql';\nimport { injectable, inject } from 'inversify';\nimport { type ICacheContext, type ICachePolicy, type IRedisCacheManager, SERVER_TYPES } from 'common/server';\nimport { CommonType } from '@common-stack/core';\n\n/**\n * Redis Cache Manager implementation\n *\n * Provides GraphQL query caching with automatic key generation and invalidation\n */\n@injectable()\nexport class RedisCacheManager implements IRedisCacheManager {\n protected logger?: any;\n\n constructor(\n @inject(SERVER_TYPES.RedisClient)\n protected readonly redisClient: Redis,\n @inject(CommonType.LOGGER)\n logger?: any,\n ) {\n if (logger) {\n this.logger = logger.child ? logger.child({ className: RedisCacheManager.name }) : logger;\n }\n }\n\n /**\n * Delete cached data for a GraphQL query\n *\n * @param query - GraphQL query to invalidate\n * @param variables - Optional variables (if omitted, clears all variants)\n * @param ctx - Cache context for tenant/user isolation\n * @param shouldRemoveAll - If true, removes all related cache keys\n */\n async del(\n query: string | DocumentNode,\n variables?: Record<string, unknown>,\n ctx?: ICacheContext,\n shouldRemoveAll = false,\n ): Promise<void> {\n const cacheKey = this.getCacheKey(query, variables ?? {}, ctx);\n\n // If variables provided, delete exact match\n if (variables && Object.keys(variables).length > 0) {\n this.log('debug', `Deleting ${cacheKey} from redis`);\n await this.redisClient.del(cacheKey);\n return;\n }\n\n // Build wildcard pattern\n const keysWithWildCard = shouldRemoveAll\n ? this.getWildCardQueryKey(query, ctx)\n : `${cacheKey.substring(0, cacheKey.lastIndexOf(':'))}:*`;\n\n const cacheKeys = await this.redisClient.keys(keysWithWildCard);\n this.log('debug', `Found ${cacheKeys.length} keys against pattern ${keysWithWildCard}`);\n\n if (cacheKeys.length) {\n this.log('debug', `Deleting ${cacheKeys.length} keys from redis`);\n await this.redisClient.del(...cacheKeys);\n }\n }\n\n /**\n * Get cached data for a GraphQL query\n *\n * @param query - GraphQL query\n * @param variables - Query variables\n * @param ctx - Cache context\n * @returns Cached data or null if cache miss\n */\n async get<T>(\n query: string | DocumentNode,\n variables: Record<string, unknown>,\n ctx: ICacheContext,\n ): Promise<T | null> {\n const cacheKey = this.getCacheKey(query, variables, ctx);\n const cacheResponse = await this.redisClient.get(cacheKey);\n\n if (cacheResponse) {\n try {\n const { data } = JSON.parse(JSON.parse(cacheResponse)?.value) ?? {};\n const queryName = this.getQueryName(query);\n this.log('debug', `Found cache for ${cacheKey}`);\n return data?.[queryName] ?? null;\n } catch (error) {\n this.log('warn', `Failed to parse cache data for ${cacheKey}:`, error);\n return null;\n }\n }\n\n this.log('debug', `No cache found for key ${cacheKey}`);\n return null;\n }\n\n /**\n * Set cached data for a GraphQL query\n *\n * @param query - GraphQL query\n * @param variables - Query variables\n * @param data - Data to cache\n * @param ctx - Cache context\n * @param cachePolicy - Cache policy (TTL, scope)\n */\n async set<T>(\n query: string | DocumentNode,\n variables: Record<string, unknown>,\n data: T,\n ctx: ICacheContext,\n cachePolicy: ICachePolicy = { maxAge: 86400, scope: 'PUBLIC' },\n ): Promise<void> {\n const cacheKey = this.getCacheKey(query, variables, ctx);\n const cacheTime = Date.now();\n\n // Ensure maxAge is not negative or zero\n const maxAge = Math.max(1, cachePolicy.maxAge);\n\n if (cachePolicy.maxAge <= 0) {\n this.log(\n 'warn',\n `Invalid maxAge (${cachePolicy.maxAge}) for cache key ${cacheKey}, using minimum value of 1 second`,\n );\n }\n\n this.log('debug', `Set cache for key ${cacheKey} with maxAge ${maxAge}`);\n\n await this.redisClient.set(\n cacheKey,\n JSON.stringify({\n value: JSON.stringify({\n data: { [this.getQueryName(query)]: data },\n cachePolicy: { ...cachePolicy, maxAge },\n cacheTime,\n }),\n expires: cacheTime + maxAge * 1000,\n }).trim(),\n 'EX',\n maxAge,\n );\n }\n\n /**\n * Extract query name from GraphQL query\n */\n private getQueryName(query: string | DocumentNode): string {\n const queryStr = typeof query === 'string' ? query : print(query);\n const [, queryName] = queryStr?.match(/{\\s*(\\w+)/) ?? [];\n return queryName;\n }\n\n /**\n * Build wildcard pattern for query invalidation\n */\n private getWildCardQueryKey(query: string | DocumentNode, ctx?: ICacheContext): string {\n const queryStr = typeof query === 'string' ? query : print(query);\n const [, queryName] = queryStr?.match(/{\\s*(\\w+)/) ?? [];\n const { tenantId } = ctx || {};\n\n // Build pattern without namespace - just APP_NAME:tenantId:segments\n const appName = this.getAppName();\n const sanitizedTenantId = tenantId || 'default';\n return `${appName}:${sanitizedTenantId}:*:${queryName || '*'}:*`;\n }\n\n /**\n * Generate cache key for a GraphQL query\n *\n * Format: APP_NAME:tenantId:userId:queryName:queryHash:variablesHash\n */\n private getCacheKey(query: string | DocumentNode, variables: Record<string, unknown>, ctx?: ICacheContext): string {\n // Generate the legacy key WITHOUT tenantId/userId since we'll add them via manual construction\n const legacyKey = generateQueryCacheKey({\n query,\n variables,\n logger: this.logger,\n });\n\n // Build key without namespace - format: APP_NAME:tenantId:userId:legacyKey\n const appName = this.getAppName();\n\n // Validate tenantId - if it looks like a hash (all hex, 24+ chars), use 'default'\n let tenantId = ctx?.tenantId || 'default';\n if (isHashLikeTenantId(tenantId)) {\n this.log(\n 'warn',\n `TenantId appears to be a hash (${tenantId.substring(0, 16)}...), using \"default\" instead`,\n );\n tenantId = 'default';\n }\n\n const userId = ctx?.userId || 'anonymous';\n\n // Sanitize components (handles Auth0 userIds like \"auth0|123\")\n const sanitizedAppName = sanitizeRedisKeyComponent(appName);\n const sanitizedTenantId = sanitizeRedisKeyComponent(tenantId);\n const sanitizedUserId = sanitizeRedisKeyComponent(userId);\n\n return `${sanitizedAppName}:${sanitizedTenantId}:${sanitizedUserId}:${legacyKey}`;\n }\n\n /**\n * Clear all cache entries for a tenant\n */\n async clearTenant(tenantId: string): Promise<number> {\n const appName = this.getAppName();\n const sanitizedTenantId = sanitizeRedisKeyComponent(tenantId);\n const pattern = `${appName}:${sanitizedTenantId}:*`;\n \n return await this.clearPattern(pattern);\n }\n\n /**\n * Clear all cache entries for a user\n */\n async clearUser(userId: string, tenantId?: string): Promise<number> {\n const appName = this.getAppName();\n const sanitizedUserId = sanitizeRedisKeyComponent(userId);\n const sanitizedTenantId = tenantId ? sanitizeRedisKeyComponent(tenantId) : '*';\n const pattern = `${appName}:${sanitizedTenantId}:${sanitizedUserId}:*`;\n \n return await this.clearPattern(pattern);\n }\n\n /**\n * Clear all cache entries matching a pattern\n */\n async clearPattern(pattern: string): Promise<number> {\n this.log('debug', `Clearing cache entries matching pattern: ${pattern}`);\n \n const keys = await this.redisClient.keys(pattern);\n \n if (keys.length === 0) {\n this.log('debug', `No keys found matching pattern: ${pattern}`);\n return 0;\n }\n\n this.log('debug', `Deleting ${keys.length} keys matching pattern: ${pattern}`);\n await this.redisClient.del(...keys);\n \n return keys.length;\n }\n\n /**\n * Get cache statistics\n */\n async getStats(tenantId?: string): Promise<{\n totalKeys: number;\n memoryUsage: number;\n hitRate?: number;\n }> {\n const appName = this.getAppName();\n const pattern = tenantId \n ? `${appName}:${sanitizeRedisKeyComponent(tenantId)}:*`\n : `${appName}:*`;\n\n const keys = await this.redisClient.keys(pattern);\n const totalKeys = keys.length;\n\n // Get memory usage for the Redis instance\n let memoryUsage = 0;\n try {\n const info = await this.redisClient.info('memory');\n const match = info.match(/used_memory:(\\d+)/);\n if (match) {\n memoryUsage = parseInt(match[1], 10);\n }\n } catch (error) {\n this.log('warn', 'Failed to get memory info from Redis:', error);\n }\n\n return {\n totalKeys,\n memoryUsage,\n hitRate: undefined, // Hit rate tracking would require additional instrumentation\n };\n }\n\n /**\n * Get application name for key prefix\n * Override this method to provide custom app name\n */\n protected getAppName(): string {\n return process.env.APP_NAME || 'COMMON_STACK';\n }\n\n /**\n * Log helper\n */\n private log(level: string, message: string, ...args: any[]): void {\n if (this.logger && typeof this.logger[level] === 'function') {\n this.logger[level](message, ...args);\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RedisCacheManager.js","sources":["../../src/services/RedisCacheManager.ts"],"sourcesContent":["/**\n * @file RedisCacheManager.ts\n * @description Redis-based cache manager for GraphQL query caching\n *\n * This implementation provides sophisticated caching for GraphQL operations with:\n * - Automatic query hashing for cache keys\n * - Multi-tenant and user isolation\n * - Wildcard-based cache invalidation\n * - TTL-based expiration\n * - Automatic key sanitization (handles Auth0 userIds with pipes)\n *\n * Migrated from @adminide-stack/platform-server to be shared across applications\n */\n\nimport { generateQueryCacheKey, sanitizeRedisKeyComponent, isHashLikeTenantId } from '../core/keyBuilder';\nimport type { Redis } from 'ioredis';\nimport { print } from 'graphql';\nimport type { DocumentNode } from 'graphql';\nimport { injectable, inject } from 'inversify';\nimport { type ICacheContext, type ICachePolicy, type IRedisCacheManager, SERVER_TYPES } from 'common/server';\nimport { CommonType } from '@common-stack/core';\n\n/**\n * Redis Cache Manager implementation\n *\n * Provides GraphQL query caching with automatic key generation and invalidation\n */\n@injectable()\nexport class RedisCacheManager implements IRedisCacheManager {\n protected logger?: any;\n\n constructor(\n @inject(SERVER_TYPES.RedisClient)\n protected readonly redisClient: Redis,\n @inject(CommonType.LOGGER)\n logger?: any,\n ) {\n if (logger) {\n this.logger = logger.child ? logger.child({ className: RedisCacheManager.name }) : logger;\n }\n }\n\n /**\n * Delete cached data for a GraphQL query\n *\n * @param query - GraphQL query to invalidate\n * @param variables - Optional variables (if omitted, clears all variants)\n * @param ctx - Cache context for tenant/user isolation\n * @param shouldRemoveAll - If true, removes all related cache keys\n */\n async del(\n query: string | DocumentNode,\n variables?: Record<string, unknown>,\n ctx?: ICacheContext,\n shouldRemoveAll = false,\n ): Promise<void> {\n // Clear both PUBLIC and PRIVATE versions of the cache\n const privateKey = this.getCacheKey(query, variables ?? {}, ctx, { maxAge: 1, scope: 'PRIVATE' });\n const publicKey = this.getCacheKey(query, variables ?? {}, ctx, { maxAge: 1, scope: 'PUBLIC' });\n\n // If variables provided, delete exact match (both versions)\n if (variables && Object.keys(variables).length > 0) {\n this.log('debug', `Deleting ${privateKey} and ${publicKey} from redis`);\n await this.redisClient.del(privateKey, publicKey);\n return;\n }\n\n // Build wildcard patterns for both PUBLIC and PRIVATE\n const privatePattern = shouldRemoveAll\n ? this.getWildCardQueryKey(query, ctx)\n : `${privateKey.substring(0, privateKey.lastIndexOf(':'))}:*`;\n\n const publicPattern = shouldRemoveAll\n ? this.getWildCardQueryKey(query, ctx)\n : `${publicKey.substring(0, publicKey.lastIndexOf(':'))}:*`;\n\n // Get keys matching both patterns\n const privateCacheKeys = await this.redisClient.keys(privatePattern);\n const publicCacheKeys = await this.redisClient.keys(publicPattern);\n const allKeys = [...new Set([...privateCacheKeys, ...publicCacheKeys])]; // Deduplicate\n\n this.log('debug', `Found ${allKeys.length} keys against patterns ${privatePattern} and ${publicPattern}`);\n\n if (allKeys.length) {\n this.log('debug', `Deleting ${allKeys.length} keys from redis`);\n await this.redisClient.del(...allKeys);\n }\n }\n\n /**\n * Get cached data for a GraphQL query\n *\n * @param query - GraphQL query\n * @param variables - Query variables\n * @param ctx - Cache context\n * @returns Cached data or null if cache miss\n */\n async get<T>(\n query: string | DocumentNode,\n variables: Record<string, unknown>,\n ctx: ICacheContext,\n ): Promise<T | null> {\n // Try PRIVATE key first (with userId)\n let cacheKey = this.getCacheKey(query, variables, ctx, { maxAge: 1, scope: 'PRIVATE' });\n let cacheResponse = await this.redisClient.get(cacheKey);\n\n // If not found, try PUBLIC key (without userId)\n if (!cacheResponse) {\n cacheKey = this.getCacheKey(query, variables, ctx, { maxAge: 1, scope: 'PUBLIC' });\n cacheResponse = await this.redisClient.get(cacheKey);\n }\n\n if (cacheResponse) {\n try {\n const { data } = JSON.parse(JSON.parse(cacheResponse)?.value) ?? {};\n const queryName = this.getQueryName(query);\n this.log('debug', `Found cache for ${cacheKey}`);\n return data?.[queryName] ?? null;\n } catch (error) {\n this.log('warn', `Failed to parse cache data for ${cacheKey}:`, error);\n return null;\n }\n }\n\n this.log('debug', `No cache found for key ${cacheKey}`);\n return null;\n }\n\n /**\n * Set cached data for a GraphQL query\n *\n * @param query - GraphQL query\n * @param variables - Query variables\n * @param data - Data to cache\n * @param ctx - Cache context\n * @param cachePolicy - Cache policy (TTL, scope)\n */\n async set<T>(\n query: string | DocumentNode,\n variables: Record<string, unknown>,\n data: T,\n ctx: ICacheContext,\n cachePolicy: ICachePolicy = { maxAge: 86400, scope: 'PUBLIC' },\n ): Promise<void> {\n const cacheKey = this.getCacheKey(query, variables, ctx, cachePolicy);\n const cacheTime = Date.now();\n\n // Ensure maxAge is not negative or zero\n const maxAge = Math.max(1, cachePolicy.maxAge);\n\n if (cachePolicy.maxAge <= 0) {\n this.log(\n 'warn',\n `Invalid maxAge (${cachePolicy.maxAge}) for cache key ${cacheKey}, using minimum value of 1 second`,\n );\n }\n\n this.log('debug', `Set cache for key ${cacheKey} with maxAge ${maxAge}`);\n\n await this.redisClient.set(\n cacheKey,\n JSON.stringify({\n value: JSON.stringify({\n data: { [this.getQueryName(query)]: data },\n cachePolicy: { ...cachePolicy, maxAge },\n cacheTime,\n }),\n expires: cacheTime + maxAge * 1000,\n }).trim(),\n 'EX',\n maxAge,\n );\n }\n\n /**\n * Extract query name from GraphQL query\n */\n private getQueryName(query: string | DocumentNode): string {\n const queryStr = typeof query === 'string' ? query : print(query);\n const [, queryName] = queryStr?.match(/{\\s*(\\w+)/) ?? [];\n return queryName;\n }\n\n /**\n * Build wildcard pattern for query invalidation\n */\n private getWildCardQueryKey(query: string | DocumentNode, ctx?: ICacheContext): string {\n const queryStr = typeof query === 'string' ? query : print(query);\n const [, queryName] = queryStr?.match(/{\\s*(\\w+)/) ?? [];\n const { tenantId } = ctx || {};\n\n // Build pattern without namespace - just APP_NAME:tenantId:segments\n const appName = this.getAppName();\n const sanitizedTenantId = tenantId || 'default';\n return `${appName}:${sanitizedTenantId}:*:${queryName || '*'}:*`;\n }\n\n /**\n * Generate cache key for a GraphQL query\n *\n * Format:\n * - With userId (PRIVATE): APP_NAME:tenantId:userId:queryName:queryHash:variablesHash\n * - Without userId (PUBLIC): APP_NAME:tenantId:queryName:queryHash:variablesHash\n */\n private getCacheKey(\n query: string | DocumentNode,\n variables: Record<string, unknown>,\n ctx?: ICacheContext,\n cachePolicy?: ICachePolicy,\n ): string {\n const appName = this.getAppName();\n\n // Validate tenantId - if it looks like a hash (all hex, 24+ chars), use 'default'\n let tenantId = ctx?.tenantId || 'default';\n if (isHashLikeTenantId(tenantId)) {\n this.log(\n 'warn',\n `TenantId appears to be a hash (${tenantId.substring(0, 16)}...), using \"default\" instead`,\n );\n tenantId = 'default';\n }\n\n // For PUBLIC scope, omit userId to share cache across all users\n // For PRIVATE scope or when not specified, include userId (or 'anonymous')\n const isPublicScope = cachePolicy?.scope === 'PUBLIC';\n const userId = isPublicScope ? undefined : ctx?.userId || 'anonymous';\n\n // Use generateQueryCacheKey with all parameters to avoid duplication\n // This will properly sanitize userId (auth0|123 -> auth0-123) and build the complete key\n return generateQueryCacheKey({\n query,\n variables,\n appName,\n tenantId,\n userId,\n logger: this.logger,\n });\n }\n\n /**\n * Clear all cache entries for a tenant\n */\n async clearTenant(tenantId: string): Promise<number> {\n const appName = this.getAppName();\n const sanitizedTenantId = sanitizeRedisKeyComponent(tenantId);\n const pattern = `${appName}:${sanitizedTenantId}:*`;\n\n return await this.clearPattern(pattern);\n }\n\n /**\n * Clear all cache entries for a user\n */\n async clearUser(userId: string, tenantId?: string): Promise<number> {\n const appName = this.getAppName();\n const sanitizedUserId = sanitizeRedisKeyComponent(userId);\n const sanitizedTenantId = tenantId ? sanitizeRedisKeyComponent(tenantId) : '*';\n const pattern = `${appName}:${sanitizedTenantId}:${sanitizedUserId}:*`;\n\n return await this.clearPattern(pattern);\n }\n\n /**\n * Clear all cache entries matching a pattern\n */\n async clearPattern(pattern: string): Promise<number> {\n this.log('debug', `Clearing cache entries matching pattern: ${pattern}`);\n\n const keys = await this.redisClient.keys(pattern);\n\n if (keys.length === 0) {\n this.log('debug', `No keys found matching pattern: ${pattern}`);\n return 0;\n }\n\n this.log('debug', `Deleting ${keys.length} keys matching pattern: ${pattern}`);\n await this.redisClient.del(...keys);\n\n return keys.length;\n }\n\n /**\n * Get cache statistics\n */\n async getStats(tenantId?: string): Promise<{\n totalKeys: number;\n memoryUsage: number;\n hitRate?: number;\n }> {\n const appName = this.getAppName();\n const pattern = tenantId ? `${appName}:${sanitizeRedisKeyComponent(tenantId)}:*` : `${appName}:*`;\n\n const keys = await this.redisClient.keys(pattern);\n const totalKeys = keys.length;\n\n // Get memory usage for the Redis instance\n let memoryUsage = 0;\n try {\n const info = await this.redisClient.info('memory');\n const match = info.match(/used_memory:(\\d+)/);\n if (match) {\n memoryUsage = parseInt(match[1], 10);\n }\n } catch (error) {\n this.log('warn', 'Failed to get memory info from Redis:', error);\n }\n\n return {\n totalKeys,\n memoryUsage,\n hitRate: undefined, // Hit rate tracking would require additional instrumentation\n };\n }\n\n /**\n * Get application name for key prefix\n * Override this method to provide custom app name\n */\n protected getAppName(): string {\n return process.env.APP_NAME || 'COMMON_STACK';\n }\n\n /**\n * Log helper\n */\n private log(level: string, message: string, ...args: any[]): void {\n if (this.logger && typeof this.logger[level] === 'function') {\n this.logger[level](message, ...args);\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ export interface IRedisClient {
4
4
  set(key: string, value: string, options?: { ex?: number }): Promise<string>;
5
5
  del(key: string): Promise<number>;
6
6
  delMulti(keys: string[]): Promise<any>;
7
+ keys(pattern: string): Promise<string[]>;
7
8
  on?(event: string | symbol, callback: (...args: any[]) => void): any;
8
9
  once?(event: string | symbol, callback: (...args: any[]) => void): any;
9
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@common-stack/store-redis",
3
- "version": "8.3.1-alpha.4",
3
+ "version": "8.3.1-alpha.6",
4
4
  "description": "Redis store utilities and services for common-stack",
5
5
  "license": "UNLICENSED",
6
6
  "author": "CDMBase LLC",
@@ -53,7 +53,7 @@
53
53
  ]
54
54
  }
55
55
  },
56
- "gitHead": "b55d59ba8bba1ed087d7edac66fc122693ca777d",
56
+ "gitHead": "fb84350cf08d0ecab5689c99c8cc03e151fd3ace",
57
57
  "typescript": {
58
58
  "definition": "lib/index.d.ts"
59
59
  }