@nest-omni/core 3.1.1-11 → 3.1.1-13

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.
Files changed (81) hide show
  1. package/cache/cache-metrics.service.d.ts +83 -0
  2. package/cache/cache-metrics.service.js +298 -0
  3. package/cache/cache-serialization.service.d.ts +29 -0
  4. package/cache/cache-serialization.service.js +217 -0
  5. package/cache/cache.health.d.ts +35 -0
  6. package/cache/cache.health.js +193 -0
  7. package/cache/cache.module.d.ts +24 -0
  8. package/cache/cache.module.js +105 -0
  9. package/cache/cache.service.d.ts +35 -0
  10. package/cache/cache.service.js +280 -0
  11. package/cache/cache.warmup.service.d.ts +34 -0
  12. package/cache/cache.warmup.service.js +199 -0
  13. package/cache/decorators/cache-evict.decorator.d.ts +2 -0
  14. package/cache/decorators/cache-evict.decorator.js +72 -0
  15. package/cache/decorators/cache-put.decorator.d.ts +2 -0
  16. package/cache/decorators/cache-put.decorator.js +48 -0
  17. package/cache/decorators/cacheable.decorator.d.ts +4 -0
  18. package/cache/decorators/cacheable.decorator.js +68 -0
  19. package/cache/decorators/index.d.ts +3 -0
  20. package/cache/decorators/index.js +11 -0
  21. package/cache/dependencies/callback.dependency.d.ts +11 -0
  22. package/cache/dependencies/callback.dependency.js +92 -0
  23. package/cache/dependencies/chain.dependency.d.ts +20 -0
  24. package/cache/dependencies/chain.dependency.js +113 -0
  25. package/cache/dependencies/db.dependency.d.ts +15 -0
  26. package/cache/dependencies/db.dependency.js +71 -0
  27. package/cache/dependencies/file.dependency.d.ts +17 -0
  28. package/cache/dependencies/file.dependency.js +63 -0
  29. package/cache/dependencies/index.d.ts +6 -0
  30. package/cache/dependencies/index.js +22 -0
  31. package/cache/dependencies/tag.dependency.d.ts +16 -0
  32. package/cache/dependencies/tag.dependency.js +75 -0
  33. package/cache/dependencies/time.dependency.d.ts +19 -0
  34. package/cache/dependencies/time.dependency.js +71 -0
  35. package/cache/examples/quick-start.d.ts +5 -0
  36. package/cache/examples/quick-start.js +341 -0
  37. package/cache/index.d.ts +14 -0
  38. package/cache/index.js +41 -0
  39. package/cache/interfaces/cache-dependency.interface.d.ts +11 -0
  40. package/cache/interfaces/cache-dependency.interface.js +2 -0
  41. package/cache/interfaces/cache-options.interface.d.ts +32 -0
  42. package/cache/interfaces/cache-options.interface.js +9 -0
  43. package/cache/interfaces/cache-provider.interface.d.ts +28 -0
  44. package/cache/interfaces/cache-provider.interface.js +2 -0
  45. package/cache/interfaces/index.d.ts +3 -0
  46. package/cache/interfaces/index.js +19 -0
  47. package/cache/providers/base-cache.provider.d.ts +16 -0
  48. package/cache/providers/base-cache.provider.js +32 -0
  49. package/cache/providers/cls-cache.provider.d.ts +16 -0
  50. package/cache/providers/cls-cache.provider.js +140 -0
  51. package/cache/providers/index.d.ts +4 -0
  52. package/cache/providers/index.js +23 -0
  53. package/cache/providers/memory-cache.provider.d.ts +23 -0
  54. package/cache/providers/memory-cache.provider.js +130 -0
  55. package/cache/providers/redis-cache.provider.d.ts +23 -0
  56. package/cache/providers/redis-cache.provider.js +215 -0
  57. package/cache/utils/dependency-manager.util.d.ts +15 -0
  58. package/cache/utils/dependency-manager.util.js +141 -0
  59. package/cache/utils/index.d.ts +2 -0
  60. package/cache/utils/index.js +18 -0
  61. package/cache/utils/key-generator.util.d.ts +13 -0
  62. package/cache/utils/key-generator.util.js +74 -0
  63. package/index.d.ts +1 -0
  64. package/index.js +1 -0
  65. package/package.json +4 -17
  66. package/redis-lock/index.d.ts +4 -0
  67. package/redis-lock/index.js +13 -0
  68. package/{setup → redis-lock}/redis-lock.decorator.d.ts +3 -0
  69. package/{setup → redis-lock}/redis-lock.decorator.js +25 -14
  70. package/redis-lock/redis-lock.examples.d.ts +13 -0
  71. package/redis-lock/redis-lock.examples.js +159 -0
  72. package/redis-lock/redis-lock.module.d.ts +23 -0
  73. package/redis-lock/redis-lock.module.js +117 -0
  74. package/{setup → redis-lock}/redis-lock.service.d.ts +9 -21
  75. package/{setup → redis-lock}/redis-lock.service.js +29 -103
  76. package/setup/index.d.ts +1 -2
  77. package/setup/index.js +1 -2
  78. package/setup/schedule.decorator.js +4 -4
  79. package/shared/serviceRegistryModule.js +63 -4
  80. package/shared/services/api-config.service.d.ts +0 -5
  81. package/shared/services/api-config.service.js +3 -27
