@naman_deep_singh/communication-core 1.0.0

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 (134) hide show
  1. package/README.md +345 -0
  2. package/dist/cjs/abstract/BaseCircuitBreaker.js +253 -0
  3. package/dist/cjs/abstract/BaseClient.js +298 -0
  4. package/dist/cjs/abstract/BaseCompressionManager.js +377 -0
  5. package/dist/cjs/abstract/BaseConnectionPool.js +543 -0
  6. package/dist/cjs/abstract/BaseInterceptor.js +235 -0
  7. package/dist/cjs/abstract/BaseLoadBalancer.js +269 -0
  8. package/dist/cjs/abstract/BaseProtocol.js +269 -0
  9. package/dist/cjs/abstract/BaseRetryStrategy.js +255 -0
  10. package/dist/cjs/abstract/BaseSerializer.js +341 -0
  11. package/dist/cjs/abstract/BaseServiceDiscoverer.js +254 -0
  12. package/dist/cjs/abstract/BaseTimeoutManager.js +295 -0
  13. package/dist/cjs/abstract/index.js +25 -0
  14. package/dist/cjs/errors/CircuitBreakerError.js +16 -0
  15. package/dist/cjs/errors/CommunicationError.js +15 -0
  16. package/dist/cjs/errors/ConnectionError.js +15 -0
  17. package/dist/cjs/errors/DiscoveryError.js +15 -0
  18. package/dist/cjs/errors/LoadBalancerError.js +15 -0
  19. package/dist/cjs/errors/ProtocolError.js +15 -0
  20. package/dist/cjs/errors/RetryError.js +16 -0
  21. package/dist/cjs/errors/SerializationError.js +15 -0
  22. package/dist/cjs/errors/ServiceUnavailableError.js +15 -0
  23. package/dist/cjs/errors/TimeoutError.js +16 -0
  24. package/dist/cjs/errors/communicationErrorCodes.js +35 -0
  25. package/dist/cjs/errors/index.js +31 -0
  26. package/dist/cjs/index.js +38 -0
  27. package/dist/cjs/interfaces/CircuitBreaker.interface.js +6 -0
  28. package/dist/cjs/interfaces/Client.interface.js +6 -0
  29. package/dist/cjs/interfaces/Compression.interface.js +6 -0
  30. package/dist/cjs/interfaces/ConnectionPool.interface.js +6 -0
  31. package/dist/cjs/interfaces/Interceptor.interface.js +6 -0
  32. package/dist/cjs/interfaces/LoadBalancer.interface.js +2 -0
  33. package/dist/cjs/interfaces/Protocol.interface.js +6 -0
  34. package/dist/cjs/interfaces/RetryStrategy.interface.js +6 -0
  35. package/dist/cjs/interfaces/Serializer.interface.js +2 -0
  36. package/dist/cjs/interfaces/ServiceDiscovery.interface.js +6 -0
  37. package/dist/cjs/interfaces/Timeout.interface.js +6 -0
  38. package/dist/cjs/interfaces/index.js +6 -0
  39. package/dist/cjs/types/config.js +6 -0
  40. package/dist/cjs/types/events.js +6 -0
  41. package/dist/cjs/types/index.js +6 -0
  42. package/dist/cjs/types/request.js +6 -0
  43. package/dist/cjs/types/response.js +6 -0
  44. package/dist/cjs/types/service.js +6 -0
  45. package/dist/cjs/utils.js +200 -0
  46. package/dist/esm/abstract/BaseCircuitBreaker.js +249 -0
  47. package/dist/esm/abstract/BaseClient.js +294 -0
  48. package/dist/esm/abstract/BaseCompressionManager.js +373 -0
  49. package/dist/esm/abstract/BaseConnectionPool.js +539 -0
  50. package/dist/esm/abstract/BaseInterceptor.js +231 -0
  51. package/dist/esm/abstract/BaseLoadBalancer.js +265 -0
  52. package/dist/esm/abstract/BaseProtocol.js +265 -0
  53. package/dist/esm/abstract/BaseRetryStrategy.js +251 -0
  54. package/dist/esm/abstract/BaseSerializer.js +337 -0
  55. package/dist/esm/abstract/BaseServiceDiscoverer.js +250 -0
  56. package/dist/esm/abstract/BaseTimeoutManager.js +291 -0
  57. package/dist/esm/abstract/index.js +11 -0
  58. package/dist/esm/errors/CircuitBreakerError.js +12 -0
  59. package/dist/esm/errors/CommunicationError.js +11 -0
  60. package/dist/esm/errors/ConnectionError.js +11 -0
  61. package/dist/esm/errors/DiscoveryError.js +11 -0
  62. package/dist/esm/errors/LoadBalancerError.js +11 -0
  63. package/dist/esm/errors/ProtocolError.js +11 -0
  64. package/dist/esm/errors/RetryError.js +12 -0
  65. package/dist/esm/errors/SerializationError.js +11 -0
  66. package/dist/esm/errors/ServiceUnavailableError.js +11 -0
  67. package/dist/esm/errors/TimeoutError.js +12 -0
  68. package/dist/esm/errors/communicationErrorCodes.js +32 -0
  69. package/dist/esm/errors/index.js +17 -0
  70. package/dist/esm/index.js +18 -0
  71. package/dist/esm/interfaces/CircuitBreaker.interface.js +5 -0
  72. package/dist/esm/interfaces/Client.interface.js +5 -0
  73. package/dist/esm/interfaces/Compression.interface.js +5 -0
  74. package/dist/esm/interfaces/ConnectionPool.interface.js +5 -0
  75. package/dist/esm/interfaces/Interceptor.interface.js +5 -0
  76. package/dist/esm/interfaces/LoadBalancer.interface.js +1 -0
  77. package/dist/esm/interfaces/Protocol.interface.js +5 -0
  78. package/dist/esm/interfaces/RetryStrategy.interface.js +5 -0
  79. package/dist/esm/interfaces/Serializer.interface.js +1 -0
  80. package/dist/esm/interfaces/ServiceDiscovery.interface.js +5 -0
  81. package/dist/esm/interfaces/Timeout.interface.js +5 -0
  82. package/dist/esm/interfaces/index.js +5 -0
  83. package/dist/esm/types/config.js +5 -0
  84. package/dist/esm/types/events.js +5 -0
  85. package/dist/esm/types/index.js +5 -0
  86. package/dist/esm/types/request.js +5 -0
  87. package/dist/esm/types/response.js +5 -0
  88. package/dist/esm/types/service.js +5 -0
  89. package/dist/esm/utils.js +193 -0
  90. package/dist/types/abstract/BaseCircuitBreaker.d.ts +167 -0
  91. package/dist/types/abstract/BaseClient.d.ts +197 -0
  92. package/dist/types/abstract/BaseCompressionManager.d.ts +180 -0
  93. package/dist/types/abstract/BaseConnectionPool.d.ts +210 -0
  94. package/dist/types/abstract/BaseInterceptor.d.ts +150 -0
  95. package/dist/types/abstract/BaseLoadBalancer.d.ts +167 -0
  96. package/dist/types/abstract/BaseProtocol.d.ts +163 -0
  97. package/dist/types/abstract/BaseRetryStrategy.d.ts +130 -0
  98. package/dist/types/abstract/BaseSerializer.d.ts +181 -0
  99. package/dist/types/abstract/BaseServiceDiscoverer.d.ts +161 -0
  100. package/dist/types/abstract/BaseTimeoutManager.d.ts +145 -0
  101. package/dist/types/abstract/index.d.ts +11 -0
  102. package/dist/types/errors/CircuitBreakerError.d.ts +8 -0
  103. package/dist/types/errors/CommunicationError.d.ts +10 -0
  104. package/dist/types/errors/ConnectionError.d.ts +9 -0
  105. package/dist/types/errors/DiscoveryError.d.ts +9 -0
  106. package/dist/types/errors/LoadBalancerError.d.ts +9 -0
  107. package/dist/types/errors/ProtocolError.d.ts +9 -0
  108. package/dist/types/errors/RetryError.d.ts +11 -0
  109. package/dist/types/errors/SerializationError.d.ts +9 -0
  110. package/dist/types/errors/ServiceUnavailableError.d.ts +12 -0
  111. package/dist/types/errors/TimeoutError.d.ts +11 -0
  112. package/dist/types/errors/communicationErrorCodes.d.ts +27 -0
  113. package/dist/types/errors/index.d.ts +11 -0
  114. package/dist/types/index.d.ts +13 -0
  115. package/dist/types/interfaces/CircuitBreaker.interface.d.ts +150 -0
  116. package/dist/types/interfaces/Client.interface.d.ts +153 -0
  117. package/dist/types/interfaces/Compression.interface.d.ts +190 -0
  118. package/dist/types/interfaces/ConnectionPool.interface.d.ts +191 -0
  119. package/dist/types/interfaces/Interceptor.interface.d.ts +220 -0
  120. package/dist/types/interfaces/LoadBalancer.interface.d.ts +153 -0
  121. package/dist/types/interfaces/Protocol.interface.d.ts +117 -0
  122. package/dist/types/interfaces/RetryStrategy.interface.d.ts +160 -0
  123. package/dist/types/interfaces/Serializer.interface.d.ts +176 -0
  124. package/dist/types/interfaces/ServiceDiscovery.interface.d.ts +189 -0
  125. package/dist/types/interfaces/Timeout.interface.d.ts +135 -0
  126. package/dist/types/interfaces/index.d.ts +15 -0
  127. package/dist/types/types/config.d.ts +540 -0
  128. package/dist/types/types/events.d.ts +204 -0
  129. package/dist/types/types/index.d.ts +9 -0
  130. package/dist/types/types/request.d.ts +143 -0
  131. package/dist/types/types/response.d.ts +155 -0
  132. package/dist/types/types/service.d.ts +279 -0
  133. package/dist/types/utils.d.ts +179 -0
  134. package/package.json +88 -0
