UNPKG

@hotmeshio/hotmesh

Version:

Permanent-Memory Workflows & AI Agents

135 lines (134 loc) 5.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Signal = void 0; const errors_1 = require("../../modules/errors"); const collator_1 = require("../collator"); const mapper_1 = require("../mapper"); const pipe_1 = require("../pipe"); const telemetry_1 = require("../telemetry"); const activity_1 = require("./activity"); class Signal extends activity_1.Activity { constructor(config, data, metadata, hook, engine, context) { super(config, data, metadata, hook, engine, context); } //******** LEG 1 ENTRY ********// async process() { this.logger.debug('signal-process', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid, }); let telemetry; try { await this.verifyEntry(); telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context); telemetry.startActivitySpan(this.leg); //save state and notarize early completion (signals only run leg1) const transaction = this.store.transact(); this.adjacencyList = await this.filterAdjacent(); this.mapOutputData(); this.mapJobData(); await this.setState(transaction); await collator_1.CollatorService.notarizeEarlyCompletion(this, transaction); await this.setStatus(this.adjacencyList.length - 1, transaction); const multiResponse = (await transaction.exec()); //todo: this should execute BEFORE the status is decremented if (this.config.subtype === 'all') { await this.hookAll(); } else { await this.hookOne(); } //transition to adjacent activities const jobStatus = this.resolveStatus(multiResponse); const attrs = { 'app.job.jss': jobStatus }; const messageIds = await this.transition(this.adjacencyList, jobStatus); if (messageIds.length) { attrs['app.activity.mids'] = messageIds.join(','); } telemetry.mapActivityAttributes(); telemetry.setActivityAttributes(attrs); return this.context.metadata.aid; } catch (error) { if (error instanceof errors_1.InactiveJobError) { this.logger.error('signal-inactive-job-error', { error }); return; } else if (error instanceof errors_1.GenerationalError) { this.logger.info('process-event-generational-job-error', { error }); return; } else if (error instanceof errors_1.GetStateError) { this.logger.error('signal-get-state-error', { error }); return; } else if (error instanceof errors_1.CollationError) { if (error.fault === 'duplicate') { this.logger.info('signal-collation-overage', { job_id: this.context.metadata.jid, guid: this.context.metadata.guid, }); return; } //unknown collation error this.logger.error('signal-collation-error', { error }); } else { this.logger.error('signal-process-error', { error }); } telemetry?.setActivityError(error.message); throw error; } finally { telemetry?.endActivitySpan(); this.logger.debug('signal-process-end', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid, }); } } mapSignalData() { if (this.config.signal?.maps) { const mapper = new mapper_1.MapperService(this.config.signal.maps, this.context); return mapper.mapRules(); } } mapResolverData() { if (this.config.resolver?.maps) { const mapper = new mapper_1.MapperService(this.config.resolver.maps, this.context); return mapper.mapRules(); } } /** * The signal activity will hook one */ async hookOne() { const topic = pipe_1.Pipe.resolve(this.config.topic, this.context); const signalInputData = this.mapSignalData(); const status = pipe_1.Pipe.resolve(this.config.status, this.context); const code = pipe_1.Pipe.resolve(this.config.code, this.context); return await this.engine.hook(topic, signalInputData, status, code); } /** * The signal activity will hook all paused jobs that share the same job key. */ async hookAll() { //prep 1) generate `input signal data` (essentially the webhook payload) const signalInputData = this.mapSignalData(); //prep 2) generate data that resolves the job key (per the YAML config) const keyResolverData = this.mapResolverData(); if (this.config.scrub) { //self-clean the indexes upon use if configured keyResolverData.scrub = true; } //prep 3) jobKeys can contain multiple indexes (per the YAML config) const key_name = pipe_1.Pipe.resolve(this.config.key_name, this.context); const key_value = pipe_1.Pipe.resolve(this.config.key_value, this.context); const indexQueryFacets = [`${key_name}:${key_value}`]; //execute: `hookAll` will now resume all paused jobs that share the same job key return await this.engine.hookAll(this.config.topic, signalInputData, keyResolverData, indexQueryFacets); } } exports.Signal = Signal;