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
JavaScript
"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