@voiceflow/common
Version:
Junk drawer of utility functions
106 lines (105 loc) • 3.98 kB
JavaScript
import { READABLE_VARIABLE_REGEXP, VARIABLE_ONLY_REGEXP } from '../constants/index.js';
import _get from 'lodash/get.js';
export const variableReplacer = (match, inner, variables, modifier = (val) => val) => {
const { id, path } = splitVariableName(inner);
if (!(id in variables)) {
return match;
}
if (!path) {
return modifier(variables[id]);
}
try {
const variable = typeof variables[id] === 'string' ? JSON.parse(variables[id]) : variables[id];
return modifier(_get(variable, path, 0));
}
catch (err) {
if (err?.message.includes('is not valid JSON')) {
return 0;
}
throw err;
}
};
export const splitVariableName = (inner, options = { pathWithDotPrefix: false }) => {
const firstDotIndex = inner.indexOf('.');
const firstSquareBracketIndex = inner.indexOf('[');
if (firstDotIndex === -1 && firstSquareBracketIndex === -1) {
return { id: inner, path: '' };
}
if (firstDotIndex !== -1 && firstSquareBracketIndex === -1) {
return {
id: inner.slice(0, firstDotIndex),
path: options.pathWithDotPrefix ? inner.slice(firstDotIndex) : inner.slice(firstDotIndex + 1),
};
}
if (firstDotIndex === -1 && firstSquareBracketIndex !== -1) {
return {
id: inner.slice(0, firstSquareBracketIndex),
path: inner.slice(firstSquareBracketIndex),
};
}
if (firstDotIndex < firstSquareBracketIndex) {
return {
id: inner.slice(0, firstDotIndex),
path: options.pathWithDotPrefix ? inner.slice(firstDotIndex) : inner.slice(firstDotIndex + 1),
};
}
return {
id: inner.slice(0, firstSquareBracketIndex),
path: inner.slice(firstSquareBracketIndex),
};
};
export function replaceVariables(phrase, variables, { trim = true, modifier, keepTypeIfOnlyVariable = false, } = {}) {
const stringPhrase = typeof phrase === 'string' ? phrase : String(phrase ?? '');
const formattedPhrase = trim ? stringPhrase.trim() : phrase;
if (!formattedPhrase) {
return '';
}
if (keepTypeIfOnlyVariable && formattedPhrase.match(VARIABLE_ONLY_REGEXP)) {
// remove the curly braces {} from phrase to get the inner
const inner = formattedPhrase.slice(1, -1);
return variableReplacer(formattedPhrase, inner, variables, modifier);
}
return formattedPhrase.replace(READABLE_VARIABLE_REGEXP, (match, inner) => String(variableReplacer(match, inner, variables, modifier)));
}
// turn float variables to 4 decimal places
export const sanitizeVariables = (variables) => Object.entries(variables).reduce((acc, [key, value]) => {
if (typeof value === 'number' && !Number.isInteger(value)) {
acc[key] = value.toFixed(4);
}
else {
acc[key] = value;
}
return acc;
}, {});
export const transformStringVariableToNumber = (str) => {
if (typeof str === 'number') {
return str;
}
if (str?.startsWith('0') && str.length > 1) {
return str;
}
const number = Number(str);
return Number.isNaN(number) ? str : number;
};
export const deepVariableSubstitution = (bodyData, variableMap, options) => {
const _recurse = (subCollection) => {
if (!subCollection) {
return subCollection;
}
if (typeof subCollection === 'string') {
return replaceVariables(subCollection, variableMap, options);
}
if (Array.isArray(subCollection)) {
return subCollection.map((v) => _recurse(v));
}
if (typeof subCollection === 'object') {
Object.keys(subCollection).forEach((key) => {
// eslint-disable-next-line no-param-reassign
subCollection[key] = _recurse(subCollection[key]);
});
return subCollection;
}
return subCollection;
};
return _recurse(bodyData);
};