@hotmeshio/hotmesh
Version:
Permanent-Memory Workflows & AI Agents
135 lines (134 loc) • 5.81 kB
JavaScript
"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;