123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- /**
- * Module dependencies
- */
- var _ = require('@sailshq/lodash');
- /**
- * Eval stringified functions at the top-level or within value (use `typeSchema` to know
- * where to expect functions-- the "lamda" type)
- *
- * @param {*} value
- * @param {*} typeSchema
- * @return {*}
- */
- module.exports = function hydrate (value, typeSchema) {
- if (_.isUndefined(typeSchema)) {
- throw new Error('rttc.hydrate() requires a 2nd argument (`typeSchema`).');
- }
- // Deserialize any lamda functions that exist in the provided input value,
- // including at the top level.
- //
- // If this is a lamda type, or something which MIGHT contain a lamda type
- // (i.e. nested array or dictionary type schema), we must recursively iterate over the
- // type schema looking for lamda types, and when we find them, parse input values as
- // stringified functions, converting them to hydrated JavaScript functions.
- //
- // But otherwise, we just go ahead and bail.
- if (typeSchema !== 'lamda' &&
- (!_.isObject(typeSchema) || _.isEqual(typeSchema, []) || _.isEqual(typeSchema, {}))) {
- return value;
- }
- return (function parseLamdaInputValues(val, keysSoFar){
- var typeHere = keysSoFar.length > 0 ? _.get(typeSchema, keysSoFar.join('.')) : typeSchema;
- // If this is supposed to be an array or dictionary, recursively traverse the
- // next leg of the type schema
- //
- // (note that we don't need to worry about circular refs because we've already
- // ensured JSON serializability above)
- if (_.isArray(typeHere)) {
- // if the actual value does not have an array here as expected,
- // just stop looking for lamdas this direction (there obviously aren't any,
- // and it's not the job of this function to catch any validation issues)
- if (!_.isArray(val)) {
- return val;
- }
- // Special case for array generic (`[]`)
- if (typeHere.length === 0) {
- return val;
- }
- // Since a type schema array will only have one item, we must iterate over
- // the actual value:
- return _.reduce(val, function (memo, unused, index){
- memo.push(parseLamdaInputValues(val[index], keysSoFar.concat('0') ));
- return memo;
- }, []);
- }
- else if (_.isObject(typeHere)){
- // if the actual value does not have a dictionary here as expected,
- // just stop looking for lamdas this direction (there obviously aren't any,
- // and it's not the job of this function to catch any validation issues)
- if (!_.isObject(val)) {
- return val;
- }
- // Special case for dictionary generic (`{}`)
- if (_.keys(typeHere).length === 0) {
- return val;
- }
- return _.reduce(typeHere, function (memo, unused, subKey){
- // If the key from the type schema contains `.`, then fail with an error.
- if ((''+subKey).match(/\./)) {
- throw new Error('Keys containing dots (`.`) are not currently supported in the type schema for `rttc.hydrate`.');
- }
- memo[subKey] = parseLamdaInputValues(val[subKey], keysSoFar.concat(subKey));
- return memo;
- }, {});
- }
- // If this is supposed to be a lamda, and the actual value is a string,
- // parse a function out of it. If anything goes wrong, just pass the value
- // through as-is.
- else if (typeHere === 'lamda' && _.isString(val)) {
- try {
- var fn;
- // If the lamda string begins with "function", then we'll assume it's a
- // complete, stringified function.
- if (val.match(/^\s*function/)){
- eval('fn='+val);
- }
- // If the lamda string doesn't begin with "function", then we'll assume it
- // is a function body, and build a machine `fn` out of it (assumes standard
- // `fn` function signature)
- else {
- eval('fn=function(inputs, exits, env){'+val+'}');
- }
- return fn;
- }
- catch (e){
- // Could not parse usable lamda function from provided string-
- // so just pass the value through as-is.
- return val;
- }
- }
- // Otherwise, just return what we've got
- return val;
- })(value, []);
- };
|