UNPKG

@voiceflow/common

Version:

Junk drawer of utility functions

106 lines (105 loc) 3.98 kB
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); };