@@ -0,0 +1,294 @@
1
+ /**
2
+ * Abstract base client implementation
3
+ * @packageDocumentation
4
+ */
5
+ import { CommunicationError } from '../errors/CommunicationError.js';
6
+ import { COMMUNICATION_ERROR_CODES } from '../errors/communicationErrorCodes.js';
7
+ /**
8
+ * Abstract base client implementation
9
+ * Provides common functionality for all client implementations
10
+ */
11
+ export class BaseClient {
12
+ /**
13
+ * Create a new base client instance
14
+ * @param serviceName Service name to communicate with
15
+ * @param config Client configuration
16
+ * @param protocol Protocol instance
17
+ */
18
+ constructor(serviceName, config, protocol) {
19
+ /** Client interceptors */
20
+ this.interceptors = [];
21
+ /** Cached service instances */
22
+ this.cachedInstances = [];
23
+ /** Client metrics */
24
+ this.metrics = {};
25
+ /** Client start time */
26
+ this.startTime = Date.now();
27
+ /** Total calls made */
28
+ this.totalCalls = 0;
29
+ /** Successful calls */
30
+ this.successfulCalls = 0;
31
+ /** Failed calls */
32
+ this.failedCalls = 0;
33
+ this.serviceName = serviceName;
34
+ this.config = { ...config };
35
+ this.protocol = protocol;
36
+ this.name = config.custom?.name || `client_${serviceName}_${Date.now()}`;
37
+ // Initialize components
38
+ this.initializeComponents();
39
+ }
40
+ /**
41
+ * Initialize client components
42
+ */
43
+ initializeComponents() {
44
+ // Can be overridden by subclasses to initialize
45
+ // serviceDiscoverer, loadBalancer, circuitBreaker, retryStrategy
46
+ }
47
+ /**
48
+ * Get service discovery instance
49
+ */
50
+ getServiceDiscoverer() {
51
+ return this.serviceDiscoverer;
52
+ }
53
+ /**
54
+ * Get load balancer instance
55
+ */
56
+ getLoadBalancer() {
57
+ return this.loadBalancer;
58
+ }
59
+ /**
60
+ * Get circuit breaker instance
61
+ */
62
+ getCircuitBreaker() {
63
+ return this.circuitBreaker;
64
+ }
65
+ /**
66
+ * Get retry strategy instance
67
+ */
68
+ getRetryStrategy() {
69
+ return this.retryStrategy;
70
+ }
71
+ /**
72
+ * Add an interceptor to the client
73
+ * @param interceptor IInterceptor to add
74
+ */
75
+ addInterceptor(interceptor) {
76
+ this.interceptors.push(interceptor);
77
+ interceptor.initialize?.({ client: this.name, service: this.serviceName });
78
+ this.onInterceptorAdded(interceptor);
79
+ }
80
+ /**
81
+ * Hook for interceptor addition
82
+ * @param interceptor Added interceptor
83
+ */
84
+ onInterceptorAdded(interceptor) {
85
+ // Can be overridden by subclasses
86
+ }
87
+ /**
88
+ * Remove an interceptor from the client
89
+ * @param interceptorId IInterceptor identifier
90
+ */
91
+ removeInterceptor(interceptorId) {
92
+ const index = this.interceptors.findIndex(i => i.name === interceptorId);
93
+ if (index !== -1) {
94
+ const interceptor = this.interceptors[index];
95
+ interceptor.cleanup?.();
96
+ this.interceptors.splice(index, 1);
97
+ this.onInterceptorRemoved(interceptorId);
98
+ }
99
+ }
100
+ /**
101
+ * Hook for interceptor removal
102
+ * @param interceptorId Removed interceptor ID
103
+ */
104
+ onInterceptorRemoved(interceptorId) {
105
+ // Can be overridden by subclasses
106
+ }
107
+ /**
108
+ * Get all interceptors
109
+ */
110
+ getInterceptors() {
111
+ return [...this.interceptors];
112
+ }
113
+ /**
114
+ * Clear all interceptors
115
+ */
116
+ clearInterceptors() {
117
+ for (const interceptor of this.interceptors) {
118
+ interceptor.cleanup?.();
119
+ }
120
+ this.interceptors = [];
121
+ this.onInterceptorsCleared();
122
+ }
123
+ /**
124
+ * Hook for interceptors cleared
125
+ */
126
+ onInterceptorsCleared() {
127
+ // Can be overridden by subclasses
128
+ }
129
+ /**
130
+ * Discover service instances
131
+ * @returns Promise resolving to service instances
132
+ */
133
+ async discoverService() {
134
+ if (!this.serviceDiscoverer) {
135
+ throw new CommunicationError(COMMUNICATION_ERROR_CODES.DISCOVERY_ERROR, 503, {
136
+ message: 'Service discovery not configured',
137
+ service: this.serviceName,
138
+ });
139
+ }
140
+ const result = await this.serviceDiscoverer.resolve(this.serviceName);
141
+ this.cachedInstances = result.instances;
142
+ this.lastCacheRefresh = Date.now();
143
+ return this.cachedInstances;
144
+ }
145
+ /**
146
+ * Get current service instances (cached)
147
+ */
148
+ getServiceInstances() {
149
+ return [...this.cachedInstances];
150
+ }
151
+ /**
152
+ * Refresh service instances cache
153
+ */
154
+ async refreshServiceInstances() {
155
+ await this.discoverService();
156
+ }
157
+ /**
158
+ * Select a service instance using load balancer
159
+ * @param instances Available instances
160
+ * @returns Selected instance
161
+ */
162
+ selectInstance(instances) {
163
+ if (instances.length === 0) {
164
+ throw new CommunicationError(COMMUNICATION_ERROR_CODES.NO_AVAILABLE_INSTANCES, 503, {
165
+ service: this.serviceName,
166
+ });
167
+ }
168
+ if (instances.length === 1) {
169
+ return instances[0];
170
+ }
171
+ if (this.loadBalancer) {
172
+ return this.loadBalancer.select(instances, {
173
+ service: this.serviceName,
174
+ client: this.name,
175
+ });
176
+ }
177
+ // Default: round-robin selection
178
+ const index = this.totalCalls % instances.length;
179
+ return instances[index];
180
+ }
181
+ /**
182
+ * Update client metrics
183
+ * @param success Whether call was successful
184
+ * @param duration Call duration in milliseconds
185
+ */
186
+ updateMetrics(success, duration) {
187
+ this.totalCalls++;
188
+ if (success) {
189
+ this.successfulCalls++;
190
+ }
191
+ else {
192
+ this.failedCalls++;
193
+ }
194
+ this.metrics = {
195
+ ...this.metrics,
196
+ totalCalls: this.totalCalls,
197
+ successfulCalls: this.successfulCalls,
198
+ failedCalls: this.failedCalls,
199
+ successRate: this.totalCalls > 0 ? this.successfulCalls / this.totalCalls : 0,
200
+ averageResponseTime: this.calculateAverageResponseTime(duration),
201
+ uptime: Date.now() - this.startTime,
202
+ cachedInstances: this.cachedInstances.length,
203
+ lastCacheRefresh: this.lastCacheRefresh,
204
+ };
205
+ }
206
+ /**
207
+ * Calculate average response time
208
+ * @param newDuration New call duration
209
+ * @returns Average response time
210
+ */
211
+ calculateAverageResponseTime(newDuration) {
212
+ const currentAverage = this.metrics.averageResponseTime || 0;
213
+ const totalDuration = currentAverage * (this.totalCalls - 1) + newDuration;
214
+ return totalDuration / this.totalCalls;
215
+ }
216
+ /**
217
+ * Health check for the client
218
+ * @returns Promise resolving to health status
219
+ */
220
+ async healthCheck() {
221
+ const checks = [];
222
+ // Check protocol health
223
+ const protocolHealth = await this.protocol.healthCheck?.() || { healthy: false };
224
+ checks.push({
225
+ component: 'protocol',
226
+ healthy: protocolHealth.healthy,
227
+ message: protocolHealth.message,
228
+ });
229
+ // Check service discovery health if configured
230
+ if (this.serviceDiscoverer) {
231
+ const discoveryHealth = await this.serviceDiscoverer.healthCheckSelf();
232
+ checks.push({
233
+ component: 'service-discovery',
234
+ healthy: discoveryHealth.healthy,
235
+ message: discoveryHealth.message,
236
+ });
237
+ }
238
+ // Check if we have service instances
239
+ const hasInstances = this.cachedInstances.length > 0;
240
+ checks.push({
241
+ component: 'service-instances',
242
+ healthy: hasInstances,
243
+ message: hasInstances ? `Has ${this.cachedInstances.length} instances` : 'No instances available',
244
+ });
245
+ const allHealthy = checks.every(check => check.healthy);
246
+ return {
247
+ healthy: allHealthy,
248
+ message: allHealthy ? 'Client is healthy' : 'Client has issues',
249
+ details: {
250
+ name: this.name,
251
+ service: this.serviceName,
252
+ checks,
253
+ metrics: this.metrics,
254
+ },
255
+ };
256
+ }
257
+ /**
258
+ * Get client metrics
259
+ */
260
+ getMetrics() {
261
+ return { ...this.metrics };
262
+ }
263
+ /**
264
+ * Reset client metrics
265
+ */
266
+ resetMetrics() {
267
+ this.totalCalls = 0;
268
+ this.successfulCalls = 0;
269
+ this.failedCalls = 0;
270
+ this.metrics = {
271
+ uptime: Date.now() - this.startTime,
272
+ };
273
+ }
274
+ /**
275
+ * Close/cleanup client resources
276
+ */
277
+ async close() {
278
+ // Clear interceptors
279
+ this.clearInterceptors();
280
+ // Close protocol
281
+ await this.protocol.disconnect?.();
282
+ // Close service discovery if configured
283
+ if (this.serviceDiscoverer) {
284
+ await this.serviceDiscoverer.close();
285
+ }
286
+ this.onClose();
287
+ }
288
+ /**
289
+ * Hook for client close
290
+ */
291
+ onClose() {
292
+ // Can be overridden by subclasses
293
+ }
294
+ }
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Abstract base compression manager implementation
3
+ * @packageDocumentation
4
+ */
5
+ import { COMMUNICATION_ERROR_CODES } from '../errors/communicationErrorCodes.js';
6
+ import { SerializationError } from '../errors/SerializationError.js';
7
+ /**
8
+ * Abstract base compression manager implementation
9
+ * Provides common functionality for compression management
10
+ */
11
+ export class BaseCompressionManager {
12
+ /**
13
+ * Create a new base compression manager instance
14
+ * @param name Compression manager name
15
+ * @param config Compression configuration
16
+ */
17
+ constructor(name, config) {
18
+ /** Supported algorithms */
19
+ this.supportedAlgorithms = ['none'];
20
+ /** Compression statistics */
21
+ this.stats = {
22
+ totalCompressions: 0,
23
+ totalDecompressions: 0,
24
+ compressionErrors: 0,
25
+ decompressionErrors: 0,
26
+ totalCompressionTime: 0,
27
+ totalDecompressionTime: 0,
28
+ totalOriginalSize: 0,
29
+ totalCompressedSize: 0,
30
+ totalDecompressedSize: 0,
31
+ averageCompressionTime: 0,
32
+ averageDecompressionTime: 0,
33
+ averageCompressionRatio: 0,
34
+ algorithmUsage: {},
35
+ };
36
+ this.name = name;
37
+ this.config = { ...config };
38
+ this.initialize();
39
+ }
40
+ /**
41
+ * Initialize compression manager
42
+ */
43
+ initialize() {
44
+ // Detect supported algorithms
45
+ this.supportedAlgorithms.push(...this.detectSupportedAlgorithms());
46
+ }
47
+ /**
48
+ * Compress data
49
+ * @param data Data to compress
50
+ * @param options Compression options
51
+ * @returns Promise resolving to compression result
52
+ * @throws {CommunicationError} If compression fails
53
+ */
54
+ async compress(data, options) {
55
+ const result = await this.compressWithMetrics(data, options);
56
+ return result;
57
+ }
58
+ /**
59
+ * Compress data with detailed metrics
60
+ * @param data Data to compress
61
+ * @param options Compression options
62
+ * @returns Promise resolving to compression result with metrics
63
+ */
64
+ async compressWithMetrics(data, options) {
65
+ const startTime = Date.now();
66
+ const algorithm = options?.algorithm || this.config.algorithm || 'gzip';
67
+ const level = options?.level || this.config.level || 6;
68
+ // Convert string to buffer if needed
69
+ const inputBuffer = typeof data === 'string'
70
+ ? Buffer.from(data, 'utf8')
71
+ : Buffer.from(data);
72
+ const originalSize = inputBuffer.length;
73
+ // Check if we should compress
74
+ if (!this.shouldCompress(originalSize)) {
75
+ return {
76
+ data: inputBuffer,
77
+ originalSize,
78
+ compressedSize: originalSize,
79
+ compressionRatio: 1,
80
+ algorithm: 'none',
81
+ level: 0,
82
+ compressionTime: Date.now() - startTime,
83
+ };
84
+ }
85
+ // Check algorithm support
86
+ if (!this.isAlgorithmSupported(algorithm)) {
87
+ throw new SerializationError(COMMUNICATION_ERROR_CODES.SERIALIZATION_ERROR, {
88
+ message: `Compression algorithm not supported: ${algorithm}`,
89
+ algorithm,
90
+ supportedAlgorithms: this.supportedAlgorithms,
91
+ });
92
+ }
93
+ try {
94
+ const compressedData = await this.compressData(inputBuffer, algorithm, level);
95
+ const compressionTime = Date.now() - startTime;
96
+ const compressedSize = compressedData.length;
97
+ const compressionRatio = compressedSize / originalSize;
98
+ // Update statistics
99
+ this.updateCompressionStats(originalSize, compressedSize, compressionTime, algorithm);
100
+ return {
101
+ data: compressedData,
102
+ originalSize,
103
+ compressedSize,
104
+ compressionRatio,
105
+ algorithm,
106
+ level,
107
+ compressionTime,
108
+ };
109
+ }
110
+ catch (error) {
111
+ this.stats.compressionErrors++;
112
+ throw new SerializationError(COMMUNICATION_ERROR_CODES.SERIALIZATION_ERROR, {
113
+ message: `Compression failed: ${error instanceof Error ? error.message : String(error)}`,
114
+ algorithm,
115
+ level,
116
+ originalSize,
117
+ }, error instanceof Error ? error : undefined);
118
+ }
119
+ }
120
+ /**
121
+ * Decompress data
122
+ * @param data Data to decompress
123
+ * @param options Decompression options
124
+ * @returns Promise resolving to decompression result
125
+ * @throws {CommunicationError} If decompression fails
126
+ */
127
+ async decompress(data, options) {
128
+ const startTime = Date.now();
129
+ const algorithm = options?.algorithm || this.detectAlgorithm(data) || 'gzip';
130
+ // Check algorithm support
131
+ if (!this.isAlgorithmSupported(algorithm)) {
132
+ throw new SerializationError(COMMUNICATION_ERROR_CODES.DESERIALIZATION_ERROR, {
133
+ message: `Decompression algorithm not supported: ${algorithm}`,
134
+ algorithm,
135
+ supportedAlgorithms: this.supportedAlgorithms,
136
+ });
137
+ }
138
+ try {
139
+ const decompressedData = await this.decompressData(Buffer.from(data), algorithm);
140
+ const decompressionTime = Date.now() - startTime;
141
+ const originalSize = data.length;
142
+ const decompressedSize = decompressedData.length;
143
+ // Update statistics
144
+ this.updateDecompressionStats(originalSize, decompressedSize, decompressionTime, algorithm);
145
+ return {
146
+ data: decompressedData,
147
+ originalSize,
148
+ decompressedSize,
149
+ decompressionTime,
150
+ algorithm,
151
+ };
152
+ }
153
+ catch (error) {
154
+ this.stats.decompressionErrors++;
155
+ throw new SerializationError(COMMUNICATION_ERROR_CODES.DESERIALIZATION_ERROR, {
156
+ message: `Decompression failed: ${error instanceof Error ? error.message : String(error)}`,
157
+ algorithm,
158
+ originalSize: data.length,
159
+ }, error instanceof Error ? error : undefined);
160
+ }
161
+ }
162
+ /**
163
+ * Detect compression algorithm from data
164
+ * @param data Compressed data
165
+ * @returns Detected algorithm or undefined
166
+ */
167
+ detectAlgorithm(data) {
168
+ const buffer = Buffer.from(data);
169
+ // Check for gzip magic number: 0x1F 0x8B
170
+ if (buffer.length >= 2 && buffer[0] === 0x1F && buffer[1] === 0x8B) {
171
+ return 'gzip';
172
+ }
173
+ // Check for deflate magic number: 0x78 (zlib header)
174
+ if (buffer.length >= 1 && (buffer[0] === 0x78 || buffer[0] === 0x58)) {
175
+ return 'deflate';
176
+ }
177
+ // Check for brotli magic number: 0xCE 0x2B 0x2F
178
+ if (buffer.length >= 3 && buffer[0] === 0xCE && buffer[1] === 0x2B && buffer[2] === 0x2F) {
179
+ return 'brotli';
180
+ }
181
+ return undefined;
182
+ }
183
+ /**
184
+ * Check if algorithm is supported
185
+ * @param algorithm Algorithm to check
186
+ * @returns True if algorithm is supported
187
+ */
188
+ isAlgorithmSupported(algorithm) {
189
+ return this.supportedAlgorithms.includes(algorithm);
190
+ }
191
+ /**
192
+ * Get content encoding header for algorithm
193
+ * @param algorithm Compression algorithm
194
+ * @returns Content-Encoding header value
195
+ */
196
+ getContentEncoding(algorithm) {
197
+ switch (algorithm) {
198
+ case 'gzip':
199
+ return 'gzip';
200
+ case 'deflate':
201
+ return 'deflate';
202
+ case 'brotli':
203
+ return 'br';
204
+ case 'none':
205
+ return 'identity';
206
+ default:
207
+ return algorithm;
208
+ }
209
+ }
210
+ /**
211
+ * Get algorithm from content encoding header
212
+ * @param contentEncoding Content-Encoding header value
213
+ * @returns Compression algorithm or undefined
214
+ */
215
+ getAlgorithmFromEncoding(contentEncoding) {
216
+ const normalized = contentEncoding.toLowerCase().trim();
217
+ switch (normalized) {
218
+ case 'gzip':
219
+ case 'x-gzip':
220
+ return 'gzip';
221
+ case 'deflate':
222
+ return 'deflate';
223
+ case 'br':
224
+ return 'brotli';
225
+ case 'identity':
226
+ case 'none':
227
+ return 'none';
228
+ default:
229
+ return undefined;
230
+ }
231
+ }
232
+ /**
233
+ * Should compress based on configuration
234
+ * @param dataSize Data size in bytes
235
+ * @param contentType Content type
236
+ * @returns True if should compress
237
+ */
238
+ shouldCompress(dataSize, contentType) {
239
+ if (!this.config.enabled) {
240
+ return false;
241
+ }
242
+ // Check minimum size
243
+ if (this.config.minSize && dataSize < this.config.minSize) {
244
+ return false;
245
+ }
246
+ // Check maximum size
247
+ if (this.config.maxSize && dataSize > this.config.maxSize) {
248
+ return false;
249
+ }
250
+ // Check content types
251
+ if (this.config.contentTypes && contentType) {
252
+ const normalizedContentType = contentType.toLowerCase().split(';')[0].trim();
253
+ const shouldCompress = this.config.contentTypes.some(ct => normalizedContentType.includes(ct.toLowerCase()));
254
+ if (!shouldCompress) {
255
+ return false;
256
+ }
257
+ }
258
+ return true;
259
+ }
260
+ /**
261
+ * Create compression stream
262
+ * @param algorithm Compression algorithm
263
+ * @param options Compression options
264
+ * @returns Transform stream for compression
265
+ */
266
+ createCompressionStream(algorithm, options) {
267
+ throw new Error('createCompressionStream not implemented');
268
+ }
269
+ /**
270
+ * Create decompression stream
271
+ * @param algorithm Compression algorithm
272
+ * @returns Transform stream for decompression
273
+ */
274
+ createDecompressionStream(algorithm) {
275
+ throw new Error('createDecompressionStream not implemented');
276
+ }
277
+ /**
278
+ * Update compression statistics
279
+ * @param originalSize Original size in bytes
280
+ * @param compressedSize Compressed size in bytes
281
+ * @param compressionTime Compression time in milliseconds
282
+ * @param algorithm Compression algorithm used
283
+ */
284
+ updateCompressionStats(originalSize, compressedSize, compressionTime, algorithm) {
285
+ this.stats.totalCompressions++;
286
+ this.stats.totalCompressionTime += compressionTime;
287
+ this.stats.totalOriginalSize += originalSize;
288
+ this.stats.totalCompressedSize += compressedSize;
289
+ // Update algorithm usage
290
+ this.stats.algorithmUsage[algorithm] = (this.stats.algorithmUsage[algorithm] || 0) + 1;
291
+ // Update averages
292
+ this.stats.averageCompressionTime = this.stats.totalCompressionTime / this.stats.totalCompressions;
293
+ this.stats.averageCompressionRatio = this.stats.totalCompressedSize / this.stats.totalOriginalSize;
294
+ }
295
+ /**
296
+ * Update decompression statistics
297
+ * @param originalSize Original compressed size in bytes
298
+ * @param decompressedSize Decompressed size in bytes
299
+ * @param decompressionTime Decompression time in milliseconds
300
+ * @param algorithm Decompression algorithm used
301
+ */
302
+ updateDecompressionStats(originalSize, decompressedSize, decompressionTime, algorithm) {
303
+ this.stats.totalDecompressions++;
304
+ this.stats.totalDecompressionTime += decompressionTime;
305
+ this.stats.totalDecompressedSize += decompressedSize;
306
+ // Update algorithm usage
307
+ this.stats.algorithmUsage[algorithm] = (this.stats.algorithmUsage[algorithm] || 0) + 1;
308
+ // Update averages
309
+ this.stats.averageDecompressionTime = this.stats.totalDecompressionTime / this.stats.totalDecompressions;
310
+ }
311
+ /**
312
+ * Update compression configuration
313
+ * @param config New configuration
314
+ */
315
+ updateConfig(config) {
316
+ this.config = { ...this.config, ...config };
317
+ }
318
+ /**
319
+ * Get compression statistics
320
+ */
321
+ getStats() {
322
+ const bytesSaved = this.stats.totalOriginalSize - this.stats.totalCompressedSize;
323
+ return {
324
+ totalCompressions: this.stats.totalCompressions,
325
+ totalDecompressions: this.stats.totalDecompressions,
326
+ compressionErrors: this.stats.compressionErrors,
327
+ decompressionErrors: this.stats.decompressionErrors,
328
+ averageCompressionTime: this.stats.averageCompressionTime,
329
+ averageDecompressionTime: this.stats.averageDecompressionTime,
330
+ averageCompressionRatio: this.stats.averageCompressionRatio,
331
+ algorithmUsage: { ...this.stats.algorithmUsage },
332
+ totalBytesCompressed: this.stats.totalOriginalSize,
333
+ totalBytesDecompressed: this.stats.totalDecompressedSize,
334
+ bytesSaved: Math.max(0, bytesSaved),
335
+ };
336
+ }
337
+ /**
338
+ * Reset compression statistics
339
+ */
340
+ resetStats() {
341
+ this.stats = {
342
+ totalCompressions: 0,
343
+ totalDecompressions: 0,
344
+ compressionErrors: 0,
345
+ decompressionErrors: 0,
346
+ totalCompressionTime: 0,
347
+ totalDecompressionTime: 0,
348
+ totalOriginalSize: 0,
349
+ totalCompressedSize: 0,
350
+ totalDecompressedSize: 0,
351
+ averageCompressionTime: 0,
352
+ averageDecompressionTime: 0,
353
+ averageCompressionRatio: 0,
354
+ algorithmUsage: {},
355
+ };
356
+ }
357
+ /**
358
+ * Health check for compression manager
359
+ */
360
+ healthCheck() {
361
+ const healthy = this.supportedAlgorithms.length > 0;
362
+ return {
363
+ healthy,
364
+ message: healthy ? 'Compression manager is operational' : 'No compression algorithms supported',
365
+ details: {
366
+ name: this.name,
367
+ supportedAlgorithms: this.supportedAlgorithms,
368
+ config: this.config,
369
+ statistics: this.getStats(),
370
+ },
371
+ };
372
+ }
373
+ }