UNPKG

@hotmeshio/hotmesh

Version:

Permanent-Memory Workflows & AI Agents

216 lines (215 loc) 7.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExporterService = void 0; const utils_1 = require("../../modules/utils"); const serializer_1 = require("../serializer"); class ExporterService { constructor(appId, store, logger) { this.appId = appId; this.logger = logger; this.store = store; } /** * Convert the job hash from its compiles format into a MemFlowJobExport object with * facets that describe the workflow in terms relevant to narrative storytelling. */ async export(jobId, options = {}) { if (!ExporterService.symbols.has(this.appId)) { const symbols = this.store.getAllSymbols(); ExporterService.symbols.set(this.appId, await symbols); } const jobData = await this.store.getRaw(jobId); const jobExport = this.inflate(jobData, options); return jobExport; } /** * Inflates the job data into a MemFlowJobExport object * @param jobHash - the job data * @param dependencyList - the list of dependencies for the job * @returns - the inflated job data */ inflate(jobHash, options) { const timeline = []; const state = {}; const data = {}; const transitionsObject = {}; const regex = /^([a-zA-Z]{3}),(\d+(?:,\d+)*)/; Object.entries(jobHash).forEach(([key, value]) => { const match = key.match(regex); if (match) { //transitions this.inflateTransition(match, value, transitionsObject); } else if (key.startsWith('_')) { //data data[key.substring(1)] = value; } else if (key.startsWith('-')) { //timeline const keyParts = this.keyToObject(key); timeline.push({ ...keyParts, key, value: this.resolveValue(value, options.values), }); } else if (key.length === 3) { //state state[this.inflateKey(key)] = serializer_1.SerializerService.fromString(value); } }); return this.filterFields({ data: (0, utils_1.restoreHierarchy)(data), state: Object.entries((0, utils_1.restoreHierarchy)(state))[0][1], status: parseInt(jobHash[':'], 10), timeline: this.sortParts(timeline), transitions: this.sortEntriesByCreated(transitionsObject), }, options.block, options.allow); } resolveValue(raw, withValues) { const resolved = serializer_1.SerializerService.fromString(raw); if (withValues !== false) { return resolved; } if (resolved && typeof resolved === 'object') { if ('data' in resolved) { resolved.data = {}; } if ('$error' in resolved) { resolved.$error = {}; } } return resolved; } /** * Inflates the key * into a human-readable JSON path, reflecting the * tree-like structure of the unidimensional Hash * @private */ inflateKey(key) { const symbols = ExporterService.symbols.get(this.appId); if (key in symbols) { const path = symbols[key]; const parts = path.split('/'); return parts.join('/'); } return key; } filterFields(fullObject, block = [], allow = []) { let result = {}; if (allow && allow.length > 0) { allow.forEach((field) => { if (field in fullObject) { result[field] = fullObject[field]; } }); } else { result = { ...fullObject }; } if (block && block.length > 0) { block.forEach((field) => { if (field in result) { delete result[field]; } }); } return result; } inflateTransition(match, value, transitionsObject) { const [_, letters, dimensions] = match; const path = this.inflateKey(letters); const parts = path.split('/'); const activity = parts[0]; const isCreate = path.endsWith('/output/metadata/ac'); const isUpdate = path.endsWith('/output/metadata/au'); //for now only export activity start/stop; activity data would also be interesting if (isCreate || isUpdate) { const targetName = `${activity},${dimensions}`; const target = transitionsObject[targetName]; if (!target) { transitionsObject[targetName] = { activity, dimensions, created: isCreate ? value : null, updated: isUpdate ? value : null, }; } else { target[isCreate ? 'created' : 'updated'] = value; } } } sortEntriesByCreated(obj) { const entriesArray = Object.values(obj); entriesArray.sort((a, b) => { return (a.created || a.updated).localeCompare(b.created || b.updated); }); return entriesArray; } /** * marker names are overloaded with details like sequence, type, etc */ keyToObject(key) { function extractDimension(label) { const parts = label.split(','); if (parts.length > 1) { parts.shift(); return parts.join(','); } } const parts = key.split('-'); if (parts.length === 4) { //-proxy-5- -search-1-1- return { index: parseInt(parts[2], 10), dimension: extractDimension(parts[1]), }; } else { //-search,0,0-1-1- -proxy,0,0-1- return { index: parseInt(parts[2], 10), secondary: parseInt(parts[3], 10), dimension: extractDimension(parts[1]), }; } } /** * idem list has a complicated sort order based on indexes and dimensions */ sortParts(parts) { return parts.sort((a, b) => { const { dimension: aDim, index: aIdx, secondary: aSec } = a; const { dimension: bDim, index: bIdx, secondary: bSec } = b; if (aDim === undefined && bDim !== undefined) return -1; if (aDim !== undefined && bDim === undefined) return 1; if (aDim !== undefined && bDim !== undefined) { if (aDim < bDim) return -1; if (aDim > bDim) return 1; } if (aIdx < bIdx) return -1; if (aIdx > bIdx) return 1; if (aSec === undefined && bSec !== undefined) return -1; if (aSec !== undefined && bSec === undefined) return 1; if (aSec !== undefined && bSec !== undefined) { if (aSec < bSec) return -1; if (aSec > bSec) return 1; } return 0; }); } } exports.ExporterService = ExporterService; ExporterService.symbols = new Map();