@@ -0,0 +1,83 @@
1
+ import { EventEmitter } from 'events';
2
+ export interface CacheOperationRecord {
3
+ type: 'get' | 'set' | 'delete' | 'clear';
4
+ layer: string;
5
+ hit: boolean;
6
+ latency: number;
7
+ error?: string;
8
+ key?: string;
9
+ timestamp: Date;
10
+ }
11
+ export interface CacheMetrics {
12
+ totalOperations: number;
13
+ hits: number;
14
+ misses: number;
15
+ hitRate: number;
16
+ avgLatency: number;
17
+ minLatency: number;
18
+ maxLatency: number;
19
+ p95Latency: number;
20
+ p99Latency: number;
21
+ gets: number;
22
+ sets: number;
23
+ deletes: number;
24
+ clears: number;
25
+ totalKeys: number;
26
+ totalSize: number;
27
+ errors: number;
28
+ errorRate: number;
29
+ layerMetrics: {
30
+ cls: {
31
+ hits: number;
32
+ misses: number;
33
+ avgLatency: number;
34
+ };
35
+ memory: {
36
+ hits: number;
37
+ misses: number;
38
+ avgLatency: number;
39
+ size: number;
40
+ };
41
+ redis: {
42
+ hits: number;
43
+ misses: number;
44
+ avgLatency: number;
45
+ };
46
+ };
47
+ timeWindow: {
48
+ start: Date;
49
+ end: Date;
50
+ duration: number;
51
+ };
52
+ }
53
+ export declare class CacheMetricsService extends EventEmitter {
54
+ private readonly logger;
55
+ private readonly operations;
56
+ private readonly maxOperations;
57
+ private readonly maxAge;
58
+ constructor();
59
+ recordOperation(type: 'get' | 'set' | 'delete' | 'clear', layer: string, hit: boolean, latency: number, error?: string, key?: string): void;
60
+ getMetrics(timeWindowMs?: number): CacheMetrics;
61
+ getSnapshot(): Partial<CacheMetrics>;
62
+ getSlowOperations(limit?: number): CacheOperationRecord[];
63
+ getMostAccessedKeys(limit?: number): {
64
+ key: string;
65
+ count: number;
66
+ avgLatency: number;
67
+ }[];
68
+ getErrorBreakdown(): {
69
+ error: string;
70
+ count: number;
71
+ lastOccurrence: Date;
72
+ }[];
73
+ resetMetrics(): void;
74
+ exportMetrics(format?: 'prometheus' | 'json'): string;
75
+ private cleanupOldRecords;
76
+ private createEmptyMetrics;
77
+ private calculateLayerMetrics;
78
+ private average;
79
+ private percentile;
80
+ private getUniqueKeyCount;
81
+ private estimateTotalSize;
82
+ private toPrometheusFormat;
83
+ }
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var CacheMetricsService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.CacheMetricsService = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const events_1 = require("events");
16
+ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService extends events_1.EventEmitter {
17
+ constructor() {
18
+ super();
19
+ this.logger = new common_1.Logger(CacheMetricsService_1.name);
20
+ this.operations = [];
21
+ this.maxOperations = 10000;
22
+ this.maxAge = 3600000;
23
+ setInterval(() => this.cleanupOldRecords(), 60000);
24
+ }
25
+ recordOperation(type, layer, hit, latency, error, key) {
26
+ const record = {
27
+ type,
28
+ layer,
29
+ hit,
30
+ latency,
31
+ error,
32
+ key,
33
+ timestamp: new Date(),
34
+ };
35
+ this.operations.push(record);
36
+ if (this.operations.length > this.maxOperations) {
37
+ this.operations.splice(0, this.operations.length - this.maxOperations);
38
+ }
39
+ this.emit('operation', record);
40
+ if (error) {
41
+ this.logger.warn(`Cache operation error: ${type} on ${layer} - ${error}`);
42
+ }
43
+ }
44
+ getMetrics(timeWindowMs = 300000) {
45
+ const endTime = new Date();
46
+ const startTime = new Date(endTime.getTime() - timeWindowMs);
47
+ const recentOperations = this.operations.filter((op) => op.timestamp >= startTime && op.timestamp <= endTime);
48
+ if (recentOperations.length === 0) {
49
+ return this.createEmptyMetrics(startTime, endTime);
50
+ }
51
+ const hits = recentOperations.filter((op) => op.hit).length;
52
+ const misses = recentOperations.filter((op) => !op.hit).length;
53
+ const errors = recentOperations.filter((op) => op.error).length;
54
+ const latencies = recentOperations
55
+ .map((op) => op.latency)
56
+ .sort((a, b) => a - b);
57
+ const gets = recentOperations.filter((op) => op.type === 'get').length;
58
+ const sets = recentOperations.filter((op) => op.type === 'set').length;
59
+ const deletes = recentOperations.filter((op) => op.type === 'delete').length;
60
+ const clears = recentOperations.filter((op) => op.type === 'clear').length;
61
+ const layerOperations = {
62
+ cls: recentOperations.filter((op) => op.layer === 'cls'),
63
+ memory: recentOperations.filter((op) => op.layer === 'memory'),
64
+ redis: recentOperations.filter((op) => op.layer === 'redis'),
65
+ };
66
+ const layerMetrics = {
67
+ cls: this.calculateLayerMetrics(layerOperations.cls),
68
+ memory: this.calculateLayerMetrics(layerOperations.memory),
69
+ redis: this.calculateLayerMetrics(layerOperations.redis),
70
+ };
71
+ return {
72
+ totalOperations: recentOperations.length,
73
+ hits,
74
+ misses,
75
+ hitRate: recentOperations.length > 0 ? hits / recentOperations.length : 0,
76
+ avgLatency: this.average(latencies),
77
+ minLatency: latencies.length > 0 ? latencies[0] : 0,
78
+ maxLatency: latencies.length > 0 ? latencies[latencies.length - 1] : 0,
79
+ p95Latency: this.percentile(latencies, 0.95),
80
+ p99Latency: this.percentile(latencies, 0.99),
81
+ gets,
82
+ sets,
83
+ deletes,
84
+ clears,
85
+ totalKeys: this.getUniqueKeyCount(recentOperations),
86
+ totalSize: this.estimateTotalSize(recentOperations),
87
+ errors,
88
+ errorRate: recentOperations.length > 0 ? errors / recentOperations.length : 0,
89
+ layerMetrics,
90
+ timeWindow: {
91
+ start: startTime,
92
+ end: endTime,
93
+ duration: timeWindowMs,
94
+ },
95
+ };
96
+ }
97
+ getSnapshot() {
98
+ const now = new Date();
99
+ const oneMinuteAgo = new Date(now.getTime() - 60000);
100
+ const recentOperations = this.operations.filter((op) => op.timestamp >= oneMinuteAgo);
101
+ if (recentOperations.length === 0) {
102
+ return {
103
+ totalOperations: 0,
104
+ hits: 0,
105
+ misses: 0,
106
+ hitRate: 0,
107
+ avgLatency: 0,
108
+ errors: 0,
109
+ errorRate: 0,
110
+ };
111
+ }
112
+ const hits = recentOperations.filter((op) => op.hit).length;
113
+ const errors = recentOperations.filter((op) => op.error).length;
114
+ const latencies = recentOperations.map((op) => op.latency);
115
+ return {
116
+ totalOperations: recentOperations.length,
117
+ hits,
118
+ misses: recentOperations.length - hits,
119
+ hitRate: hits / recentOperations.length,
120
+ avgLatency: this.average(latencies),
121
+ minLatency: Math.min(...latencies),
122
+ maxLatency: Math.max(...latencies),
123
+ errors,
124
+ errorRate: errors / recentOperations.length,
125
+ };
126
+ }
127
+ getSlowOperations(limit = 10) {
128
+ return this.operations
129
+ .filter((op) => op.error === undefined)
130
+ .sort((a, b) => b.latency - a.latency)
131
+ .slice(0, limit);
132
+ }
133
+ getMostAccessedKeys(limit = 10) {
134
+ const keyStats = new Map();
135
+ this.operations.forEach((op) => {
136
+ if (op.key) {
137
+ const stats = keyStats.get(op.key) || { count: 0, totalLatency: 0 };
138
+ stats.count++;
139
+ stats.totalLatency += op.latency;
140
+ keyStats.set(op.key, stats);
141
+ }
142
+ });
143
+ return Array.from(keyStats.entries())
144
+ .map(([key, stats]) => ({
145
+ key,
146
+ count: stats.count,
147
+ avgLatency: stats.totalLatency / stats.count,
148
+ }))
149
+ .sort((a, b) => b.count - a.count)
150
+ .slice(0, limit);
151
+ }
152
+ getErrorBreakdown() {
153
+ const errorStats = new Map();
154
+ this.operations.forEach((op) => {
155
+ if (op.error) {
156
+ const stats = errorStats.get(op.error) || {
157
+ count: 0,
158
+ lastOccurrence: op.timestamp,
159
+ };
160
+ stats.count++;
161
+ stats.lastOccurrence =
162
+ op.timestamp > stats.lastOccurrence
163
+ ? op.timestamp
164
+ : stats.lastOccurrence;
165
+ errorStats.set(op.error, stats);
166
+ }
167
+ });
168
+ return Array.from(errorStats.entries())
169
+ .map(([error, stats]) => ({
170
+ error,
171
+ count: stats.count,
172
+ lastOccurrence: stats.lastOccurrence,
173
+ }))
174
+ .sort((a, b) => b.count - a.count);
175
+ }
176
+ resetMetrics() {
177
+ this.operations.length = 0;
178
+ this.logger.log('Cache metrics reset');
179
+ this.emit('reset');
180
+ }
181
+ exportMetrics(format = 'json') {
182
+ const metrics = this.getMetrics();
183
+ if (format === 'prometheus') {
184
+ return this.toPrometheusFormat(metrics);
185
+ }
186
+ return JSON.stringify(metrics, null, 2);
187
+ }
188
+ cleanupOldRecords() {
189
+ const cutoff = new Date(Date.now() - this.maxAge);
190
+ const initialLength = this.operations.length;
191
+ for (let i = this.operations.length - 1; i >= 0; i--) {
192
+ if (this.operations[i].timestamp < cutoff) {
193
+ this.operations.splice(0, i + 1);
194
+ break;
195
+ }
196
+ }
197
+ const removed = initialLength - this.operations.length;
198
+ if (removed > 0) {
199
+ this.logger.debug(`Cleaned up ${removed} old cache operation records`);
200
+ }
201
+ }
202
+ createEmptyMetrics(start, end) {
203
+ return {
204
+ totalOperations: 0,
205
+ hits: 0,
206
+ misses: 0,
207
+ hitRate: 0,
208
+ avgLatency: 0,
209
+ minLatency: 0,
210
+ maxLatency: 0,
211
+ p95Latency: 0,
212
+ p99Latency: 0,
213
+ gets: 0,
214
+ sets: 0,
215
+ deletes: 0,
216
+ clears: 0,
217
+ totalKeys: 0,
218
+ totalSize: 0,
219
+ errors: 0,
220
+ errorRate: 0,
221
+ layerMetrics: {
222
+ cls: { hits: 0, misses: 0, avgLatency: 0 },
223
+ memory: { hits: 0, misses: 0, avgLatency: 0, size: 0 },
224
+ redis: { hits: 0, misses: 0, avgLatency: 0 },
225
+ },
226
+ timeWindow: {
227
+ start,
228
+ end,
229
+ duration: end.getTime() - start.getTime(),
230
+ },
231
+ };
232
+ }
233
+ calculateLayerMetrics(operations) {
234
+ const hits = operations.filter((op) => op.hit).length;
235
+ const misses = operations.filter((op) => !op.hit).length;
236
+ const latencies = operations.map((op) => op.latency);
237
+ return {
238
+ hits,
239
+ misses,
240
+ avgLatency: latencies.length > 0 ? this.average(latencies) : 0,
241
+ size: operations.length,
242
+ };
243
+ }
244
+ average(numbers) {
245
+ return numbers.length > 0
246
+ ? numbers.reduce((sum, num) => sum + num, 0) / numbers.length
247
+ : 0;
248
+ }
249
+ percentile(sortedNumbers, p) {
250
+ if (sortedNumbers.length === 0)
251
+ return 0;
252
+ const index = Math.ceil(sortedNumbers.length * p) - 1;
253
+ return sortedNumbers[Math.max(0, Math.min(index, sortedNumbers.length - 1))];
254
+ }
255
+ getUniqueKeyCount(operations) {
256
+ const keys = new Set();
257
+ operations.forEach((op) => {
258
+ if (op.key)
259
+ keys.add(op.key);
260
+ });
261
+ return keys.size;
262
+ }
263
+ estimateTotalSize(operations) {
264
+ const avgValueSize = 120;
265
+ return this.getUniqueKeyCount(operations) * avgValueSize;
266
+ }
267
+ toPrometheusFormat(metrics) {
268
+ const labels = {
269
+ layer: 'cls|memory|redis',
270
+ };
271
+ let output = '';
272
+ output += `# HELP cache_hit_rate Cache hit rate\n`;
273
+ output += `# TYPE cache_hit_rate gauge\n`;
274
+ output += `cache_hit_rate ${metrics.hitRate}\n`;
275
+ output += `# HELP cache_operations_total Total cache operations\n`;
276
+ output += `# TYPE cache_operations_total counter\n`;
277
+ output += `cache_operations_total{type="get"} ${metrics.gets}\n`;
278
+ output += `cache_operations_total{type="set"} ${metrics.sets}\n`;
279
+ output += `cache_operations_total{type="delete"} ${metrics.deletes}\n`;
280
+ output += `cache_operations_total{type="clear"} ${metrics.clears}\n`;
281
+ output += `# HELP cache_latency_ms Cache operation latency\n`;
282
+ output += `# TYPE cache_latency_ms histogram\n`;
283
+ output += `cache_latency_ms{quantile="0.5"} ${metrics.avgLatency}\n`;
284
+ output += `cache_latency_ms{quantile="0.95"} ${metrics.p95Latency}\n`;
285
+ output += `cache_latency_ms{quantile="0.99"} ${metrics.p99Latency}\n`;
286
+ Object.entries(metrics.layerMetrics).forEach(([layer, layerMetrics]) => {
287
+ output += `cache_layer_hits_total{layer="${layer}"} ${layerMetrics.hits}\n`;
288
+ output += `cache_layer_misses_total{layer="${layer}"} ${layerMetrics.misses}\n`;
289
+ output += `cache_layer_latency_ms{layer="${layer}"} ${layerMetrics.avgLatency}\n`;
290
+ });
291
+ return output;
292
+ }
293
+ };
294
+ exports.CacheMetricsService = CacheMetricsService;
295
+ exports.CacheMetricsService = CacheMetricsService = CacheMetricsService_1 = __decorate([
296
+ (0, common_1.Injectable)(),
297
+ __metadata("design:paramtypes", [])
298
+ ], CacheMetricsService);
@@ -0,0 +1,29 @@
1
+ export interface CacheSerializationOptions {
2
+ compress?: boolean;
3
+ compressThreshold?: number;
4
+ serializer?: (value: any) => string;
5
+ deserializer?: (data: string) => any;
6
+ preserveTypes?: boolean;
7
+ }
8
+ export interface SerializationResult {
9
+ data: Buffer;
10
+ compressed: boolean;
11
+ originalSize: number;
12
+ serializedSize: number;
13
+ }
14
+ export interface DeserializationResult<T> {
15
+ data: T;
16
+ wasCompressed: boolean;
17
+ wasSerialized: boolean;
18
+ }
19
+ export declare class CacheSerializationService {
20
+ private readonly logger;
21
+ serialize<T>(value: T, options?: CacheSerializationOptions): Promise<SerializationResult>;
22
+ deserialize<T>(buffer: Buffer, options?: CacheSerializationOptions): Promise<DeserializationResult<T>>;
23
+ getSerializationStats(value: any, options?: CacheSerializationOptions): any;
24
+ validateSerializedData(buffer: Buffer): boolean;
25
+ getOptimalOptions(value: any): CacheSerializationOptions;
26
+ private preserveTypeSerialization;
27
+ private preserveTypeDeserialization;
28
+ private hasComplexTypes;
29
+ }
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
9
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
10
+ return new (P || (P = Promise))(function (resolve, reject) {
11
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
12
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
13
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
14
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
15
+ });
16
+ };
17
+ var CacheSerializationService_1;
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.CacheSerializationService = void 0;
20
+ const common_1 = require("@nestjs/common");
21
+ const zlib_1 = require("zlib");
22
+ const util_1 = require("util");
23
+ const gzipAsync = (0, util_1.promisify)(zlib_1.gzip);
24
+ const gunzipAsync = (0, util_1.promisify)(zlib_1.gunzip);
25
+ let CacheSerializationService = CacheSerializationService_1 = class CacheSerializationService {
26
+ constructor() {
27
+ this.logger = new common_1.Logger(CacheSerializationService_1.name);
28
+ }
29
+ serialize(value_1) {
30
+ return __awaiter(this, arguments, void 0, function* (value, options = {}) {
31
+ const { compress = false, compressThreshold = 1024, serializer, preserveTypes = true, } = options;
32
+ try {
33
+ let serializedData;
34
+ let wasSerialized = false;
35
+ if (serializer) {
36
+ serializedData = serializer(value);
37
+ wasSerialized = true;
38
+ }
39
+ else {
40
+ serializedData = preserveTypes
41
+ ? this.preserveTypeSerialization(value)
42
+ : JSON.stringify(value);
43
+ wasSerialized = true;
44
+ }
45
+ const originalSize = Buffer.byteLength(serializedData, 'utf8');
46
+ let data;
47
+ let wasCompressed = false;
48
+ if (compress && originalSize >= compressThreshold) {
49
+ try {
50
+ data = yield gzipAsync(Buffer.from(serializedData, 'utf8'));
51
+ wasCompressed = true;
52
+ }
53
+ catch (error) {
54
+ this.logger.warn('Compression failed, using uncompressed data:', error);
55
+ data = Buffer.from(serializedData, 'utf8');
56
+ }
57
+ }
58
+ else {
59
+ data = Buffer.from(serializedData, 'utf8');
60
+ }
61
+ return {
62
+ data,
63
+ compressed: wasCompressed,
64
+ originalSize,
65
+ serializedSize: data.length,
66
+ };
67
+ }
68
+ catch (error) {
69
+ this.logger.error('Serialization failed:', error);
70
+ throw new Error(`Failed to serialize cache value: ${error instanceof Error ? error.message : String(error)}`);
71
+ }
72
+ });
73
+ }
74
+ deserialize(buffer_1) {
75
+ return __awaiter(this, arguments, void 0, function* (buffer, options = {}) {
76
+ const { deserializer, preserveTypes = true } = options;
77
+ try {
78
+ let dataString;
79
+ let wasCompressed = false;
80
+ if (buffer.length >= 2 && buffer[0] === 0x1f && buffer[1] === 0x8b) {
81
+ dataString = (yield gunzipAsync(buffer)).toString('utf8');
82
+ wasCompressed = true;
83
+ }
84
+ else {
85
+ dataString = buffer.toString('utf8');
86
+ }
87
+ let data;
88
+ let wasSerialized = true;
89
+ if (deserializer) {
90
+ data = deserializer(dataString);
91
+ }
92
+ else {
93
+ data = preserveTypes
94
+ ? this.preserveTypeDeserialization(dataString)
95
+ : JSON.parse(dataString);
96
+ }
97
+ return {
98
+ data,
99
+ wasCompressed,
100
+ wasSerialized,
101
+ };
102
+ }
103
+ catch (error) {
104
+ this.logger.error('Deserialization failed:', error);
105
+ throw new Error(`Failed to deserialize cache value: ${error instanceof Error ? error.message : String(error)}`);
106
+ }
107
+ });
108
+ }
109
+ getSerializationStats(value, options = {}) {
110
+ const valueStr = JSON.stringify(value);
111
+ const valueSize = Buffer.byteLength(valueStr, 'utf8');
112
+ return {
113
+ originalSize: valueSize,
114
+ estimatedCompressedSize: Math.floor(valueSize * 0.6),
115
+ recommendedCompression: valueSize >= (options.compressThreshold || 1024),
116
+ hasComplexTypes: this.hasComplexTypes(value),
117
+ };
118
+ }
119
+ validateSerializedData(buffer) {
120
+ if (!Buffer.isBuffer(buffer)) {
121
+ return false;
122
+ }
123
+ if (buffer.length === 0) {
124
+ return false;
125
+ }
126
+ if (!(buffer.length >= 2 && buffer[0] === 0x1f && buffer[1] === 0x8b)) {
127
+ try {
128
+ buffer.toString('utf8');
129
+ return true;
130
+ }
131
+ catch (_a) {
132
+ return false;
133
+ }
134
+ }
135
+ return true;
136
+ }
137
+ getOptimalOptions(value) {
138
+ const stats = this.getSerializationStats(value);
139
+ const options = {};
140
+ if (stats.recommendedCompression) {
141
+ options.compress = true;
142
+ options.compressThreshold = 1024;
143
+ }
144
+ if (stats.hasComplexTypes) {
145
+ options.preserveTypes = true;
146
+ }
147
+ return options;
148
+ }
149
+ preserveTypeSerialization(value) {
150
+ return JSON.stringify(value, (key, val) => {
151
+ if (val instanceof Date) {
152
+ return { __type: 'Date', value: val.toISOString() };
153
+ }
154
+ if (val instanceof RegExp) {
155
+ return { __type: 'RegExp', value: val.source, flags: val.flags };
156
+ }
157
+ if (val instanceof Set) {
158
+ return { __type: 'Set', value: Array.from(val) };
159
+ }
160
+ if (val instanceof Map) {
161
+ return { __type: 'Map', value: Array.from(val.entries()) };
162
+ }
163
+ if (typeof val === 'bigint') {
164
+ return { __type: 'BigInt', value: val.toString() };
165
+ }
166
+ if (Buffer.isBuffer(val)) {
167
+ return { __type: 'Buffer', value: Array.from(val) };
168
+ }
169
+ return val;
170
+ });
171
+ }
172
+ preserveTypeDeserialization(dataString) {
173
+ return JSON.parse(dataString, (key, val) => {
174
+ if (val && typeof val === 'object' && val.__type) {
175
+ switch (val.__type) {
176
+ case 'Date':
177
+ return new Date(val.value);
178
+ case 'RegExp':
179
+ return new RegExp(val.value, val.flags);
180
+ case 'Set':
181
+ return new Set(val.value);
182
+ case 'Map':
183
+ return new Map(val.value);
184
+ case 'BigInt':
185
+ return BigInt(val.value);
186
+ case 'Buffer':
187
+ return Buffer.from(val.value);
188
+ default:
189
+ return val;
190
+ }
191
+ }
192
+ return val;
193
+ });
194
+ }
195
+ hasComplexTypes(value) {
196
+ if (value instanceof Date ||
197
+ value instanceof RegExp ||
198
+ value instanceof Set ||
199
+ value instanceof Map) {
200
+ return true;
201
+ }
202
+ if (Buffer.isBuffer(value) || typeof value === 'bigint') {
203
+ return true;
204
+ }
205
+ if (Array.isArray(value)) {
206
+ return value.some((item) => this.hasComplexTypes(item));
207
+ }
208
+ if (value && typeof value === 'object') {
209
+ return Object.values(value).some((item) => this.hasComplexTypes(item));
210
+ }
211
+ return false;
212
+ }
213
+ };
214
+ exports.CacheSerializationService = CacheSerializationService;
215
+ exports.CacheSerializationService = CacheSerializationService = CacheSerializationService_1 = __decorate([
216
+ (0, common_1.Injectable)()
217
+ ], CacheSerializationService);
@@ -0,0 +1,35 @@
1
+ import { CacheService } from './cache.service';
2
+ export interface CacheHealthResult {
3
+ status: 'healthy' | 'degraded' | 'unhealthy';
4
+ layers: {
5
+ cls: {
6
+ available: boolean;
7
+ latency?: number;
8
+ error?: string;
9
+ };
10
+ memory: {
11
+ available: boolean;
12
+ latency?: number;
13
+ size?: number;
14
+ error?: string;
15
+ };
16
+ redis: {
17
+ available: boolean;
18
+ latency?: number;
19
+ error?: string;
20
+ };
21
+ };
22
+ timestamp: Date;
23
+ overallLatency: number;
24
+ }
25
+ export declare class CacheHealthChecker {
26
+ private readonly cacheService;
27
+ private readonly logger;
28
+ constructor(cacheService: CacheService);
29
+ checkHealth(): Promise<CacheHealthResult>;
30
+ getDetailedStats(): Promise<any>;
31
+ private checkClsHealth;
32
+ private checkMemoryHealth;
33
+ private checkRedisHealth;
34
+ private generateRecommendations;
35
+ }