@nestjsvn/swagger-sse
Version:
OpenAPI documentation and interactive Swagger UI for NestJS Server-Sent Events endpoints
150 lines (149 loc) • 5.41 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateSseOptions = validateSseOptions;
exports.eventNameToSchemaName = eventNameToSchemaName;
exports.hasSwaggerDecorators = hasSwaggerDecorators;
exports.getDefaultSseContentType = getDefaultSseContentType;
exports.createSseEventExample = createSseEventExample;
exports.getSseMetadata = getSseMetadata;
const constants_1 = require("./constants");
/**
* Validate ApiSse decorator options
*
* @param options SSE configuration options
* @throws Error if options are invalid
*/
function validateSseOptions(options) {
if (!options) {
throw new Error('ApiSse options are required');
}
if (!options.events) {
throw new Error('ApiSse options must include events mapping');
}
if (typeof options.events !== 'object' || Array.isArray(options.events)) {
throw new Error('ApiSse events must be an object mapping event names to DTO classes');
}
const eventNames = Object.keys(options.events);
if (eventNames.length === 0) {
throw new Error('ApiSse events mapping cannot be empty');
}
// Validate each event mapping
Object.entries(options.events).forEach(([eventName, eventType]) => {
if (!eventName || typeof eventName !== 'string') {
throw new Error('Event names must be non-empty strings');
}
// Validate event type according to ApiSseOptions interface
if (!eventType ||
(typeof eventType !== 'function' &&
typeof eventType !== 'string' &&
!Array.isArray(eventType))) {
throw new Error(`Invalid event type for event '${eventName}': must be a class constructor, string, or array of class constructors`);
}
// Validate array types
if (Array.isArray(eventType)) {
const arrayType = eventType;
if (arrayType.length === 0) {
throw new Error(`Invalid array event type for event '${eventName}': array cannot be empty`);
}
if (typeof arrayType[0] !== 'function') {
throw new Error(`Invalid array event type for event '${eventName}': array must contain class constructors`);
}
}
});
// Validate optional properties
if (options.produces && typeof options.produces !== 'string') {
throw new Error('produces must be a string');
}
if (options.summary && typeof options.summary !== 'string') {
throw new Error('summary must be a string');
}
if (options.description && typeof options.description !== 'string') {
throw new Error('description must be a string');
}
if (options.operationId && typeof options.operationId !== 'string') {
throw new Error('operationId must be a string');
}
if (options.tags && !Array.isArray(options.tags)) {
throw new Error('tags must be an array of strings');
}
if (options.tags) {
options.tags.forEach((tag, index) => {
if (typeof tag !== 'string') {
throw new Error(`Tag at index ${index} must be a string`);
}
});
}
if (options.deprecated !== undefined && typeof options.deprecated !== 'boolean') {
throw new Error('deprecated must be a boolean');
}
if (options.security && !Array.isArray(options.security)) {
throw new Error('security must be an array of security requirement objects');
}
}
/**
* Convert event name to a valid schema name
*
* @param eventName Event name (e.g., 'user-created')
* @returns Schema name (e.g., 'UserCreated')
*/
function eventNameToSchemaName(eventName) {
return eventName
.split('-')
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
.join('');
}
/**
* Check if a class has Swagger decorators
*
* @param dtoClass DTO class constructor
* @returns True if the class has Swagger metadata
*/
function hasSwaggerDecorators(dtoClass) {
if (!dtoClass || typeof dtoClass !== 'function') {
return false;
}
// Check for common Swagger metadata keys
const metadataKeys = [
'swagger/apiModelPropertiesArray',
'swagger/apiModelProperties',
'swagger/apiModel',
];
return metadataKeys.some(key => Reflect.hasMetadata(key, dtoClass) ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
Reflect.hasMetadata(key, Object.getPrototypeOf(dtoClass.prototype)));
}
/**
* Get default content type for SSE
*
* @returns Default SSE content type
*/
function getDefaultSseContentType() {
return 'text/event-stream';
}
/**
* Create example SSE event data
*
* @param eventName Event name
* @param data Event data
* @returns Formatted SSE event string
*/
function createSseEventExample(eventName, data) {
const lines = [];
if (eventName) {
lines.push(`event: ${eventName}`);
}
lines.push(`data: ${JSON.stringify(data)}`);
lines.push(''); // Empty line to end the event
return lines.join('\n');
}
/**
* Get SSE metadata from a method
*
* @param target Target class
* @param propertyKey Method name
* @returns SSE metadata or undefined
*/
function getSseMetadata(target, propertyKey) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Reflect.getMetadata(constants_1.METADATA_KEYS.API_SSE, target, propertyKey);
}