Files
smartserve/ts/decorators/decorators.compress.ts

116 lines
2.8 KiB
TypeScript

/**
* Compression control decorators (@Compress, @NoCompress)
*
* These decorators allow per-route control over compression:
* - @Compress(options?) - Force compression with optional settings
* - @NoCompress() - Disable compression for this route (useful for SSE, streaming)
*/
import { getControllerMetadata } from './decorators.metadata.js';
import type { IRouteCompressionOptions } from './decorators.types.js';
/**
* Set compression options for a route
*/
function setRouteCompression(
target: any,
methodName: string | symbol,
options: IRouteCompressionOptions
): void {
const metadata = getControllerMetadata(target.constructor);
let route = metadata.routes.get(methodName);
if (!route) {
// Create placeholder route (will be completed by @Get/@Post/etc.)
route = {
method: 'GET',
path: '',
methodName,
interceptors: [],
options: {},
};
metadata.routes.set(methodName, route);
}
route.compression = options;
}
/**
* @Compress decorator - Force compression on a route with optional settings
*
* @example
* ```typescript
* @Controller('/api')
* class ApiController {
* @Get('/heavy-data')
* @Compress({ level: 11 }) // Max brotli compression
* getHeavyData() {
* return massiveJsonData;
* }
*
* @Get('/data')
* @Compress() // Use default settings
* getData() {
* return jsonData;
* }
* }
* ```
*/
export function Compress(options?: { level?: number }) {
return function <This, Args extends any[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
context.addInitializer(function (this: This) {
setRouteCompression(this, context.name, {
enabled: true,
level: options?.level,
});
});
return target;
};
}
/**
* @NoCompress decorator - Disable compression for a route
*
* Useful for:
* - Server-Sent Events (SSE)
* - Streaming responses
* - Already-compressed content
* - Real-time data where latency is critical
*
* @example
* ```typescript
* @Controller('/api')
* class EventController {
* @Get('/events')
* @NoCompress() // SSE should not be compressed
* getEvents() {
* return new Response(eventStream, {
* headers: { 'Content-Type': 'text/event-stream' }
* });
* }
*
* @Get('/video/:id')
* @NoCompress() // Already compressed media
* getVideo() {
* return videoStream;
* }
* }
* ```
*/
export function NoCompress() {
return function <This, Args extends any[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
context.addInitializer(function (this: This) {
setRouteCompression(this, context.name, {
enabled: false,
});
});
return target;
};
}