tasq/node_modules/agentic-flow/dist/utils/compression-middleware.js

150 lines
4.7 KiB
JavaScript

/**
* Compression Middleware for Proxy Responses
* Provides 30-70% bandwidth reduction with Brotli/Gzip
*/
import { brotliCompress, gzip, constants } from 'zlib';
import { promisify } from 'util';
import { logger } from './logger.js';
const brotliCompressAsync = promisify(brotliCompress);
const gzipAsync = promisify(gzip);
export class CompressionMiddleware {
config;
constructor(config = {}) {
this.config = {
minSize: config.minSize || 1024, // 1KB minimum
level: config.level ?? constants.BROTLI_DEFAULT_QUALITY,
preferredEncoding: config.preferredEncoding || 'br',
enableBrotli: config.enableBrotli ?? true,
enableGzip: config.enableGzip ?? true
};
}
/**
* Compress data using best available algorithm
*/
async compress(data, acceptedEncodings) {
const startTime = Date.now();
const originalSize = data.length;
// Skip compression for small payloads
if (originalSize < this.config.minSize) {
return {
compressed: data,
encoding: 'identity',
originalSize,
compressedSize: originalSize,
ratio: 1.0,
duration: 0
};
}
// Determine encoding based on accept-encoding header
const encoding = this.selectEncoding(acceptedEncodings);
let compressed;
try {
switch (encoding) {
case 'br':
compressed = await this.compressBrotli(data);
break;
case 'gzip':
compressed = await this.compressGzip(data);
break;
default:
compressed = data;
}
}
catch (error) {
logger.error('Compression failed, using uncompressed', {
error: error.message
});
compressed = data;
}
const duration = Date.now() - startTime;
const compressedSize = compressed.length;
const ratio = compressedSize / originalSize;
logger.debug('Compression complete', {
encoding,
originalSize,
compressedSize,
ratio: `${(ratio * 100).toFixed(2)}%`,
savings: `${((1 - ratio) * 100).toFixed(2)}%`,
duration
});
return {
compressed,
encoding,
originalSize,
compressedSize,
ratio,
duration
};
}
/**
* Compress using Brotli (best compression ratio)
*/
async compressBrotli(data) {
return brotliCompressAsync(data, {
params: {
[constants.BROTLI_PARAM_QUALITY]: this.config.level,
[constants.BROTLI_PARAM_MODE]: constants.BROTLI_MODE_TEXT
}
});
}
/**
* Compress using Gzip (faster, broader support)
*/
async compressGzip(data) {
return gzipAsync(data, {
level: Math.min(this.config.level, 9) // Gzip max level is 9
});
}
/**
* Select best encoding based on client support
*/
selectEncoding(acceptedEncodings) {
if (!acceptedEncodings) {
return this.config.preferredEncoding === 'br' && this.config.enableBrotli
? 'br'
: this.config.enableGzip
? 'gzip'
: 'identity';
}
const encodings = acceptedEncodings.toLowerCase().split(',').map(e => e.trim());
// Prefer Brotli if supported and enabled
if (encodings.includes('br') && this.config.enableBrotli) {
return 'br';
}
// Fall back to Gzip if supported and enabled
if (encodings.includes('gzip') && this.config.enableGzip) {
return 'gzip';
}
return 'identity';
}
/**
* Check if compression is recommended for content type
*/
shouldCompress(contentType) {
if (!contentType)
return true;
const type = contentType.toLowerCase();
// Compressible types
const compressible = [
'text/',
'application/json',
'application/javascript',
'application/xml',
'application/x-www-form-urlencoded'
];
return compressible.some(prefix => type.includes(prefix));
}
/**
* Get compression statistics
*/
getStats() {
return {
config: this.config,
capabilities: {
brotli: this.config.enableBrotli,
gzip: this.config.enableGzip
}
};
}
}
//# sourceMappingURL=compression-middleware.js.map