@hotmeshio/hotmesh
Version:
Permanent-Memory Workflows & AI Agents
243 lines (242 loc) • 9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Pipe = void 0;
const functions_1 = __importDefault(require("./functions"));
class Pipe {
constructor(rules, jobData, context) {
this.rules = rules;
this.jobData = jobData;
this.context = context;
}
isPipeType(currentRow) {
return !Array.isArray(currentRow) && '@pipe' in currentRow;
}
isreduceType(currentRow) {
return !Array.isArray(currentRow) && '@reduce' in currentRow;
}
static isPipeObject(obj) {
return (typeof obj === 'object' &&
obj !== null &&
!Array.isArray(obj) &&
'@pipe' in obj);
}
static resolve(unresolved, context) {
let pipe;
if (Pipe.isPipeObject(unresolved)) {
pipe = new Pipe(unresolved['@pipe'], context);
}
else {
pipe = new Pipe([[unresolved]], context);
}
return pipe.process();
}
/**
* loop through each PipeItem row in this Pipe, resolving and transforming line by line
* @returns {any} the result of the pipe
*/
process(resolved = null) {
let index = 0;
if (!(resolved ||
this.isPipeType(this.rules[0]) ||
this.isreduceType(this.rules[0]))) {
resolved = this.processCells(this.rules[0]); // Add type assertion
index = 1;
}
const len = this.rules.length;
const subPipeQueue = [];
for (let i = index; i < len; i++) {
resolved = this.processRow(this.rules[i], resolved, subPipeQueue);
}
return resolved[0];
}
cloneUnknown(value) {
if (value === null || typeof value !== 'object') {
return value;
}
if (value instanceof Date) {
return new Date(value.getTime());
}
if (value instanceof RegExp) {
return new RegExp(value);
}
if (Array.isArray(value)) {
return value.map((item) => this.cloneUnknown(item));
}
const clonedObj = {};
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
clonedObj[key] = this.cloneUnknown(value[key]);
}
}
return clonedObj;
}
/**
* Transforms iterable `input` into a single value. Vars $output, $item, $key
* and $input are available. The final statement in the iterator (the reduction)
* is assumed to be the return value. A default $output object may be provided
* to the iterator by placing the the second cell of the preceding row. Otherwise,
* construct the object during first run and ensure it is the first cell of the
* last row of the iterator, so it is returned as the $output for the next cycle
* @param {unknown[]} input
* @returns {unknown}
* @private
*/
reduce(input) {
let resolved = this.cloneUnknown(input[1] ?? null);
if (Array.isArray(input[0])) {
for (let index = 0; index < input[0].length; index++) {
this.context = {
$input: input[0],
$output: resolved,
$item: input[0][index],
$key: index.toString(),
$index: index,
};
resolved = this.process([resolved]);
}
}
else {
let index = -1;
for (const $key in input[0]) {
index++;
this.context = {
$input: input[0],
$output: resolved,
$item: input[0][$key],
$key,
$index: index,
};
resolved = this.process([resolved]);
}
}
return [resolved];
}
processRow(currentRow, resolvedPriorRow, subPipeQueue) {
if (resolvedPriorRow && this.isreduceType(currentRow)) {
//reduce the resolvedPriorRow and return the output from the reducer
const subPipe = new Pipe(currentRow['@reduce'], this.jobData);
const reduced = subPipe.reduce(resolvedPriorRow);
return reduced;
}
else if (this.isPipeType(currentRow)) {
//process subPipe and push to subPipeQueue; echo resolvedPriorRow
const subPipe = new Pipe(currentRow['@pipe'], this.jobData, this.context);
subPipeQueue.push(subPipe.process());
return resolvedPriorRow;
}
else {
//pivot the subPipeQueue into the arguments array (resolvedPriorRow)
if (subPipeQueue.length > 0) {
resolvedPriorRow = [...subPipeQueue];
subPipeQueue.length = 0;
}
if (!resolvedPriorRow) {
//if no prior row, use current row as prior row
return [].concat(this.processCells(Array.isArray(currentRow) ? [...currentRow] : []));
}
else {
const [functionName, ...params] = currentRow; // Add type assertion
//use resolved values from prior row (n - 1) as input params to cell 1 function
let resolvedValue;
if (this.isContextVariable(functionName)) {
resolvedValue = this.resolveContextValue(functionName);
}
else {
resolvedValue = Pipe.resolveFunction(functionName)(...resolvedPriorRow);
}
//resolve remaining cells in row and return concatenated with resolvedValue
return [resolvedValue].concat(this.processCells([...params]));
}
}
}
static resolveFunction(functionName) {
let [prefix, suffix] = functionName.split('.');
prefix = prefix.substring(2);
suffix = suffix.substring(0, suffix.length - 1);
const domain = functions_1.default[prefix];
if (!domain) {
throw new Error(`Unknown domain name [${functionName}]: ${prefix}`);
}
if (!domain[suffix]) {
throw new Error(`Unknown domain function [${functionName}]: ${prefix}.${suffix}`);
}
return domain[suffix];
}
processCells(cells) {
const resolved = [];
if (Array.isArray(cells)) {
for (const currentCell of cells) {
resolved.push(this.resolveCellValue(currentCell));
}
}
return resolved;
}
isFunction(currentCell) {
return (typeof currentCell === 'string' &&
currentCell.startsWith('{@') &&
currentCell.endsWith('}'));
}
isContextVariable(currentCell) {
if (typeof currentCell === 'string' && currentCell.endsWith('}')) {
return (currentCell.startsWith('{$item') ||
currentCell.startsWith('{$key') ||
currentCell.startsWith('{$index') ||
currentCell.startsWith('{$input') ||
currentCell.startsWith('{$output'));
}
}
isMappable(currentCell) {
return (typeof currentCell === 'string' &&
currentCell.startsWith('{') &&
currentCell.endsWith('}'));
}
resolveCellValue(currentCell) {
if (this.isFunction(currentCell)) {
const fn = Pipe.resolveFunction(currentCell);
return fn.call();
}
else if (this.isContextVariable(currentCell)) {
return this.resolveContextValue(currentCell);
}
else if (this.isMappable(currentCell)) {
return this.resolveMappableValue(currentCell);
}
else {
return currentCell;
}
}
getNestedProperty(obj, path) {
const pathParts = path.split('.');
let current = obj;
for (const part of pathParts) {
if (current === null ||
typeof current !== 'object' ||
!current.hasOwnProperty(part)) {
return undefined;
}
current = current[part];
}
return current;
}
resolveMappableValue(currentCell) {
const term = this.resolveMapTerm(currentCell);
return this.getNestedProperty(this.jobData, term);
}
resolveContextValue(currentCell) {
const term = this.resolveContextTerm(currentCell);
return this.getNestedProperty(this.context, term);
}
resolveContextTerm(currentCell) {
return currentCell.substring(1, currentCell.length - 1);
}
resolveFunctionTerm(currentCell) {
return currentCell.substring(2, currentCell.length - 1);
}
resolveMapTerm(currentCell) {
return currentCell.substring(1, currentCell.length - 1);
}
}
exports.Pipe = Pipe;