@forzalabs/remora
Version:
A powerful CLI tool for seamless data translation.
278 lines (277 loc) • 14.6 kB
JavaScript
;
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;