UNPKG

nestjs-temporal-core

Version:

Complete NestJS integration for Temporal.io with auto-discovery, declarative scheduling, enhanced monitoring, and enterprise-ready features

346 lines 14.3 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; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var TemporalDiscoveryService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.TemporalDiscoveryService = void 0; const common_1 = require("@nestjs/common"); const core_1 = require("@nestjs/core"); const constants_1 = require("../constants"); const logger_1 = require("../utils/logger"); const temporal_metadata_service_1 = require("./temporal-metadata.service"); let TemporalDiscoveryService = TemporalDiscoveryService_1 = class TemporalDiscoveryService { constructor(discoveryService, metadataAccessor = new temporal_metadata_service_1.TemporalMetadataAccessor(), options, activityModuleOptions = { activityClasses: [] }) { this.discoveryService = discoveryService; this.metadataAccessor = metadataAccessor; this.options = options; this.activityModuleOptions = activityModuleOptions; this.discoveredActivities = new Map(); this.isDiscoveryComplete = false; this.discoveryStartTime = null; this.lastDiscoveryTime = null; this.logger = (0, logger_1.createLogger)(TemporalDiscoveryService_1.name, { enableLogger: options.enableLogger, logLevel: options.logLevel, }); } async onModuleInit() { try { this.logger.verbose('Starting component discovery...'); this.discoveryStartTime = new Date(); const result = await this.discoverComponents(); this.isDiscoveryComplete = true; this.lastDiscoveryTime = new Date(); this.logDiscoveryResults(result); } catch (error) { this.logger.error('Failed to complete component discovery', error); throw error; } } getDiscoveredActivities() { return new Map(this.discoveredActivities); } getActivity(name) { const activityInfo = this.discoveredActivities.get(name); if (!activityInfo) return undefined; const info = activityInfo; return info.handler || (typeof info === 'function' ? info : undefined); } getAllActivities() { const allActivities = {}; for (const [name, activityInfo] of this.discoveredActivities) { const handler = activityInfo.handler || activityInfo; if (typeof handler === 'function') { allActivities[name] = handler; } } return allActivities; } hasActivity(name) { return this.discoveredActivities.has(name); } getActivityNames() { return Array.from(this.discoveredActivities.keys()); } async executeActivity(name, ...args) { const activity = this.getActivity(name); if (!activity) { return { success: false, error: new Error(`Activity '${name}' not found`), activityName: name, }; } const startTime = Date.now(); try { this.logger.verbose(`Executing activity: ${name}`); const result = await activity(...args); const executionTime = Date.now() - startTime; this.logger.verbose(`Activity completed: ${name}`); return { success: true, result, executionTime, activityName: name, args, }; } catch (error) { const executionTime = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.error(`Failed to execute activity ${name}: ${errorMessage}`, error); return { success: false, error: error instanceof Error ? error : new Error(errorMessage), executionTime, activityName: name, args, }; } } async rediscover() { this.logger.info('Refreshing component discovery...'); this.clearDiscoveredComponents(); const result = await this.discoverComponents(); this.isDiscoveryComplete = true; this.lastDiscoveryTime = new Date(); this.logger.info('Component discovery refresh completed'); return result; } getStats() { return { methods: this.discoveredActivities.size, activities: this.discoveredActivities.size, totalComponents: this.discoveredActivities.size, }; } getHealthStatus() { const hasDiscoveredComponents = this.getTotalDiscovered() > 0; const discoveryDuration = this.discoveryStartTime && this.lastDiscoveryTime ? this.lastDiscoveryTime.getTime() - this.discoveryStartTime.getTime() : null; return { status: hasDiscoveredComponents ? 'healthy' : 'degraded', discoveredItems: { activities: this.discoveredActivities.size, }, isComplete: this.isDiscoveryComplete, lastDiscovery: this.lastDiscoveryTime, discoveryDuration, totalComponents: this.getTotalDiscovered(), }; } async discoverComponents() { const startTime = Date.now(); const errors = []; let discoveredCount = 0; const providers = this.discoveryService.getProviders(); const controllers = this.discoveryService.getControllers(); this.logger.verbose(`Scanning ${providers.length} providers and ${controllers.length} controllers`); const allWrappers = [...providers, ...controllers]; for (const wrapper of allWrappers) { try { const result = await this.processWrapper(wrapper); if (result.success) { discoveredCount += result.processedCount; } errors.push(...result.errors); } catch (error) { const wrapperName = this.getWrapperName(wrapper); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; errors.push({ component: wrapperName, error: errorMessage }); this.logger.warn(`Failed to process wrapper ${wrapperName}`, error); } } const duration = Date.now() - startTime; return { success: errors.length === 0, discoveredCount, errors, duration, }; } async processWrapper(wrapper) { const errors = []; let processedCount = 0; try { const instance = wrapper.instance; const metatype = wrapper.metatype; if (!instance || !metatype) { return { success: true, processedCount: 0, errors: [], }; } const className = metatype.name; if (this.metadataAccessor.isActivity(metatype)) { const result = await this.discoverActivitiesInClass(instance, className); if (result.success) { processedCount += result.discoveredCount; } errors.push(...result.errors); } return { success: errors.length === 0, processedCount, errors, }; } catch (error) { const wrapperName = this.getWrapperName(wrapper); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; errors.push({ component: wrapperName, error: errorMessage }); return { success: false, processedCount: 0, errors, }; } } async discoverActivitiesInClass(instance, className) { const errors = []; let discoveredCount = 0; try { const filterClasses = this.activityModuleOptions?.activityClasses || []; const metatype = instance.constructor; if (filterClasses.length > 0 && !filterClasses.includes(metatype)) { return { success: true, discoveredCount: 0, errors: [], }; } if (!this.metadataAccessor.isActivity(metatype)) { return { success: true, discoveredCount: 0, errors: [], }; } const validation = this.metadataAccessor.validateActivityClass(metatype); if (!validation.isValid) { const errorMessage = `Activity class ${className} has validation issues: ${validation.issues.join(', ')}`; this.logger.warn(errorMessage); errors.push({ component: className, error: errorMessage }); return { success: false, discoveredCount: 0, errors, }; } const activityMethodsResult = this.metadataAccessor.extractActivityMethods(instance); for (const [activityName, methodInfo] of activityMethodsResult.methods.entries()) { try { const handler = typeof methodInfo === 'function' ? methodInfo : methodInfo.handler; if (typeof handler === 'function') { this.discoveredActivities.set(activityName, { name: activityName, className, method: methodInfo, instance, handler: handler, }); discoveredCount++; } else { const errorMessage = `Invalid activity method for ${activityName}: not a function`; this.logger.warn(errorMessage); errors.push({ component: `${className}.${activityName}`, error: errorMessage, }); } } catch (methodError) { const errorMessage = methodError instanceof Error ? methodError.message : 'Unknown error'; errors.push({ component: `${className}.${activityName}`, error: errorMessage, }); } } return { success: errors.length === 0, discoveredCount, errors, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.warn(`Failed to discover activities in ${className}`, error); errors.push({ component: className, error: errorMessage }); return { success: false, discoveredCount, errors, }; } } clearDiscoveredComponents() { this.discoveredActivities.clear(); this.isDiscoveryComplete = false; this.discoveryStartTime = null; this.lastDiscoveryTime = null; } getTotalDiscovered() { return this.discoveredActivities.size; } getWrapperName(wrapper) { try { const metatype = wrapper.metatype; return metatype?.name || 'unknown'; } catch { return 'unknown'; } } logDiscoveryResults(result) { const totalDiscovered = this.getTotalDiscovered(); this.logger.info(`Discovery completed: ${this.discoveredActivities.size} activities`); if (result) { this.logger.info(`Discovery took ${result.duration}ms`); if (result.errors.length > 0) { this.logger.warn(`Discovery completed with ${result.errors.length} errors`); result.errors.forEach(({ component, error }) => { this.logger.warn(`Error in ${component}: ${error}`); }); } } if (totalDiscovered === 0) { this.logger.warn('No Temporal components discovered. Ensure decorators are properly applied.'); } this.logDiscoveredComponents(); } logDiscoveredComponents() { if (this.discoveredActivities.size > 0) { const activities = Array.from(this.discoveredActivities.keys()); this.logger.debug(`Discovered activities: [${activities.join(', ')}]`); } } }; exports.TemporalDiscoveryService = TemporalDiscoveryService; exports.TemporalDiscoveryService = TemporalDiscoveryService = TemporalDiscoveryService_1 = __decorate([ (0, common_1.Injectable)(), __param(1, (0, common_1.Optional)()), __param(2, (0, common_1.Inject)(constants_1.TEMPORAL_MODULE_OPTIONS)), __param(3, (0, common_1.Inject)(constants_1.ACTIVITY_MODULE_OPTIONS)), __metadata("design:paramtypes", [core_1.DiscoveryService, temporal_metadata_service_1.TemporalMetadataAccessor, Object, Object]) ], TemporalDiscoveryService); //# sourceMappingURL=temporal-discovery.service.js.map