XingFramework/Relayer

View on GitHub
dist/cjs/relayer/jsonpath.js

Summary

Maintainability
F
3 days
Test Coverage
/* JSONPath 0.8.0 - XPath for JSON
 *
 * Copyright (c) 2007 Stefan Goessner (goessner.net)
 * Licensed under the MIT (MIT-LICENSE.txt) licence.
 */

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = jsonPath;
if (!Array.isArray) {
  Array.isArray = function (vArg) {
    return Object.prototype.toString.call(vArg) === "[object Array]";
  };
}

var cache = {};

function push(arr, elem) {
  arr = arr.slice();arr.push(elem);return arr;
}
function unshift(elem, arr) {
  arr = arr.slice();arr.unshift(elem);return arr;
}

function jsonPath(obj, expr, arg) {
  var P = {
    resultType: arg && arg.resultType || "value",
    flatten: arg && arg.flatten || false,
    wrap: arg && arg.hasOwnProperty("wrap") ? arg.wrap : true,
    sandbox: arg && arg.sandbox ? arg.sandbox : {},
    normalize: function normalize(expr) {
      if (cache[expr]) {
        return cache[expr];
      }
      var subx = [];
      var normalized = expr.replace(/[\['](\??\(.*?\))[\]']/g, function ($0, $1) {
        return "[#" + (subx.push($1) - 1) + "]";
      }).replace(/'?\.'?|\['?/g, ";").replace(/(;)?(\^+)(;)?/g, function (_, front, ups, back) {
        return ";" + ups.split("").join(";") + ";";
      }).replace(/;;;|;;/g, ";..;").replace(/;$|'?\]|'$/g, "");
      var exprList = normalized.split(";").map(function (expr) {
        var match = expr.match(/#([0-9]+)/);
        return !match || !match[1] ? expr : subx[match[1]];
      });
      cache[expr] = exprList;
      return exprList;
    },
    asPath: function asPath(path) {
      var x = path,
          p = "$";
      for (var i = 1, n = x.length; i < n; i++) {
        p += /^[0-9*]+$/.test(x[i]) ? "[" + x[i] + "]" : "['" + x[i] + "']";
      }
      return p;
    },
    trace: function trace(expr, val, path) {
      function addRet(elems) {
        ret = ret.concat(elems);
      }
      // no expr to follow? return path and value as the result of this trace branch
      if (!expr.length) {
        return [{ path: path, value: val }];
      }

      var loc = expr[0],
          x = expr.slice(1);
      // the parent sel computation is handled in the frame above using the
      // ancestor object of val
      if (loc === "^") {
        if (path.length) {
          return [{ path: path.slice(0, -1), expr: x, isParentSelector: true }];
        } else {
          return [];
        }
      }

      // we need to gather the return value of recursive trace calls in order to
      // do the parent sel computation.
      var ret = [];

      if (val && val.hasOwnProperty(loc)) {
        // simple case, directly follow property
        addRet(P.trace(x, val[loc], push(path, loc)));
      } else if (loc === "*") {
        // any property
        P.walk(loc, x, val, path, function (m, l, x, v, p) {
          addRet(P.trace(unshift(m, x), v, p));
        });
      } else if (loc === "..") {
        // all chid properties
        addRet(P.trace(x, val, path));
        P.walk(loc, x, val, path, function (m, l, x, v, p) {
          if (typeof v[m] === "object") {
            addRet(P.trace(unshift("..", x), v[m], push(p, m)));
          }
        });
      } else if (loc[0] === "(") {
        // [(expr)]
        addRet(P.trace(unshift(P.evaluate(loc, val, path[path.length], path), x), val, path));
      } else if (loc.indexOf("?(") === 0) {
        // [?(expr)]
        P.walk(loc, x, val, path, function (m, l, x, v, p) {
          if (P.evaluate(l.replace(/^\?\((.*?)\)$/, "$1"), v[m], m, path)) {
            addRet(P.trace(unshift(m, x), v, p));
          }
        });
      } else if (loc.indexOf(",") > -1) {
        // [name1,name2,...]
        for (var parts = loc.split(","), i = 0; i < parts.length; i++) {
          addRet(P.trace(unshift(parts[i], x), val, path));
        }
      } else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) {
        // [start:end:step]  python slice syntax
        addRet(P.slice(loc, x, val, path));
      }

      // we check the resulting values for parent selections. for parent
      // selections we discard the value object and continue the trace with the
      // current val object
      return ret.reduce(function (all, ea) {
        return all.concat(ea.isParentSelector ? P.trace(ea.expr, val, ea.path) : [ea]);
      }, []);
    },
    walk: function walk(loc, expr, val, path, f) {
      if (Array.isArray(val)) {
        for (var i = 0, n = val.length; i < n; i++) {
          f(i, loc, expr, val, path);
        }
      } else if (typeof val === "object") {
        for (var m in val) {
          if (val.hasOwnProperty(m)) {
            f(m, loc, expr, val, path);
          }
        }
      }
    },
    slice: function slice(loc, expr, val, path) {
      if (!Array.isArray(val)) {
        return;
      }
      var len = val.length,
          parts = loc.split(":"),
          start = parts[0] && parseInt(parts[0]) || 0,
          end = parts[1] && parseInt(parts[1]) || len,
          step = parts[2] && parseInt(parts[2]) || 1;
      start = start < 0 ? Math.max(0, start + len) : Math.min(len, start);
      end = end < 0 ? Math.max(0, end + len) : Math.min(len, end);
      var ret = [];
      for (var i = start; i < end; i += step) {
        ret = ret.concat(P.trace(unshift(i, expr), val, path));
      }
      return ret;
    },
    evaluate: function evaluate(code, _v, _vname, path) {
      if (false) {
        //supporting this requires allowing JS eval - very dodgy
        if (!$ || !_v) {
          return false;
        }
        if (code.indexOf("@path") > -1) {
          P.sandbox["_path"] = P.asPath(path.concat([_vname]));
          code = code.replace(/@path/g, "_path");
        }
        if (code.indexOf("@") > -1) {
          P.sandbox["_v"] = _v;
          code = code.replace(/@/g, "_v");
        }
        try {
          return vm.runInNewContext(code, P.sandbox);
        } catch (e) {
          console.log(e);
          throw new Error("jsonPath: " + e.message + ": " + code);
        }
      } else {
        var msg = "Refusing to eval: '" + code + "'";
        console.log(msg);
        throw new Error("jsonPath: " + msg);
      }
    }
  };

  var $ = obj;
  var resultType = P.resultType.toLowerCase();
  if (obj) {
    if (expr && (resultType == "value" || resultType == "path")) {
      var exprList = P.normalize(expr);
      if (exprList[0] === "$" && exprList.length > 1) {
        exprList.shift();
      }
      var result = P.trace(exprList, obj, ["$"]);
      result = result.filter(function (ea) {
        return ea && !ea.isParentSelector;
      });
      if (!result.length) {
        if (P.wrap) {
          return [];
        } else {
          return false;
        }
      }

      if (result.length === 1 && !P.wrap && (resultType == "path" || !Array.isArray(result[0].value))) {
        var ret = result[0][resultType];
        if (typeof ret === "undefined") {
          return false;
        } else {
          return ret;
        }
      }
      return result.reduce(function (result, ea) {
        var valOrPath = ea[resultType];
        // For our purposes we don't return paths but rather arrays of segments
        // if (resultType === 'path'){ valOrPath = P.asPath(valOrPath); }
        if (P.flatten && Array.isArray(valOrPath)) {
          result = result.concat(valOrPath);
        } else {
          result.push(valOrPath);
        }
        return result;
      }, []);
    }
  } else {
    return obj;
  }
}

module.exports = exports["default"];