UNPKG

@forzalabs/remora

Version:

A powerful CLI tool for seamless data translation.

278 lines (277 loc) 14.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const Algo_1 = __importDefault(require("../../core/Algo")); const TypeCaster_1 = __importDefault(require("./TypeCaster")); const CryptoEngine_1 = __importDefault(require("../CryptoEngine")); const DeveloperEngine_1 = __importDefault(require("../ai/DeveloperEngine")); class TransformationEngineClass { constructor() { this.applyTransformations = (value, transformations, field, record) => { var _a; if (Array.isArray(transformations)) { // Process array transformations without creating intermediate arrays let result = value; for (const transform of transformations) { result = this.applyTransformations(result, transform, field, record); } return result; } // Single transformation if ('cast' in transformations) { const { cast, format } = transformations; const casted = TypeCaster_1.default.cast(value, cast, format); if (cast === 'number' && isNaN(casted)) throw new Error(`Cannot cast non-numeric value in field '${field.key}'`); if (cast === 'datetime' && casted instanceof Date && isNaN(casted.getTime())) throw new Error(`Cannot cast value to date in field '${field.key}'`); return casted; } if ('multiply' in transformations) { const num = TypeCaster_1.default.cast(value, 'number'); if (isNaN(num)) throw new Error(`Cannot multiply non-numeric value in field '${field.key}'`); return num * transformations.multiply; } if ('multiplyBy' in transformations) { if (!record) { throw new Error(`Cannot apply combine_fields transformation without record context in field '${field.key}'`); } const { fields } = transformations.multiplyBy; const fieldValues = fields.map(fieldName => { const fieldValue = record[fieldName]; return fieldValue !== null && fieldValue !== undefined ? TypeCaster_1.default.cast(fieldValue, 'number') : 1; }); const product = fieldValues.reduce((accumulator, value) => accumulator * value, 1); return product; } if ('add' in transformations) { const num = TypeCaster_1.default.cast(value, 'number'); if (isNaN(num)) throw new Error(`Cannot add to non-numeric value in field '${field.key}'`); return num + transformations.add; } if ('addBy' in transformations) { if (!record) { throw new Error(`Cannot apply combine_fields transformation without record context in field '${field.key}'`); } const { fields } = transformations.addBy; const fieldValues = fields.map(fieldName => { const fieldValue = record[fieldName]; return fieldValue !== null && fieldValue !== undefined ? TypeCaster_1.default.cast(fieldValue, 'number') : 1; }); const sum = fieldValues.reduce((accumulator, value) => accumulator + value); return sum; } if ('extract' in transformations) { const date = TypeCaster_1.default.cast(value, 'date'); if (isNaN(date.getTime())) throw new Error(`Invalid date for extraction in field '${field.key}'`); switch (transformations.extract) { case 'year': return date.getFullYear(); case 'month': return date.getMonth() + 1; // 1-based month case 'day': return date.getDate(); case 'hour': return date.getHours(); case 'minute': return date.getMinutes(); } } if ('concat' in transformations) { if (!Array.isArray(value)) throw new Error(`Cannot concat non-array value in field '${field.key}'`); return value.join(transformations.concat.separator); } if ('split' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot split non-string value in field '${field.key}'`); const parts = value.split(transformations.split.separator); if (transformations.split.index >= parts.length) { throw new Error(`Split index ${transformations.split.index} out of bounds in field '${field.key}'`); } return parts[transformations.split.index]; } if ('regex_match' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot apply regex_match to non-string value in field '${field.key}'`); try { const regex = new RegExp(transformations.regex_match.pattern, transformations.regex_match.flags); return regex.test(value); } catch (error) { throw new Error(`Invalid regex pattern in field '${field.key}': ${error.message}`); } } if ('regex_replace' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot apply regex_replace to non-string value in field '${field.key}'`); try { const regex = new RegExp(transformations.regex_replace.pattern, transformations.regex_replace.flags); return value.replace(regex, transformations.regex_replace.replacement); } catch (error) { throw new Error(`Invalid regex pattern in field '${field.key}': ${error.message}`); } } if ('regex_extract' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot apply regex_extract to non-string value in field '${field.key}'`); try { const regex = new RegExp(transformations.regex_extract.pattern, transformations.regex_extract.flags); const matches = value.match(regex); if (!matches) return null; const groupIndex = transformations.regex_extract.group; return (_a = matches[groupIndex]) !== null && _a !== void 0 ? _a : null; } catch (error) { throw new Error(`Invalid regex pattern in field '${field.key}': ${error.message}`); } } if ('trim' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot trim non-string value in field '${field.key}'`); return value.trim(); } if ('to_lowercase' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot convert non-string value to lowercase in field '${field.key}'`); return value.toLowerCase(); } if ('to_uppercase' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot convert non-string value to uppercase in field '${field.key}'`); return value.toUpperCase(); } if ('capitalize' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot capitalize non-string value in field '${field.key}'`); return value.charAt(0).toUpperCase() + value.slice(1); } if ('substring' in transformations) { if (!Algo_1.default.hasVal(value)) return ''; if (typeof value !== 'string') throw new Error(`Cannot take substring of non-string value in field '${field.key}'`); const { start, end } = transformations.substring; return end !== undefined ? value.substring(start, end) : value.substring(start); } if ('pad_start' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot pad non-string value in field '${field.key}'`); const { length, char } = transformations.pad_start; if (char.length !== 1) throw new Error(`Pad character must be exactly one character in field '${field.key}'`); return value.padStart(length, char); } if ('pad_end' in transformations) { if (typeof value !== 'string') throw new Error(`Cannot pad non-string value in field '${field.key}'`); const { length, char } = transformations.pad_end; if (char.length !== 1) throw new Error(`Pad character must be exactly one character in field '${field.key}'`); return value.padEnd(length, char); } if ('prepend' in transformations) return transformations.prepend + TypeCaster_1.default.cast(value, 'string'); if ('append' in transformations) return TypeCaster_1.default.cast(value, 'string') + transformations.append; if ('combine_fields' in transformations) { if (!record) { throw new Error(`Cannot apply combine_fields transformation without record context in field '${field.key}'`); } const { fields, separator = '', template } = transformations.combine_fields; // Get values from the specified fields const fieldValues = fields.map(fieldName => { const fieldValue = record[fieldName]; return fieldValue !== null && fieldValue !== undefined ? String(fieldValue) : ''; }); // If template is provided, use it for formatting if (template) { let result = template; for (let i = 0; i < fields.length; i++) { const placeholder = `{${fields[i]}}`; result = result.replace(new RegExp(placeholder, 'g'), fieldValues[i]); } return result; } else { // Otherwise, join with separator return fieldValues.join(separator); } } if ('mask' in transformations) { return this.applyMasking(value, transformations.mask, field); } if ('conditional' in transformations) { for (const clause of transformations.conditional.clauses) { if (this.evaluateCondition(value, clause.if)) { return clause.then; } } return transformations.conditional.else !== undefined ? transformations.conditional.else : value; } return value; }; this.evaluateCondition = (value, condition) => { if ('greater_than' in condition) { return TypeCaster_1.default.cast(value, 'number') > condition.greater_than; } if ('greater_than_or_equal' in condition) { return TypeCaster_1.default.cast(value, 'number') >= condition.greater_than_or_equal; } if ('less_than' in condition) { return TypeCaster_1.default.cast(value, 'number') < condition.less_than; } if ('less_than_or_equal' in condition) { return TypeCaster_1.default.cast(value, 'number') <= condition.less_than_or_equal; } if ('equals' in condition) { return value === condition.equals; } if ('not_equals' in condition) { return value !== condition.not_equals; } if ('in' in condition) { return condition.in.includes(value); } if ('not_in' in condition) { return !condition.not_in.includes(value); } if ('starts_with' in condition) { return TypeCaster_1.default.cast(value, 'string').startsWith(condition.starts_with); } if ('ends_with' in condition) { return TypeCaster_1.default.cast(value, 'string').endsWith(condition.ends_with); } if ('contains' in condition) { return TypeCaster_1.default.cast(value, 'string').includes(condition.contains); } if ('not_contains' in condition) { return !TypeCaster_1.default.cast(value, 'string').includes(condition.not_contains); } if ('is_empty' in condition) { return value === null || value === undefined || TypeCaster_1.default.cast(value, 'string').trim() === ''; } if ('is_not_empty' in condition) { return value !== null && value !== undefined && TypeCaster_1.default.cast(value, 'string').trim() !== ''; } return false; }; this.applyMasking = (value, maskType, field) => { if (!Algo_1.default.hasVal(value)) return value; if (maskType === 'none') return value; const valueType = DeveloperEngine_1.default.inferDimensionType(value); try { return CryptoEngine_1.default.hashValue(maskType, String(value), valueType); } catch (error) { throw new Error(`Failed to apply masking transformation '${maskType}' to field '${field.key}': ${error.message}`); } }; } } const TransformationEngine = new TransformationEngineClass(); exports.default = TransformationEngine;