UNPKG

@logistically/events-nestjs

Version:

NestJS integration for @logistically/events v3 - Event-driven architecture with Redis Streams

188 lines (187 loc) 6.57 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 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; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EventMetadataExplorer = void 0; const common_1 = require("@nestjs/common"); const core_1 = require("@nestjs/core"); const auto_event_handler_decorator_1 = require("../decorators/auto-event-handler.decorator"); let EventMetadataExplorer = class EventMetadataExplorer { constructor() { this.metadataScanner = new core_1.MetadataScanner(); // Cache for metadata scanning results to avoid repeated scans this.metadataCache = new WeakMap(); // Cache TTL in milliseconds (5 minutes) this.cacheTTL = 5 * 60 * 1000; // Track cache statistics this.cacheHits = 0; this.cacheMisses = 0; } /** * Explore an instance for event handler methods with caching * Based on NestJS microservices ListenerMetadataExplorer pattern */ explore(instance) { if (!instance) { return []; } // Check cache first const cached = this.metadataCache.get(instance); if (cached && this.isCacheValid(cached.timestamp)) { this.cacheHits++; return cached.handlers; } this.cacheMisses++; // Perform fresh scan const instancePrototype = Object.getPrototypeOf(instance); if (!instancePrototype) { return []; } const handlers = this.metadataScanner .getAllMethodNames(instancePrototype) .map(method => this.exploreMethodMetadata(instancePrototype, method)) .filter(metadata => metadata !== null); // Cache the results this.metadataCache.set(instance, { handlers, hasHandlers: handlers.length > 0, eventTypes: handlers.map(h => h.eventType), timestamp: Date.now() }); return handlers; } /** * Explore a single method for event handler metadata */ exploreMethodMetadata(instancePrototype, methodKey) { const targetCallback = instancePrototype[methodKey]; // Check if method has @AutoEventHandler decorator const metadata = Reflect.getMetadata(auto_event_handler_decorator_1.AUTO_EVENT_HANDLER_METADATA, targetCallback); if (!metadata) { return null; } return { methodKey, targetCallback, eventType: metadata.eventType, options: metadata }; } /** * Check if an instance has any event handlers with caching */ hasEventHandlers(instance) { if (!instance) { return false; } const cached = this.metadataCache.get(instance); if (cached && this.isCacheValid(cached.timestamp)) { this.cacheHits++; return cached.hasHandlers; } this.cacheMisses++; const result = this.explore(instance).length > 0; // Update cache if not already cached if (!cached) { const handlers = this.explore(instance); this.metadataCache.set(instance, { handlers, hasHandlers: result, eventTypes: handlers.map(h => h.eventType), timestamp: Date.now() }); } return result; } /** * Get all event types that an instance handles with caching */ getEventTypes(instance) { if (!instance) { return []; } const cached = this.metadataCache.get(instance); if (cached && this.isCacheValid(cached.timestamp)) { this.cacheHits++; return cached.eventTypes; } this.cacheMisses++; const handlers = this.explore(instance); // Update cache if not already cached if (!cached) { this.metadataCache.set(instance, { handlers, hasHandlers: handlers.length > 0, eventTypes: handlers.map(h => h.eventType), timestamp: Date.now() }); } return handlers.map(h => h.eventType); } /** * Check if cached data is still valid */ isCacheValid(timestamp) { return Date.now() - timestamp < this.cacheTTL; } /** * Invalidate cache for a specific instance * Useful when instance methods are modified at runtime */ invalidateCache(instance) { if (instance) { this.metadataCache.delete(instance); } } /** * Clear all cached data * Useful for testing or when memory usage is a concern */ clearCache() { this.metadataCache = new WeakMap(); this.cacheHits = 0; this.cacheMisses = 0; } /** * Get cache statistics for monitoring */ getCacheStats() { const total = this.cacheHits + this.cacheMisses; const hitRate = total > 0 ? (this.cacheHits / total) * 100 : 0; // Estimate cache size (WeakMap doesn't provide size, so we estimate) let cacheSize = 0; // Note: WeakMap size cannot be determined, this is an approximation // In practice, the WeakMap will automatically clean up when instances are garbage collected return { hits: this.cacheHits, misses: this.cacheMisses, hitRate: Math.round(hitRate * 100) / 100, cacheSize }; } /** * Cleanup on module destroy */ onModuleDestroy() { this.clearCache(); } /** * Pre-warm cache for known instances * Useful for frequently accessed services */ preWarmCache(instances) { for (const instance of instances) { if (instance) { this.explore(instance); } } } }; exports.EventMetadataExplorer = EventMetadataExplorer; exports.EventMetadataExplorer = EventMetadataExplorer = __decorate([ (0, common_1.Injectable)() ], EventMetadataExplorer);