UNPKG

nestjs-temporal-core

Version:

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

186 lines 8.38 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 TemporalConnectionFactory_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.TemporalConnectionFactory = void 0; const common_1 = require("@nestjs/common"); const client_1 = require("@temporalio/client"); const logger_1 = require("../utils/logger"); let TemporalConnectionFactory = TemporalConnectionFactory_1 = class TemporalConnectionFactory { constructor() { this.clientConnectionCache = new Map(); this.workerConnectionCache = new Map(); this.connectionAttempts = new Map(); this.MAX_RETRY_ATTEMPTS = 3; this.RETRY_DELAY_MS = 1000; this.logger = (0, logger_1.createLogger)(TemporalConnectionFactory_1.name); } async createClient(options) { if (!options.connection) { this.logger.info('No connection configuration provided - running without client'); return null; } const connectionKey = this.getConnectionKey(options.connection); const cachedClient = this.clientConnectionCache.get(connectionKey); if (cachedClient && this.isClientHealthy(cachedClient)) { this.logger.debug('Reusing cached client connection'); return cachedClient; } if (cachedClient) { this.clientConnectionCache.delete(connectionKey); } return this.createNewClient(options, connectionKey); } async createWorkerConnection(options) { if (!options.connection) { this.logger.debug('No connection configuration provided for worker'); return null; } const connectionKey = this.getConnectionKey(options.connection); const cachedConnection = this.workerConnectionCache.get(connectionKey); if (cachedConnection && this.isWorkerConnectionHealthy(cachedConnection)) { this.logger.debug('Reusing cached worker connection'); return cachedConnection; } if (cachedConnection) { try { await cachedConnection.close(); } catch (error) { this.logger.warn('Failed to close unhealthy worker connection', error); } this.workerConnectionCache.delete(connectionKey); } return this.createNewWorkerConnection(options, connectionKey); } async onModuleDestroy() { await this.cleanup(); } async cleanup() { this.logger.info('Cleaning up all connections...'); this.logger.debug('Skipping worker connection cleanup - managed by worker service lifecycle'); this.clientConnectionCache.clear(); this.workerConnectionCache.clear(); this.connectionAttempts.clear(); this.logger.info('Connection cleanup completed'); } getConnectionHealth() { return { clientConnections: this.clientConnectionCache.size, workerConnections: this.workerConnectionCache.size, totalAttempts: Array.from(this.connectionAttempts.values()).reduce((sum, attempts) => sum + attempts, 0), }; } async createNewClient(options, connectionKey) { const attempts = this.connectionAttempts.get(connectionKey) || 0; if (attempts >= this.MAX_RETRY_ATTEMPTS) { if (options.allowConnectionFailure !== false) { this.logger.warn(`Max retry attempts exceeded for ${options.connection.address} - running without client`); return null; } else { throw new Error(`Failed to connect to ${options.connection.address} after ${this.MAX_RETRY_ATTEMPTS} attempts`); } } try { this.logger.info(`Creating new client connection to ${options.connection.address} (attempt ${attempts + 1})`); const connection = await client_1.Connection.lazy({ address: options.connection.address, tls: options.connection.tls, metadata: options.connection.metadata, ...(options.connection.apiKey && { metadata: { ...(options.connection.metadata || {}), authorization: `Bearer ${options.connection.apiKey}`, }, }), }); const client = new client_1.Client({ connection, namespace: options.connection.namespace || 'default', }); this.clientConnectionCache.set(connectionKey, client); this.connectionAttempts.delete(connectionKey); logger_1.LoggerUtils.logConnection(this.logger, options.connection.address, true); return client; } catch (error) { this.connectionAttempts.set(connectionKey, attempts + 1); logger_1.LoggerUtils.logConnection(this.logger, options.connection.address, false, error); if (attempts + 1 < this.MAX_RETRY_ATTEMPTS) { this.logger.info(`Retrying connection in ${this.RETRY_DELAY_MS}ms...`); await this.delay(this.RETRY_DELAY_MS); return this.createNewClient(options, connectionKey); } if (options.allowConnectionFailure !== false) { this.logger.warn('Client connection failed - continuing without client'); return null; } throw error; } } async createNewWorkerConnection(options, connectionKey) { try { this.logger.debug(`Creating new worker connection to ${options.connection.address}`); const { NativeConnection } = await Promise.resolve().then(() => require('@temporalio/worker')); const address = options.connection.address; const connectOptions = { address, tls: options.connection.tls, }; if (options.connection.apiKey) { connectOptions.metadata = { ...(options.connection.metadata || {}), authorization: `Bearer ${options.connection.apiKey}`, }; } const connection = await NativeConnection.connect(connectOptions); this.workerConnectionCache.set(connectionKey, connection); this.logger.info(`Worker connection established to ${address}`); return connection; } catch (error) { this.logger.warn('Failed to create worker connection, returning null', error); return null; } } getConnectionKey(connection) { return `${connection.address}:${connection.namespace || 'default'}:${connection.apiKey ? 'auth' : 'noauth'}`; } isClientHealthy(client) { try { return Boolean(client.workflow); } catch { return false; } } isWorkerConnectionHealthy(connection) { try { return (connection !== null && connection !== undefined && typeof connection === 'object' && connection.constructor !== undefined); } catch { return false; } } delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } }; exports.TemporalConnectionFactory = TemporalConnectionFactory; exports.TemporalConnectionFactory = TemporalConnectionFactory = TemporalConnectionFactory_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", []) ], TemporalConnectionFactory); //# sourceMappingURL=temporal-connection.factory.js.map