UNPKG

@hotmeshio/hotmesh

Version:

Permanent-Memory Workflows & AI Agents

243 lines (242 loc) 9 kB
"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;