@common-stack/store-redis 8.2.5-alpha.35 → 8.2.5-alpha.36

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.
@@ -1,9 +1,3 @@
1
- import { ContainerModule } from 'inversify';
2
- import { SERVER_TYPES } from 'common/server';
3
- import { RedisCacheManager } from '../services/RedisCacheManager.js';
4
-
5
- const infraContainer = (settings) => new ContainerModule((bind) => {
1
+ import {ContainerModule}from'inversify';import {SERVER_TYPES}from'common/server';import {RedisCacheManager}from'../services/RedisCacheManager.js';const infraContainer = (settings) => new ContainerModule((bind) => {
6
2
  bind(SERVER_TYPES.RedisCacheManager).to(RedisCacheManager).inSingletonScope();
7
- });
8
-
9
- export { infraContainer };
3
+ });export{infraContainer};//# sourceMappingURL=container.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container.js","sources":["../../src/containers/container.ts"],"sourcesContent":["import { ContainerModule, interfaces } from 'inversify';\nimport { SERVER_TYPES, IRedisCacheManager } from 'common/server';\nimport { RedisCacheManager } from '../services/RedisCacheManager';\n\nexport const infraContainer: (settings: any) => interfaces.ContainerModule = (settings: any) =>\n new ContainerModule((bind: interfaces.Bind) => {\n bind<IRedisCacheManager>(SERVER_TYPES.RedisCacheManager).to(RedisCacheManager).inSingletonScope();\n });\n"],"names":[],"mappings":"kJAIO,MAAM,cAAc,GAAkD,CAAC,QAAa,KACvF,IAAI,eAAe,CAAC,CAAC,IAAqB,KAAI;AAC1C,IAAA,IAAI,CAAqB,YAAY,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,gBAAgB,EAAE,CAAC;AACtG,CAAC"}
@@ -1,6 +1,4 @@
1
- import Redis from 'ioredis';
2
-
3
- class IORedisClient {
1
+ import Redis from'ioredis';class IORedisClient {
4
2
  redis;
5
3
  constructor(config) {
6
4
  this.redis = new Redis(config.url);
@@ -26,6 +24,4 @@ class IORedisClient {
26
24
  on(event, cb) {
27
25
  return this.redis.on(event, cb);
28
26
  }
29
- }
30
-
31
- export { IORedisClient };
27
+ }export{IORedisClient};//# sourceMappingURL=ioredis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ioredis.js","sources":["../../src/core/ioredis.ts"],"sourcesContent":["import Redis from 'ioredis';\nimport { 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): Promise<any | null> {\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"],"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;IAED,MAAM,GAAG,CAAC,GAAW,EAAA;QACjB,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;AACJ"}
@@ -1,8 +1,4 @@
1
- import { createHash } from 'crypto';
2
- import { print } from 'graphql/language/index.js';
3
- import { buildRedisKey } from './sanitize-redis-key.js';
4
-
5
- /**
1
+ import {createHash}from'crypto';import {print}from'graphql/language/index.js';import {buildRedisKey}from'./sanitize-redis-key.js';/**
6
2
  * @file generate-query-cache-key.ts
7
3
  * @description Generates consistent cache keys for GraphQL queries
8
4
  *
@@ -83,6 +79,4 @@ const generateQueryCacheKey = ({ query, variables, logger, userId, tenantId, app
83
79
  });
84
80
  // Remove the temporary appName prefix: __temp__:
85
81
  return fullKey.substring('__temp__:'.length);
86
- };
87
-
88
- export { generateQueryCacheKey };
82
+ };export{generateQueryCacheKey};//# sourceMappingURL=generate-query-cache-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-query-cache-key.js","sources":["../../../src/core/keyBuilder/generate-query-cache-key.ts"],"sourcesContent":["/**\n * @file generate-query-cache-key.ts\n * @description Generates consistent cache keys for GraphQL queries\n *\n * This utility creates deterministic cache keys by hashing GraphQL queries and variables,\n * with proper sanitization for Redis key compatibility (e.g., Auth0 userIds with pipes).\n *\n * Key format: queryName:queryHash[:variablesHash]\n * - Query is hashed using SHA-256 for consistent, compact keys\n * - Variables are optionally hashed if provided\n * - All components are sanitized for Redis key safety\n */\n\nimport { createHash } from 'crypto';\nimport { DocumentNode, print } from 'graphql/language/index.js';\nimport { buildRedisKey } from './sanitize-redis-key';\n\nexport type GenerateQueryCacheKeyOptions = {\n query: string | DocumentNode;\n variables: Record<string, unknown>;\n userId?: string;\n tenantId?: string;\n appName?: string;\n logger?: any;\n};\n\n/**\n * Generates a cache key for GraphQL queries using standardized Redis key builder\n *\n * Key format: `[appName]:[tenantId]:[userId]:queryName:queryHash[:variablesHash]`\n * or legacy format: `[tenantId]:[userId]:queryName:queryHash[:variablesHash]`\n * - Query and variables are hashed for consistent, compact keys\n * - Uses buildRedisKey for proper sanitization and validation\n * - Optional components are omitted if not provided\n *\n * @param options - Cache key generation options\n * @returns Generated cache key string\n *\n * @example\n * ```typescript\n * // With appName (recommended)\n * generateQueryCacheKey({\n * query: GetUserDocument,\n * variables: { userId: '123' },\n * userId: 'auth0|123',\n * tenantId: 'default',\n * appName: 'MY_APP',\n * logger: myLogger\n * })\n * // Returns: 'MY_APP:default:auth0-123:user:hash1:hash2'\n *\n * // Legacy format without appName (for backward compatibility)\n * generateQueryCacheKey({\n * query: GetUserDocument,\n * variables: { userId: '123' },\n * userId: 'auth0|123',\n * tenantId: 'default',\n * })\n * // Returns: 'default:auth0-123:user:hash1:hash2'\n * ```\n */\nexport const generateQueryCacheKey = ({\n query,\n variables,\n logger,\n userId,\n tenantId,\n appName,\n}: GenerateQueryCacheKeyOptions): string => {\n const normalizedQuery = (typeof query === 'string' ? query : print(query)).trim();\n const [, queryName] = normalizedQuery?.match(/{\\s*(\\w+)/) ?? [];\n\n // Log the data right before hashing\n if (logger && typeof logger.trace === 'function') {\n logger.trace('About to hash normalized query: [%s]', normalizedQuery);\n logger.trace('About to hash variables: [%j]', variables);\n }\n\n const queryHash = createHash('sha256').update(normalizedQuery).digest('hex');\n const variablesHash = variables ? createHash('sha256').update(JSON.stringify(variables)).digest('hex') : null;\n\n // Build segments: queryName, queryHash, and optional variablesHash\n const segments = [queryName, queryHash];\n if (variablesHash) {\n segments.push(variablesHash);\n }\n\n // If appName is provided, use the full standardized format\n if (appName) {\n return buildRedisKey({\n appName,\n tenantId,\n userId,\n segments,\n });\n }\n\n // Legacy format for backward compatibility (no appName)\n // Still uses buildRedisKey for sanitization, but strips the appName prefix\n const fullKey = buildRedisKey({\n appName: '__temp__',\n tenantId,\n userId,\n segments,\n });\n\n // Remove the temporary appName prefix: __temp__:\n return fullKey.substring('__temp__:'.length);\n};\n"],"names":[],"mappings":"kIAAA;;;;;;;;;;;AAWG;AAeH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCG;AACU,MAAA,qBAAqB,GAAG,CAAC,EAClC,KAAK,EACL,SAAS,EACT,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,GACoB,KAAY;IACvC,MAAM,eAAe,GAAG,CAAC,OAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;AAClF,IAAA,MAAM,GAAG,SAAS,CAAC,GAAG,eAAe,EAAE,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;;IAGhE,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE;AAC9C,QAAA,MAAM,CAAC,KAAK,CAAC,sCAAsC,EAAE,eAAe,CAAC,CAAC;AACtE,QAAA,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,SAAS,CAAC,CAAC;KAC5D;AAED,IAAA,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC7E,IAAA,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;;AAG9G,IAAA,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACxC,IAAI,aAAa,EAAE;AACf,QAAA,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;KAChC;;IAGD,IAAI,OAAO,EAAE;AACT,QAAA,OAAO,aAAa,CAAC;YACjB,OAAO;YACP,QAAQ;YACR,MAAM;YACN,QAAQ;AACX,SAAA,CAAC,CAAC;KACN;;;IAID,MAAM,OAAO,GAAG,aAAa,CAAC;AAC1B,QAAA,OAAO,EAAE,UAAU;QACnB,QAAQ;QACR,MAAM;QACN,QAAQ;AACX,KAAA,CAAC,CAAC;;IAGH,OAAO,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AACjD"}
@@ -1,9 +1,4 @@
1
- import { buildRedisKeyWithNamespace, buildRedisKeyPatternWithNamespace } from './redis-key-builder.js';
2
- export { RedisNamespace, extractNamespaceFromRedisKey, extractTenantIdFromRedisKey, isValidRedisKeyWithNamespace, parseRedisKey } from './redis-key-builder.js';
3
- import 'crypto';
4
- import 'graphql/language/index.js';
5
-
6
- /**
1
+ import {buildRedisKeyWithNamespace,buildRedisKeyPatternWithNamespace}from'./redis-key-builder.js';export{RedisNamespace,extractNamespaceFromRedisKey,extractTenantIdFromRedisKey,isValidRedisKeyWithNamespace,parseRedisKey}from'./redis-key-builder.js';import'crypto';import'graphql/language/index.js';/**
7
2
  * Redis Utilities
8
3
  *
9
4
  * Utility functions for Redis key building, sanitization, and cache key generation.
@@ -22,6 +17,4 @@ function buildRedisKey(options) {
22
17
  function buildRedisKeyPattern(options) {
23
18
  const appName = process.env.APP_NAME || 'APP';
24
19
  return buildRedisKeyPatternWithNamespace({ ...options, appName });
25
- }
26
-
27
- export { buildRedisKey, buildRedisKeyPattern, buildRedisKeyPatternWithNamespace, buildRedisKeyWithNamespace };
20
+ }export{buildRedisKey,buildRedisKeyPattern,buildRedisKeyPatternWithNamespace,buildRedisKeyWithNamespace};//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../src/core/keyBuilder/index.ts"],"sourcesContent":["/**\n * Redis Utilities\n *\n * Utility functions for Redis key building, sanitization, and cache key generation.\n * These utilities provide consistent Redis key formatting across applications.\n */\n\nexport * from './sanitize-redis-key';\nexport * from './redis-key-builder';\nexport * from './generate-query-cache-key';\n\n// Re-export with simplified API for common use cases\nimport {\n buildRedisKeyWithNamespace,\n buildRedisKeyPatternWithNamespace,\n type RedisKeyBuilderOptions,\n} from './redis-key-builder';\n\n/**\n * Simplified buildRedisKey that uses APP_NAME from environment\n */\nexport function buildRedisKey(options: Omit<RedisKeyBuilderOptions, 'appName'>): string {\n const appName = process.env.APP_NAME || 'APP';\n return buildRedisKeyWithNamespace({ ...options, appName });\n}\n\n/**\n * Simplified buildRedisKeyPattern that uses APP_NAME from environment\n */\nexport function buildRedisKeyPattern(options: Omit<RedisKeyBuilderOptions, 'appName'>): string {\n const appName = process.env.APP_NAME || 'APP';\n return buildRedisKeyPatternWithNamespace({ ...options, appName });\n}\n"],"names":[],"mappings":"0SAAA;;;;;AAKG;AAaH;;AAEG;AACG,SAAU,aAAa,CAAC,OAAgD,EAAA;IAC1E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC9C,OAAO,0BAA0B,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED;;AAEG;AACG,SAAU,oBAAoB,CAAC,OAAgD,EAAA;IACjF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC9C,OAAO,iCAAiC,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACtE"}
@@ -1,6 +1,4 @@
1
- import { sanitizeRedisKeyComponent } from './sanitize-redis-key.js';
2
-
3
- /**
1
+ import {sanitizeRedisKeyComponent}from'./sanitize-redis-key.js';/**
4
2
  * Centralized Redis Key Builder
5
3
  *
6
4
  * Enforces consistent Redis key formatting across the entire application.
@@ -180,6 +178,4 @@ function parseRedisKey(key) {
180
178
  namespace: parts[2],
181
179
  segments: parts.slice(3),
182
180
  };
183
- }
184
-
185
- export { RedisNamespace, buildRedisKeyPatternWithNamespace, buildRedisKeyWithNamespace, extractNamespaceFromRedisKey, extractTenantIdFromRedisKey, isValidRedisKeyWithNamespace, parseRedisKey };
181
+ }export{RedisNamespace,buildRedisKeyPatternWithNamespace,buildRedisKeyWithNamespace,extractNamespaceFromRedisKey,extractTenantIdFromRedisKey,isValidRedisKeyWithNamespace,parseRedisKey};//# sourceMappingURL=redis-key-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-key-builder.js","sources":["../../../src/core/keyBuilder/redis-key-builder.ts"],"sourcesContent":["/**\n * Centralized Redis Key Builder\n *\n * Enforces consistent Redis key formatting across the entire application.\n * Standard format: <APP_NAME>:<tenantId>:<namespace>:<...segments>\n *\n * Key Design Principles:\n * 1. All keys MUST start with APP_NAME for application isolation\n * 2. All keys SHOULD include tenantId for multi-tenancy (use 'default' if no tenant context)\n * 3. Keys are sanitized to remove invalid Redis characters\n * 4. Use namespaces to organize keys by domain/feature\n */\n\nimport { sanitizeRedisKeyComponent } from './sanitize-redis-key';\n\n/**\n * Options for building Redis keys with namespace support\n */\nexport interface RedisKeyBuilderOptions {\n /** Application name for key prefix (e.g., 'CDMBASE_TEST') */\n appName: string;\n /** Tenant ID for multi-tenancy. Use 'default' for global/system keys */\n tenantId?: string;\n /** Optional user ID for user-specific keys */\n userId?: string;\n /** Namespace for organizing keys (e.g., 'cache', 'session', 'tenant', 'config') */\n namespace: string;\n /** Additional key segments (e.g., ['user', userId] or ['query', queryName]) */\n segments: string[];\n}\n\n/**\n * Builds a standardized Redis key with namespace and consistent formatting\n *\n * Format: <APP_NAME>:<tenantId>:<namespace>:<segment1>:<segment2>:...\n *\n * @param options - Key building options\n * @returns Formatted and sanitized Redis key\n *\n * @example\n * ```typescript\n * // Cache key with tenant\n * buildRedisKeyWithNamespace({\n * appName: 'CDMBASE_TEST',\n * tenantId: 'tenant-123',\n * namespace: 'cache',\n * segments: ['user', userId, 'profile']\n * });\n * // => \"CDMBASE_TEST:tenant-123:cache:user:auth0-123:profile\"\n *\n * // Global configuration key\n * buildRedisKeyWithNamespace({\n * appName: 'CDMBASE_TEST',\n * tenantId: 'default',\n * namespace: 'config',\n * segments: ['extension', 'permissions']\n * });\n * // => \"CDMBASE_TEST:default:config:extension:permissions\"\n *\n * // Session key without tenant\n * buildRedisKeyWithNamespace({\n * appName: 'CDMBASE_TEST',\n * namespace: 'session',\n * segments: ['abc123']\n * });\n * // => \"CDMBASE_TEST:default:session:abc123\"\n * ```\n */\nexport function buildRedisKeyWithNamespace(options: RedisKeyBuilderOptions): string {\n const { appName, tenantId = 'default', userId, namespace, segments } = options;\n\n // Sanitize all components\n const sanitizedAppName = sanitizeRedisKeyComponent(appName);\n const sanitizedTenantId = sanitizeRedisKeyComponent(tenantId);\n const sanitizedNamespace = sanitizeRedisKeyComponent(namespace);\n const sanitizedSegments = segments.map(sanitizeRedisKeyComponent);\n\n // Build key with consistent format, including userId if provided\n const parts = [sanitizedAppName, sanitizedTenantId];\n\n if (userId) {\n parts.push(sanitizeRedisKeyComponent(userId));\n }\n\n parts.push(sanitizedNamespace, ...sanitizedSegments);\n\n return parts.join(':');\n}\n\n/**\n * Builds a wildcard pattern for Redis KEYS command with namespace support\n *\n * @param options - Key building options (segments can include '*' wildcards)\n * @returns Redis key pattern for matching multiple keys\n *\n * @example\n * ```typescript\n * // Match all cache keys for a tenant\n * buildRedisKeyPatternWithNamespace({\n * appName: 'CDMBASE_TEST',\n * tenantId: 'tenant-123',\n * namespace: 'cache',\n * segments: ['*']\n * });\n * // => \"CDMBASE_TEST:tenant-123:cache:*\"\n *\n * // Match all user cache keys across all tenants\n * buildRedisKeyPatternWithNamespace({\n * appName: 'CDMBASE_TEST',\n * tenantId: '*',\n * namespace: 'cache',\n * segments: ['user', '*']\n * });\n * // => \"CDMBASE_TEST:*:cache:user:*\"\n * ```\n */\nexport function buildRedisKeyPatternWithNamespace(options: RedisKeyBuilderOptions): string {\n // For patterns, allow '*' to pass through without sanitization\n const { appName, tenantId = '*', userId, namespace, segments } = options;\n\n const sanitizedAppName = sanitizeRedisKeyComponent(appName);\n const sanitizedTenantId = tenantId === '*' ? '*' : sanitizeRedisKeyComponent(tenantId);\n const sanitizedNamespace = sanitizeRedisKeyComponent(namespace);\n const sanitizedSegments = segments.map((seg) => (seg === '*' ? '*' : sanitizeRedisKeyComponent(seg)));\n\n // Build pattern with userId if provided\n const parts = [sanitizedAppName, sanitizedTenantId];\n\n if (userId !== undefined) {\n parts.push(userId === '*' ? '*' : sanitizeRedisKeyComponent(userId));\n }\n\n parts.push(sanitizedNamespace, ...sanitizedSegments);\n\n return parts.join(':');\n}\n\n/**\n * Common namespaces used across the application\n * These provide a standardized vocabulary for organizing Redis keys\n */\nexport const RedisNamespace = {\n /** GraphQL/API response caching */\n CACHE: 'cache',\n /** User session data */\n SESSION: 'session',\n /** Tenant information cache */\n TENANT: 'tenant',\n /** Configuration and settings */\n CONFIG: 'config',\n /** Extension/plugin data */\n EXTENSION: 'extension',\n /** Contribution points */\n CONTRIBUTION: 'contribution',\n /** Request-scoped storage */\n STORAGE: 'storage',\n /** Permission and access control */\n PERMISSION: 'permission',\n /** Temporary/TTL based data */\n TEMP: 'temp',\n} as const;\n\nexport type RedisNamespaceType = (typeof RedisNamespace)[keyof typeof RedisNamespace];\n\n/**\n * Extracts tenant ID from a Redis key if present\n *\n * @param key - Redis key to parse\n * @returns Tenant ID or null if not found/parseable\n */\nexport function extractTenantIdFromRedisKey(key: string): string | null {\n const parts = key.split(':');\n // Format: APP_NAME:tenantId:namespace:...\n if (parts.length >= 3) {\n return parts[1];\n }\n return null;\n}\n\n/**\n * Extracts namespace from a Redis key if present\n *\n * @param key - Redis key to parse\n * @returns Namespace or null if not found/parseable\n */\nexport function extractNamespaceFromRedisKey(key: string): string | null {\n const parts = key.split(':');\n // Format: APP_NAME:tenantId:namespace:...\n if (parts.length >= 3) {\n return parts[2];\n }\n return null;\n}\n\n/**\n * Validates if a key follows the standard format with namespace\n *\n * @param key - Redis key to validate\n * @returns True if key follows standard format\n */\nexport function isValidRedisKeyWithNamespace(key: string): boolean {\n const parts = key.split(':');\n // Minimum format: APP_NAME:tenantId:namespace\n return parts.length >= 3 && parts[0].length > 0 && parts[1].length > 0 && parts[2].length > 0;\n}\n\n/**\n * Parses a Redis key into its components\n *\n * @param key - Redis key to parse\n * @returns Parsed key components or null if invalid\n */\nexport interface ParsedRedisKey {\n appName: string;\n tenantId: string;\n namespace: string;\n segments: string[];\n}\n\nexport function parseRedisKey(key: string): ParsedRedisKey | null {\n const parts = key.split(':');\n if (parts.length < 3) {\n return null;\n }\n\n return {\n appName: parts[0],\n tenantId: parts[1],\n namespace: parts[2],\n segments: parts.slice(3),\n };\n}\n"],"names":[],"mappings":"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"}
@@ -78,10 +78,7 @@ const buildRedisKey = ({ appName, tenantId, userId, segments = [], logger }) =>
78
78
  const sanitizeRedisKey = (key) => {
79
79
  if (!key)
80
80
  return '';
81
- return key
82
- .split(':')
83
- .map(sanitizeRedisKeyComponent)
84
- .join(':');
81
+ return key.split(':').map(sanitizeRedisKeyComponent).join(':');
85
82
  };
86
83
  /**
87
84
  * Escapes special Redis pattern characters for literal matching
@@ -115,6 +112,4 @@ const isValidRedisKey = (key) => {
115
112
  if (/[\s|"'\\]/.test(key))
116
113
  return false;
117
114
  return true;
118
- };
119
-
120
- export { buildRedisKey, escapeRedisPattern, isHashLikeTenantId, isValidRedisKey, sanitizeRedisKey, sanitizeRedisKeyComponent };
115
+ };export{buildRedisKey,escapeRedisPattern,isHashLikeTenantId,isValidRedisKey,sanitizeRedisKey,sanitizeRedisKeyComponent};//# sourceMappingURL=sanitize-redis-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize-redis-key.js","sources":["../../../src/core/keyBuilder/sanitize-redis-key.ts"],"sourcesContent":["/**\n * Sanitizes a Redis key component to ensure it's valid and safe\n *\n * Redis keys can contain any binary sequence, but for best practices and compatibility:\n * - Avoid whitespace (spaces, tabs, newlines) - replace with underscores\n * - Avoid pipes (|) which are used in Auth0 userIds - replace with hyphens\n * - Avoid quotes and backslashes that could cause escaping issues - remove them\n * - Avoid control characters - remove them\n *\n * @param str - The string component to sanitize\n * @returns Sanitized string safe for use in Redis keys\n *\n * @example\n * ```typescript\n * sanitizeRedisKeyComponent('auth0|6120ecbb1e5c68006aabe17a')\n * // Returns: 'auth0-6120ecbb1e5c68006aabe17a'\n *\n * sanitizeRedisKeyComponent('user name with spaces')\n * // Returns: 'user_name_with_spaces'\n * ```\n */\nexport const sanitizeRedisKeyComponent = (str: string): string => {\n if (!str) return str;\n return (\n str\n .replace(/[\\s\\r\\n\\t]+/g, '_') // Replace whitespace with underscores\n .replace(/\\|/g, '-') // Replace pipes with hyphens (Auth0 userIds: auth0|123 -> auth0-123)\n .replace(/[\"'\\\\]/g, '') // Remove quotes and backslashes\n // eslint-disable-next-line no-control-regex\n .replace(/[\\x00-\\x1F\\x7F]/g, '') // Remove control characters\n .trim()\n );\n};\n\n/**\n * Validates if a tenantId looks like a MongoDB ObjectId hash\n * MongoDB ObjectIds are 24 character hexadecimal strings\n *\n * @param tenantId - The tenant ID to validate\n * @returns true if the tenantId appears to be a hash, false otherwise\n */\nexport const isHashLikeTenantId = (tenantId: string): boolean => {\n if (!tenantId || tenantId === 'default') return false;\n return /^[a-f0-9]{24,}$/i.test(tenantId);\n};\n\n/**\n * Builds a standardized Redis key with proper tenant isolation and sanitization\n *\n * Key format: `{appName}:{tenantId}:{userId}:{...segments}`\n * - All components are sanitized to remove invalid characters\n * - TenantId is validated and replaced with 'default' if it appears to be a hash\n * - Components can be skipped by passing null/undefined\n *\n * @param options - Key building options\n * @param options.appName - Application name (e.g., 'CDMBASE_TEST')\n * @param options.tenantId - Tenant identifier (validated against hash pattern)\n * @param options.userId - User identifier (sanitized for Auth0 format)\n * @param options.segments - Additional key segments (array of strings)\n * @param options.logger - Optional logger for warnings\n * @returns Constructed Redis key\n *\n * @example\n * ```typescript\n * buildRedisKey({\n * appName: 'CDMBASE_TEST',\n * tenantId: 'default',\n * userId: 'auth0|6120ecbb',\n * segments: ['currentPagePermissions', 'hash123']\n * })\n * // Returns: 'CDMBASE_TEST:default:auth0-6120ecbb:currentPagePermissions:hash123'\n * ```\n */\nexport interface BuildRedisKeyOptions {\n appName: string;\n tenantId?: string;\n userId?: string;\n segments?: string[];\n logger?: {\n warn: (message: string, ...args: unknown[]) => void;\n };\n}\n\nexport const buildRedisKey = ({ appName, tenantId, userId, segments = [], logger }: BuildRedisKeyOptions): string => {\n // Validate and sanitize tenantId\n let sanitizedTenantId = tenantId || 'default';\n if (sanitizedTenantId !== 'default' && isHashLikeTenantId(sanitizedTenantId)) {\n if (logger) {\n logger.warn(\n 'TenantId appears to be a hash (%s...), using \"default\" instead',\n sanitizedTenantId.substring(0, 16),\n );\n }\n sanitizedTenantId = 'default';\n }\n\n // Build key parts\n const parts: string[] = [sanitizeRedisKeyComponent(appName), sanitizeRedisKeyComponent(sanitizedTenantId)];\n\n // Add userId if provided\n if (userId) {\n parts.push(sanitizeRedisKeyComponent(userId));\n }\n\n // Add additional segments\n if (segments && segments.length > 0) {\n parts.push(...segments.map((seg) => sanitizeRedisKeyComponent(seg)));\n }\n\n return parts.join(':');\n};\n\n/**\n * Builds a wildcard pattern for Redis key matching\n * Useful for bulk operations like deleting all keys matching a pattern\n *\n * @param options - Pattern building options (same as buildRedisKey)\n * @returns Redis key pattern with wildcards\n *\n * @example\n * ```typescript\n * buildRedisKeyPattern({\n * appName: 'CDMBASE_TEST',\n * tenantId: 'default',\n * segments: ['currentPagePermissions']\n * })\n * // Returns: 'CDMBASE_TEST:default:*:currentPagePermissions:*'\n * ```\n */\nexport const buildRedisKeyPattern = ({\n appName,\n tenantId,\n userId,\n segments = [],\n}: Omit<BuildRedisKeyOptions, 'logger'>): string => {\n const parts: string[] = [sanitizeRedisKeyComponent(appName), tenantId ? sanitizeRedisKeyComponent(tenantId) : '*'];\n\n // Add userId or wildcard\n parts.push(userId ? sanitizeRedisKeyComponent(userId) : '*');\n\n // Add segments with wildcards\n if (segments && segments.length > 0) {\n parts.push(...segments.map((seg) => (seg === '*' ? '*' : sanitizeRedisKeyComponent(seg))));\n } else {\n parts.push('*');\n }\n\n return parts.join(':');\n};\n\n/**\n * Sanitizes a complete Redis key by sanitizing each component\n *\n * @param key - Redis key to sanitize\n * @returns Sanitized Redis key\n *\n * @example\n * ```typescript\n * sanitizeRedisKey('APP:tenant with spaces:cache:user|data')\n * // Returns: 'APP:tenant_with_spaces:cache:user-data'\n * ```\n */\nexport const sanitizeRedisKey = (key: string): string => {\n if (!key) return '';\n return key.split(':').map(sanitizeRedisKeyComponent).join(':');\n};\n\n/**\n * Escapes special Redis pattern characters for literal matching\n *\n * @param pattern - Pattern string to escape\n * @returns Escaped pattern string\n *\n * @example\n * ```typescript\n * escapeRedisPattern('file*.txt')\n * // Returns: 'file\\\\*.txt'\n * ```\n */\nexport const escapeRedisPattern = (pattern: string): string => {\n if (!pattern) return '';\n return pattern.replace(/[*?[\\]^\\\\]/g, '\\\\$&');\n};\n\n/**\n * Validates if a Redis key is properly formatted\n *\n * @param key - Redis key to validate\n * @returns true if key is valid, false otherwise\n */\nexport const isValidRedisKey = (key: string): boolean => {\n if (!key || typeof key !== 'string') return false;\n if (key.length > 512) return false; // Redis key length limit\n // Check for invalid characters (whitespace, pipes, etc.)\n if (/[\\s|\"'\\\\]/.test(key)) return false;\n return true;\n};\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;AAoBG;AACU,MAAA,yBAAyB,GAAG,CAAC,GAAW,KAAY;AAC7D,IAAA,IAAI,CAAC,GAAG;AAAE,QAAA,OAAO,GAAG,CAAC;AACrB,IAAA,QACI,GAAG;AACE,SAAA,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;AAC5B,SAAA,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;AACnB,SAAA,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;;AAEtB,SAAA,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,IAAI,EAAE,EACb;AACN,EAAE;AAEF;;;;;;AAMG;AACU,MAAA,kBAAkB,GAAG,CAAC,QAAgB,KAAa;AAC5D,IAAA,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,SAAS;AAAE,QAAA,OAAO,KAAK,CAAC;AACtD,IAAA,OAAO,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC7C,EAAE;AAuCW,MAAA,aAAa,GAAG,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,MAAM,EAAwB,KAAY;;AAEhH,IAAA,IAAI,iBAAiB,GAAG,QAAQ,IAAI,SAAS,CAAC;IAC9C,IAAI,iBAAiB,KAAK,SAAS,IAAI,kBAAkB,CAAC,iBAAiB,CAAC,EAAE;QAC1E,IAAI,MAAM,EAAE;AACR,YAAA,MAAM,CAAC,IAAI,CACP,gEAAgE,EAChE,iBAAiB,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CACrC,CAAC;SACL;QACD,iBAAiB,GAAG,SAAS,CAAC;KACjC;;AAGD,IAAA,MAAM,KAAK,GAAa,CAAC,yBAAyB,CAAC,OAAO,CAAC,EAAE,yBAAyB,CAAC,iBAAiB,CAAC,CAAC,CAAC;;IAG3G,IAAI,MAAM,EAAE;QACR,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAC;KACjD;;IAGD,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;AACjC,QAAA,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,yBAAyB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;KACxE;AAED,IAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,EAAE;AAwCF;;;;;;;;;;;AAWG;AACU,MAAA,gBAAgB,GAAG,CAAC,GAAW,KAAY;AACpD,IAAA,IAAI,CAAC,GAAG;AAAE,QAAA,OAAO,EAAE,CAAC;AACpB,IAAA,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnE,EAAE;AAEF;;;;;;;;;;;AAWG;AACU,MAAA,kBAAkB,GAAG,CAAC,OAAe,KAAY;AAC1D,IAAA,IAAI,CAAC,OAAO;AAAE,QAAA,OAAO,EAAE,CAAC;IACxB,OAAO,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;AAClD,EAAE;AAEF;;;;;AAKG;AACU,MAAA,eAAe,GAAG,CAAC,GAAW,KAAa;AACpD,IAAA,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,KAAK,CAAC;AAClD,IAAA,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG;QAAE,OAAO,KAAK,CAAC;;AAEnC,IAAA,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;AAAE,QAAA,OAAO,KAAK,CAAC;AACxC,IAAA,OAAO,IAAI,CAAC;AAChB"}
@@ -1,6 +1,4 @@
1
- import { Redis } from '@upstash/redis/cloudflare';
2
-
3
- class UpstashRedisClient {
1
+ import {Redis}from'@upstash/redis/cloudflare';class UpstashRedisClient {
4
2
  redis;
5
3
  constructor(config) {
6
4
  this.redis = new Redis({ url: config.url, token: config.token });
@@ -22,6 +20,4 @@ class UpstashRedisClient {
22
20
  async del(key) {
23
21
  return await this.redis.del(key);
24
22
  }
25
- }
26
-
27
- export { UpstashRedisClient };
23
+ }export{UpstashRedisClient};//# sourceMappingURL=upstash-redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstash-redis.js","sources":["../../src/core/upstash-redis.ts"],"sourcesContent":["import { Redis } from '@upstash/redis/cloudflare';\nimport { 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): Promise<any | null> {\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"],"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;IAED,MAAM,GAAG,CAAC,GAAW,EAAA;QACjB,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;AACJ"}
@@ -0,0 +1,134 @@
1
+ """
2
+ Standard Redis namespaces for organizing keys.
3
+ Using namespaces helps prevent key collisions and makes bulk operations easier.
4
+ """
5
+ enum RedisNamespace {
6
+ """Cache-related keys"""
7
+ cache
8
+ """Session data keys"""
9
+ session
10
+ """Tenant-specific keys"""
11
+ tenant
12
+ """Configuration keys"""
13
+ config
14
+ """Extension-related keys"""
15
+ extension
16
+ """Contribution-related keys"""
17
+ contribution
18
+ """Storage keys"""
19
+ storage
20
+ """Permission keys"""
21
+ permission
22
+ """Temporary keys"""
23
+ temp
24
+ """Queue-related keys"""
25
+ queue
26
+ """Lock keys"""
27
+ lock
28
+ }
29
+
30
+ """
31
+ Options for building Redis keys with standardized format.
32
+ Example: APP_NAME:tenant-123:cache:user:profile
33
+ """
34
+ input RedisKeyOptions {
35
+ """Tenant ID for multi-tenant isolation (defaults to 'default')"""
36
+ tenantId: String
37
+ """Namespace to categorize keys (e.g., 'cache', 'session', 'storage')"""
38
+ namespace: String!
39
+ """Additional key segments to append"""
40
+ segments: [String!]!
41
+ """Optional user ID for user-specific keys"""
42
+ userId: String
43
+ }
44
+
45
+ """
46
+ Context for cache operations.
47
+ Provides tenant, user, and organization isolation for cached data.
48
+ """
49
+ input CacheContext {
50
+ """Tenant identifier for multi-tenant applications"""
51
+ tenantId: String
52
+ """User identifier for user-specific caching"""
53
+ userId: String
54
+ """Organization identifier for org-level caching"""
55
+ orgId: String
56
+ }
57
+
58
+ """
59
+ Cache policy for controlling cache behavior.
60
+ """
61
+ input CachePolicy {
62
+ """Maximum age in seconds (TTL)"""
63
+ maxAge: Int!
64
+ """Cache scope (PUBLIC, PRIVATE, etc.)"""
65
+ scope: String
66
+ }
67
+
68
+ """
69
+ Options for scanning Redis keys.
70
+ Used for iterating through keys matching a pattern.
71
+ """
72
+ input RedisScanOptions {
73
+ """Pattern to match keys (supports wildcards)"""
74
+ match: String
75
+ """Number of keys to return per iteration"""
76
+ count: Int
77
+ """Optional Redis data type to filter by"""
78
+ type: RedisDataType
79
+ }
80
+
81
+ """
82
+ Redis data types for filtering scan operations
83
+ """
84
+ enum RedisDataType {
85
+ string
86
+ list
87
+ set
88
+ zset
89
+ hash
90
+ stream
91
+ }
92
+
93
+ """
94
+ Result from a Redis SCAN operation
95
+ """
96
+ type RedisScanResult {
97
+ """Next cursor position (0 means scan is complete)"""
98
+ cursor: String!
99
+ """Array of keys matching the scan criteria"""
100
+ keys: [String!]!
101
+ }
102
+
103
+ """
104
+ Options for Redis bulk operations.
105
+ Used when performing operations on multiple keys.
106
+ """
107
+ input RedisBulkOptions {
108
+ """Whether to use Redis pipeline for batch operations"""
109
+ pipeline: Boolean
110
+ """Whether to wrap operations in a MULTI/EXEC transaction"""
111
+ transaction: Boolean
112
+ }
113
+
114
+ """
115
+ Redis connection configuration
116
+ """
117
+ input RedisConnectionConfig {
118
+ """Redis server host"""
119
+ host: String
120
+ """Redis server port"""
121
+ port: Int
122
+ """Optional password for authentication"""
123
+ password: String
124
+ """Database number to use"""
125
+ db: Int
126
+ """Prefix to add to all keys"""
127
+ keyPrefix: String
128
+ """Maximum retry attempts"""
129
+ maxRetriesPerRequest: Int
130
+ """Whether to check connection is ready"""
131
+ enableReadyCheck: Boolean
132
+ """Whether to delay connection until first command"""
133
+ lazyConnect: Boolean
134
+ }
package/lib/index.js CHANGED
@@ -1,7 +1 @@
1
- export { IORedisClient } from './core/ioredis.js';
2
- export { UpstashRedisClient } from './core/upstash-redis.js';
3
- export { buildRedisKey, buildRedisKeyPattern } from './core/keyBuilder/index.js';
4
- export { RedisCacheManager } from './services/RedisCacheManager.js';
5
- export { escapeRedisPattern, isHashLikeTenantId, isValidRedisKey, sanitizeRedisKey, sanitizeRedisKeyComponent } from './core/keyBuilder/sanitize-redis-key.js';
6
- export { RedisNamespace, buildRedisKeyPatternWithNamespace, buildRedisKeyWithNamespace, extractNamespaceFromRedisKey, extractTenantIdFromRedisKey, isValidRedisKeyWithNamespace, parseRedisKey } from './core/keyBuilder/redis-key-builder.js';
7
- export { generateQueryCacheKey } from './core/keyBuilder/generate-query-cache-key.js';
1
+ export{IORedisClient}from'./core/ioredis.js';export{UpstashRedisClient}from'./core/upstash-redis.js';export{buildRedisKey,buildRedisKeyPattern}from'./core/keyBuilder/index.js';export{RedisCacheManager}from'./services/RedisCacheManager.js';export{escapeRedisPattern,isHashLikeTenantId,isValidRedisKey,sanitizeRedisKey,sanitizeRedisKeyComponent}from'./core/keyBuilder/sanitize-redis-key.js';export{RedisNamespace,buildRedisKeyPatternWithNamespace,buildRedisKeyWithNamespace,extractNamespaceFromRedisKey,extractTenantIdFromRedisKey,isValidRedisKeyWithNamespace,parseRedisKey}from'./core/keyBuilder/redis-key-builder.js';export{generateQueryCacheKey}from'./core/keyBuilder/generate-query-cache-key.js';//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/lib/module.js CHANGED
@@ -1,9 +1,4 @@
1
- import { Feature } from '@common-stack/server-core';
2
- import { infraContainer } from './containers/container.js';
3
-
4
- var module = new Feature({
1
+ import {Feature}from'@common-stack/server-core';import {infraContainer}from'./containers/container.js';var module = new Feature({
5
2
  createContainerFunc: [infraContainer],
6
3
  createMicroServiceContainerFunc: [infraContainer],
7
- });
8
-
9
- export { module as default };
4
+ });export{module as default};//# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sources":["../src/module.ts"],"sourcesContent":["import { Feature } from '@common-stack/server-core';\nimport { infraContainer } from './containers';\n\nexport default new Feature({\n createContainerFunc: [infraContainer],\n createMicroServiceContainerFunc: [infraContainer],\n});\n"],"names":[],"mappings":"uGAGA,aAAe,IAAI,OAAO,CAAC;IACvB,mBAAmB,EAAE,CAAC,cAAc,CAAC;IACrC,+BAA+B,EAAE,CAAC,cAAc,CAAC;AACpD,CAAA,CAAC"}
@@ -1,10 +1,4 @@
1
- import { __decorate, __param, __metadata } from 'tslib';
2
- import { isHashLikeTenantId, sanitizeRedisKeyComponent } from '../core/keyBuilder/sanitize-redis-key.js';
3
- import { generateQueryCacheKey } from '../core/keyBuilder/generate-query-cache-key.js';
4
- import { print } from 'graphql';
5
- import { injectable, inject } from 'inversify';
6
-
7
- /**
1
+ import {__decorate,__param,__metadata}from'tslib';import {isHashLikeTenantId,sanitizeRedisKeyComponent}from'../core/keyBuilder/sanitize-redis-key.js';import {generateQueryCacheKey}from'../core/keyBuilder/generate-query-cache-key.js';import {print}from'graphql';import {injectable,inject}from'inversify';/**
8
2
  * @file RedisCacheManager.ts
9
3
  * @description Redis-based cache manager for GraphQL query caching
10
4
  *
@@ -180,6 +174,4 @@ RedisCacheManager = RedisCacheManager_1 = __decorate([
180
174
  __param(0, inject('REDIS_CLIENT')),
181
175
  __param(1, inject('LOGGER')),
182
176
  __metadata("design:paramtypes", [Function, Object])
183
- ], RedisCacheManager);
184
-
185
- export { RedisCacheManager };
177
+ ], RedisCacheManager);export{RedisCacheManager};//# sourceMappingURL=RedisCacheManager.js.map
@@ -0,0 +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, ICachePolicy, IRedisCacheManager } from 'common/server';\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('REDIS_CLIENT')\n protected readonly redisClient: Redis,\n @inject('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 * 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@common-stack/store-redis",
3
- "version": "8.2.5-alpha.35",
3
+ "version": "8.2.5-alpha.36",
4
4
  "description": "Redis store utilities and services for common-stack",
5
5
  "license": "UNLICENSED",
6
6
  "author": "CDMBase LLC",
@@ -55,5 +55,5 @@
55
55
  "typescript": {
56
56
  "definition": "lib/index.d.ts"
57
57
  },
58
- "gitHead": "33d6be7fdf924ca76bc1b98cee3031d55a8115d7"
58
+ "gitHead": "073ba112d0d673927cca535e7805946fe30880d4"
59
59
  }