Update to v13 and add queue and completely change code

This commit is contained in:
a 2021-12-06 16:34:00 +01:00
parent dcef23d0ed
commit 55a38726a3
6706 changed files with 424137 additions and 61608 deletions

1056
node_modules/jsonpath-plus/dist/index-browser-esm.js generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1066
node_modules/jsonpath-plus/dist/index-browser-umd.js generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

735
node_modules/jsonpath-plus/dist/index-node-cjs.js generated vendored Normal file
View file

@ -0,0 +1,735 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var vm = require('vm');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var vm__default = /*#__PURE__*/_interopDefaultLegacy(vm);
const {
hasOwnProperty: hasOwnProp
} = Object.prototype;
/**
* @typedef {null|boolean|number|string|PlainObject|GenericArray} JSONObject
*/
/**
* Copies array and then pushes item into it.
* @param {GenericArray} arr Array to copy and into which to push
* @param {any} item Array item to add (to end)
* @returns {GenericArray} Copy of the original array
*/
function push(arr, item) {
arr = arr.slice();
arr.push(item);
return arr;
}
/**
* Copies array and then unshifts item into it.
* @param {any} item Array item to add (to beginning)
* @param {GenericArray} arr Array to copy and into which to unshift
* @returns {GenericArray} Copy of the original array
*/
function unshift(item, arr) {
arr = arr.slice();
arr.unshift(item);
return arr;
}
/**
* Caught when JSONPath is used without `new` but rethrown if with `new`
* @extends Error
*/
class NewError extends Error {
/**
* @param {any} value The evaluated scalar value
*/
constructor(value) {
super('JSONPath should not be called with "new" (it prevents return ' + 'of (unwrapped) scalar values)');
this.avoidNew = true;
this.value = value;
this.name = 'NewError';
}
}
/**
* @typedef {PlainObject} ReturnObject
* @property {string} path
* @property {JSONObject} value
* @property {PlainObject|GenericArray} parent
* @property {string} parentProperty
*/
/**
* @callback JSONPathCallback
* @param {string|PlainObject} preferredOutput
* @param {"value"|"property"} type
* @param {ReturnObject} fullRetObj
* @returns {void}
*/
/**
* @callback OtherTypeCallback
* @param {JSONObject} val
* @param {string} path
* @param {PlainObject|GenericArray} parent
* @param {string} parentPropName
* @returns {boolean}
*/
/* eslint-disable max-len -- Can make multiline type after https://github.com/syavorsky/comment-parser/issues/109 */
/**
* @typedef {PlainObject} JSONPathOptions
* @property {JSON} json
* @property {string|string[]} path
* @property {"value"|"path"|"pointer"|"parent"|"parentProperty"|"all"} [resultType="value"]
* @property {boolean} [flatten=false]
* @property {boolean} [wrap=true]
* @property {PlainObject} [sandbox={}]
* @property {boolean} [preventEval=false]
* @property {PlainObject|GenericArray|null} [parent=null]
* @property {string|null} [parentProperty=null]
* @property {JSONPathCallback} [callback]
* @property {OtherTypeCallback} [otherTypeCallback] Defaults to
* function which throws on encountering `@other`
* @property {boolean} [autostart=true]
*/
/* eslint-enable max-len -- Can make multiline type after https://github.com/syavorsky/comment-parser/issues/109 */
/**
* @param {string|JSONPathOptions} opts If a string, will be treated as `expr`
* @param {string} [expr] JSON path to evaluate
* @param {JSON} [obj] JSON object to evaluate against
* @param {JSONPathCallback} [callback] Passed 3 arguments: 1) desired payload
* per `resultType`, 2) `"value"|"property"`, 3) Full returned object with
* all payloads
* @param {OtherTypeCallback} [otherTypeCallback] If `@other()` is at the end
* of one's query, this will be invoked with the value of the item, its
* path, its parent, and its parent's property name, and it should return
* a boolean indicating whether the supplied value belongs to the "other"
* type or not (or it may handle transformations and return `false`).
* @returns {JSONPath}
* @class
*/
function JSONPath(opts, expr, obj, callback, otherTypeCallback) {
// eslint-disable-next-line no-restricted-syntax
if (!(this instanceof JSONPath)) {
try {
return new JSONPath(opts, expr, obj, callback, otherTypeCallback);
} catch (e) {
if (!e.avoidNew) {
throw e;
}
return e.value;
}
}
if (typeof opts === 'string') {
otherTypeCallback = callback;
callback = obj;
obj = expr;
expr = opts;
opts = null;
}
const optObj = opts && typeof opts === 'object';
opts = opts || {};
this.json = opts.json || obj;
this.path = opts.path || expr;
this.resultType = opts.resultType || 'value';
this.flatten = opts.flatten || false;
this.wrap = hasOwnProp.call(opts, 'wrap') ? opts.wrap : true;
this.sandbox = opts.sandbox || {};
this.preventEval = opts.preventEval || false;
this.parent = opts.parent || null;
this.parentProperty = opts.parentProperty || null;
this.callback = opts.callback || callback || null;
this.otherTypeCallback = opts.otherTypeCallback || otherTypeCallback || function () {
throw new TypeError('You must supply an otherTypeCallback callback option ' + 'with the @other() operator.');
};
if (opts.autostart !== false) {
const args = {
path: optObj ? opts.path : expr
};
if (!optObj) {
args.json = obj;
} else if ('json' in opts) {
args.json = opts.json;
}
const ret = this.evaluate(args);
if (!ret || typeof ret !== 'object') {
throw new NewError(ret);
}
return ret;
}
} // PUBLIC METHODS
JSONPath.prototype.evaluate = function (expr, json, callback, otherTypeCallback) {
let currParent = this.parent,
currParentProperty = this.parentProperty;
let {
flatten,
wrap
} = this;
this.currResultType = this.resultType;
this.currPreventEval = this.preventEval;
this.currSandbox = this.sandbox;
callback = callback || this.callback;
this.currOtherTypeCallback = otherTypeCallback || this.otherTypeCallback;
json = json || this.json;
expr = expr || this.path;
if (expr && typeof expr === 'object' && !Array.isArray(expr)) {
if (!expr.path && expr.path !== '') {
throw new TypeError('You must supply a "path" property when providing an object ' + 'argument to JSONPath.evaluate().');
}
if (!hasOwnProp.call(expr, 'json')) {
throw new TypeError('You must supply a "json" property when providing an object ' + 'argument to JSONPath.evaluate().');
}
({
json
} = expr);
flatten = hasOwnProp.call(expr, 'flatten') ? expr.flatten : flatten;
this.currResultType = hasOwnProp.call(expr, 'resultType') ? expr.resultType : this.currResultType;
this.currSandbox = hasOwnProp.call(expr, 'sandbox') ? expr.sandbox : this.currSandbox;
wrap = hasOwnProp.call(expr, 'wrap') ? expr.wrap : wrap;
this.currPreventEval = hasOwnProp.call(expr, 'preventEval') ? expr.preventEval : this.currPreventEval;
callback = hasOwnProp.call(expr, 'callback') ? expr.callback : callback;
this.currOtherTypeCallback = hasOwnProp.call(expr, 'otherTypeCallback') ? expr.otherTypeCallback : this.currOtherTypeCallback;
currParent = hasOwnProp.call(expr, 'parent') ? expr.parent : currParent;
currParentProperty = hasOwnProp.call(expr, 'parentProperty') ? expr.parentProperty : currParentProperty;
expr = expr.path;
}
currParent = currParent || null;
currParentProperty = currParentProperty || null;
if (Array.isArray(expr)) {
expr = JSONPath.toPathString(expr);
}
if (!expr && expr !== '' || !json) {
return undefined;
}
const exprList = JSONPath.toPathArray(expr);
if (exprList[0] === '$' && exprList.length > 1) {
exprList.shift();
}
this._hasParentSelector = null;
const result = this._trace(exprList, json, ['$'], currParent, currParentProperty, callback).filter(function (ea) {
return ea && !ea.isParentSelector;
});
if (!result.length) {
return wrap ? [] : undefined;
}
if (!wrap && result.length === 1 && !result[0].hasArrExpr) {
return this._getPreferredOutput(result[0]);
}
return result.reduce((rslt, ea) => {
const valOrPath = this._getPreferredOutput(ea);
if (flatten && Array.isArray(valOrPath)) {
rslt = rslt.concat(valOrPath);
} else {
rslt.push(valOrPath);
}
return rslt;
}, []);
}; // PRIVATE METHODS
JSONPath.prototype._getPreferredOutput = function (ea) {
const resultType = this.currResultType;
switch (resultType) {
case 'all':
{
const path = Array.isArray(ea.path) ? ea.path : JSONPath.toPathArray(ea.path);
ea.pointer = JSONPath.toPointer(path);
ea.path = typeof ea.path === 'string' ? ea.path : JSONPath.toPathString(ea.path);
return ea;
}
case 'value':
case 'parent':
case 'parentProperty':
return ea[resultType];
case 'path':
return JSONPath.toPathString(ea[resultType]);
case 'pointer':
return JSONPath.toPointer(ea.path);
default:
throw new TypeError('Unknown result type');
}
};
JSONPath.prototype._handleCallback = function (fullRetObj, callback, type) {
if (callback) {
const preferredOutput = this._getPreferredOutput(fullRetObj);
fullRetObj.path = typeof fullRetObj.path === 'string' ? fullRetObj.path : JSONPath.toPathString(fullRetObj.path); // eslint-disable-next-line node/callback-return
callback(preferredOutput, type, fullRetObj);
}
};
/**
*
* @param {string} expr
* @param {JSONObject} val
* @param {string} path
* @param {PlainObject|GenericArray} parent
* @param {string} parentPropName
* @param {JSONPathCallback} callback
* @param {boolean} hasArrExpr
* @param {boolean} literalPriority
* @returns {ReturnObject|ReturnObject[]}
*/
JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, callback, hasArrExpr, literalPriority) {
// No expr to follow? return path and value as the result of
// this trace branch
let retObj;
if (!expr.length) {
retObj = {
path,
value: val,
parent,
parentProperty: parentPropName,
hasArrExpr
};
this._handleCallback(retObj, callback, 'value');
return retObj;
}
const loc = expr[0],
x = expr.slice(1); // We need to gather the return value of recursive trace calls in order to
// do the parent sel computation.
const ret = [];
/**
*
* @param {ReturnObject|ReturnObject[]} elems
* @returns {void}
*/
function addRet(elems) {
if (Array.isArray(elems)) {
// This was causing excessive stack size in Node (with or
// without Babel) against our performance test:
// `ret.push(...elems);`
elems.forEach(t => {
ret.push(t);
});
} else {
ret.push(elems);
}
}
if ((typeof loc !== 'string' || literalPriority) && val && hasOwnProp.call(val, loc)) {
// simple case--directly follow property
addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, hasArrExpr));
} else if (loc === '*') {
// all child properties
this._walk(loc, x, val, path, parent, parentPropName, callback, (m, l, _x, v, p, par, pr, cb) => {
addRet(this._trace(unshift(m, _x), v, p, par, pr, cb, true, true));
});
} else if (loc === '..') {
// all descendent parent properties
// Check remaining expression with val's immediate children
addRet(this._trace(x, val, path, parent, parentPropName, callback, hasArrExpr));
this._walk(loc, x, val, path, parent, parentPropName, callback, (m, l, _x, v, p, par, pr, cb) => {
// We don't join m and x here because we only want parents,
// not scalar values
if (typeof v[m] === 'object') {
// Keep going with recursive descent on val's
// object children
addRet(this._trace(unshift(l, _x), v[m], push(p, m), v, m, cb, true));
}
}); // The parent sel computation is handled in the frame above using the
// ancestor object of val
} else if (loc === '^') {
// This is not a final endpoint, so we do not invoke the callback here
this._hasParentSelector = true;
return {
path: path.slice(0, -1),
expr: x,
isParentSelector: true
};
} else if (loc === '~') {
// property name
retObj = {
path: push(path, loc),
value: parentPropName,
parent,
parentProperty: null
};
this._handleCallback(retObj, callback, 'property');
return retObj;
} else if (loc === '$') {
// root only
addRet(this._trace(x, val, path, null, null, callback, hasArrExpr));
} else if (/^(-?\d*):(-?\d*):?(\d*)$/u.test(loc)) {
// [start:end:step] Python slice syntax
addRet(this._slice(loc, x, val, path, parent, parentPropName, callback));
} else if (loc.indexOf('?(') === 0) {
// [?(expr)] (filtering)
if (this.currPreventEval) {
throw new Error('Eval [?(expr)] prevented in JSONPath expression.');
}
this._walk(loc, x, val, path, parent, parentPropName, callback, (m, l, _x, v, p, par, pr, cb) => {
if (this._eval(l.replace(/^\?\((.*?)\)$/u, '$1'), v[m], m, p, par, pr)) {
addRet(this._trace(unshift(m, _x), v, p, par, pr, cb, true));
}
});
} else if (loc[0] === '(') {
// [(expr)] (dynamic property/index)
if (this.currPreventEval) {
throw new Error('Eval [(expr)] prevented in JSONPath expression.');
} // As this will resolve to a property name (but we don't know it
// yet), property and parent information is relative to the
// parent of the property to which this expression will resolve
addRet(this._trace(unshift(this._eval(loc, val, path[path.length - 1], path.slice(0, -1), parent, parentPropName), x), val, path, parent, parentPropName, callback, hasArrExpr));
} else if (loc[0] === '@') {
// value type: @boolean(), etc.
let addType = false;
const valueType = loc.slice(1, -2);
switch (valueType) {
case 'scalar':
if (!val || !['object', 'function'].includes(typeof val)) {
addType = true;
}
break;
case 'boolean':
case 'string':
case 'undefined':
case 'function':
// eslint-disable-next-line valid-typeof
if (typeof val === valueType) {
addType = true;
}
break;
case 'integer':
if (Number.isFinite(val) && !(val % 1)) {
addType = true;
}
break;
case 'number':
if (Number.isFinite(val)) {
addType = true;
}
break;
case 'nonFinite':
if (typeof val === 'number' && !Number.isFinite(val)) {
addType = true;
}
break;
case 'object':
// eslint-disable-next-line valid-typeof
if (val && typeof val === valueType) {
addType = true;
}
break;
case 'array':
if (Array.isArray(val)) {
addType = true;
}
break;
case 'other':
addType = this.currOtherTypeCallback(val, path, parent, parentPropName);
break;
case 'null':
if (val === null) {
addType = true;
}
break;
/* istanbul ignore next */
default:
throw new TypeError('Unknown value type ' + valueType);
}
if (addType) {
retObj = {
path,
value: val,
parent,
parentProperty: parentPropName
};
this._handleCallback(retObj, callback, 'value');
return retObj;
} // `-escaped property
} else if (loc[0] === '`' && val && hasOwnProp.call(val, loc.slice(1))) {
const locProp = loc.slice(1);
addRet(this._trace(x, val[locProp], push(path, locProp), val, locProp, callback, hasArrExpr, true));
} else if (loc.includes(',')) {
// [name1,name2,...]
const parts = loc.split(',');
for (const part of parts) {
addRet(this._trace(unshift(part, x), val, path, parent, parentPropName, callback, true));
} // simple case--directly follow property
} else if (!literalPriority && val && hasOwnProp.call(val, loc)) {
addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, hasArrExpr, true));
} // 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
if (this._hasParentSelector) {
for (let t = 0; t < ret.length; t++) {
const rett = ret[t];
if (rett && rett.isParentSelector) {
const tmp = this._trace(rett.expr, val, rett.path, parent, parentPropName, callback, hasArrExpr);
if (Array.isArray(tmp)) {
ret[t] = tmp[0];
const tl = tmp.length;
for (let tt = 1; tt < tl; tt++) {
t++;
ret.splice(t, 0, tmp[tt]);
}
} else {
ret[t] = tmp;
}
}
}
}
return ret;
};
JSONPath.prototype._walk = function (loc, expr, val, path, parent, parentPropName, callback, f) {
if (Array.isArray(val)) {
const n = val.length;
for (let i = 0; i < n; i++) {
f(i, loc, expr, val, path, parent, parentPropName, callback);
}
} else if (val && typeof val === 'object') {
Object.keys(val).forEach(m => {
f(m, loc, expr, val, path, parent, parentPropName, callback);
});
}
};
JSONPath.prototype._slice = function (loc, expr, val, path, parent, parentPropName, callback) {
if (!Array.isArray(val)) {
return undefined;
}
const len = val.length,
parts = loc.split(':'),
step = parts[2] && Number.parseInt(parts[2]) || 1;
let start = parts[0] && Number.parseInt(parts[0]) || 0,
end = parts[1] && Number.parseInt(parts[1]) || len;
start = start < 0 ? Math.max(0, start + len) : Math.min(len, start);
end = end < 0 ? Math.max(0, end + len) : Math.min(len, end);
const ret = [];
for (let i = start; i < end; i += step) {
const tmp = this._trace(unshift(i, expr), val, path, parent, parentPropName, callback, true); // Should only be possible to be an array here since first part of
// ``unshift(i, expr)` passed in above would not be empty, nor `~`,
// nor begin with `@` (as could return objects)
// This was causing excessive stack size in Node (with or
// without Babel) against our performance test: `ret.push(...tmp);`
tmp.forEach(t => {
ret.push(t);
});
}
return ret;
};
JSONPath.prototype._eval = function (code, _v, _vname, path, parent, parentPropName) {
if (code.includes('@parentProperty')) {
this.currSandbox._$_parentProperty = parentPropName;
code = code.replace(/@parentProperty/gu, '_$_parentProperty');
}
if (code.includes('@parent')) {
this.currSandbox._$_parent = parent;
code = code.replace(/@parent/gu, '_$_parent');
}
if (code.includes('@property')) {
this.currSandbox._$_property = _vname;
code = code.replace(/@property/gu, '_$_property');
}
if (code.includes('@path')) {
this.currSandbox._$_path = JSONPath.toPathString(path.concat([_vname]));
code = code.replace(/@path/gu, '_$_path');
}
if (code.includes('@root')) {
this.currSandbox._$_root = this.json;
code = code.replace(/@root/gu, '_$_root');
}
if (/@([.\s)[])/u.test(code)) {
this.currSandbox._$_v = _v;
code = code.replace(/@([.\s)[])/gu, '_$_v$1');
}
try {
return this.vm.runInNewContext(code, this.currSandbox);
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
throw new Error('jsonPath: ' + e.message + ': ' + code);
}
}; // PUBLIC CLASS PROPERTIES AND METHODS
// Could store the cache object itself
JSONPath.cache = {};
/**
* @param {string[]} pathArr Array to convert
* @returns {string} The path string
*/
JSONPath.toPathString = function (pathArr) {
const x = pathArr,
n = x.length;
let p = '$';
for (let i = 1; i < n; i++) {
if (!/^(~|\^|@.*?\(\))$/u.test(x[i])) {
p += /^[0-9*]+$/u.test(x[i]) ? '[' + x[i] + ']' : "['" + x[i] + "']";
}
}
return p;
};
/**
* @param {string} pointer JSON Path
* @returns {string} JSON Pointer
*/
JSONPath.toPointer = function (pointer) {
const x = pointer,
n = x.length;
let p = '';
for (let i = 1; i < n; i++) {
if (!/^(~|\^|@.*?\(\))$/u.test(x[i])) {
p += '/' + x[i].toString().replace(/~/gu, '~0').replace(/\//gu, '~1');
}
}
return p;
};
/**
* @param {string} expr Expression to convert
* @returns {string[]}
*/
JSONPath.toPathArray = function (expr) {
const {
cache
} = JSONPath;
if (cache[expr]) {
return cache[expr].concat();
}
const subx = [];
const normalized = expr // Properties
.replace(/@(?:null|boolean|number|string|integer|undefined|nonFinite|scalar|array|object|function|other)\(\)/gu, ';$&;') // Parenthetical evaluations (filtering and otherwise), directly
// within brackets or single quotes
.replace(/[['](\??\(.*?\))[\]']/gu, function ($0, $1) {
return '[#' + (subx.push($1) - 1) + ']';
}) // Escape periods and tildes within properties
.replace(/\['([^'\]]*)'\]/gu, function ($0, prop) {
return "['" + prop.replace(/\./gu, '%@%').replace(/~/gu, '%%@@%%') + "']";
}) // Properties operator
.replace(/~/gu, ';~;') // Split by property boundaries
.replace(/'?\.'?(?![^[]*\])|\['?/gu, ';') // Reinsert periods within properties
.replace(/%@%/gu, '.') // Reinsert tildes within properties
.replace(/%%@@%%/gu, '~') // Parent
.replace(/(?:;)?(\^+)(?:;)?/gu, function ($0, ups) {
return ';' + ups.split('').join(';') + ';';
}) // Descendents
.replace(/;;;|;;/gu, ';..;') // Remove trailing
.replace(/;$|'?\]|'$/gu, '');
const exprList = normalized.split(';').map(function (exp) {
const match = exp.match(/#(\d+)/u);
return !match || !match[1] ? exp : subx[match[1]];
});
cache[expr] = exprList;
return cache[expr].concat();
};
JSONPath.prototype.vm = vm__default['default'];
exports.JSONPath = JSONPath;

727
node_modules/jsonpath-plus/dist/index-node-esm.mjs generated vendored Normal file
View file

@ -0,0 +1,727 @@
import vm from 'vm';
const {
hasOwnProperty: hasOwnProp
} = Object.prototype;
/**
* @typedef {null|boolean|number|string|PlainObject|GenericArray} JSONObject
*/
/**
* Copies array and then pushes item into it.
* @param {GenericArray} arr Array to copy and into which to push
* @param {any} item Array item to add (to end)
* @returns {GenericArray} Copy of the original array
*/
function push(arr, item) {
arr = arr.slice();
arr.push(item);
return arr;
}
/**
* Copies array and then unshifts item into it.
* @param {any} item Array item to add (to beginning)
* @param {GenericArray} arr Array to copy and into which to unshift
* @returns {GenericArray} Copy of the original array
*/
function unshift(item, arr) {
arr = arr.slice();
arr.unshift(item);
return arr;
}
/**
* Caught when JSONPath is used without `new` but rethrown if with `new`
* @extends Error
*/
class NewError extends Error {
/**
* @param {any} value The evaluated scalar value
*/
constructor(value) {
super('JSONPath should not be called with "new" (it prevents return ' + 'of (unwrapped) scalar values)');
this.avoidNew = true;
this.value = value;
this.name = 'NewError';
}
}
/**
* @typedef {PlainObject} ReturnObject
* @property {string} path
* @property {JSONObject} value
* @property {PlainObject|GenericArray} parent
* @property {string} parentProperty
*/
/**
* @callback JSONPathCallback
* @param {string|PlainObject} preferredOutput
* @param {"value"|"property"} type
* @param {ReturnObject} fullRetObj
* @returns {void}
*/
/**
* @callback OtherTypeCallback
* @param {JSONObject} val
* @param {string} path
* @param {PlainObject|GenericArray} parent
* @param {string} parentPropName
* @returns {boolean}
*/
/* eslint-disable max-len -- Can make multiline type after https://github.com/syavorsky/comment-parser/issues/109 */
/**
* @typedef {PlainObject} JSONPathOptions
* @property {JSON} json
* @property {string|string[]} path
* @property {"value"|"path"|"pointer"|"parent"|"parentProperty"|"all"} [resultType="value"]
* @property {boolean} [flatten=false]
* @property {boolean} [wrap=true]
* @property {PlainObject} [sandbox={}]
* @property {boolean} [preventEval=false]
* @property {PlainObject|GenericArray|null} [parent=null]
* @property {string|null} [parentProperty=null]
* @property {JSONPathCallback} [callback]
* @property {OtherTypeCallback} [otherTypeCallback] Defaults to
* function which throws on encountering `@other`
* @property {boolean} [autostart=true]
*/
/* eslint-enable max-len -- Can make multiline type after https://github.com/syavorsky/comment-parser/issues/109 */
/**
* @param {string|JSONPathOptions} opts If a string, will be treated as `expr`
* @param {string} [expr] JSON path to evaluate
* @param {JSON} [obj] JSON object to evaluate against
* @param {JSONPathCallback} [callback] Passed 3 arguments: 1) desired payload
* per `resultType`, 2) `"value"|"property"`, 3) Full returned object with
* all payloads
* @param {OtherTypeCallback} [otherTypeCallback] If `@other()` is at the end
* of one's query, this will be invoked with the value of the item, its
* path, its parent, and its parent's property name, and it should return
* a boolean indicating whether the supplied value belongs to the "other"
* type or not (or it may handle transformations and return `false`).
* @returns {JSONPath}
* @class
*/
function JSONPath(opts, expr, obj, callback, otherTypeCallback) {
// eslint-disable-next-line no-restricted-syntax
if (!(this instanceof JSONPath)) {
try {
return new JSONPath(opts, expr, obj, callback, otherTypeCallback);
} catch (e) {
if (!e.avoidNew) {
throw e;
}
return e.value;
}
}
if (typeof opts === 'string') {
otherTypeCallback = callback;
callback = obj;
obj = expr;
expr = opts;
opts = null;
}
const optObj = opts && typeof opts === 'object';
opts = opts || {};
this.json = opts.json || obj;
this.path = opts.path || expr;
this.resultType = opts.resultType || 'value';
this.flatten = opts.flatten || false;
this.wrap = hasOwnProp.call(opts, 'wrap') ? opts.wrap : true;
this.sandbox = opts.sandbox || {};
this.preventEval = opts.preventEval || false;
this.parent = opts.parent || null;
this.parentProperty = opts.parentProperty || null;
this.callback = opts.callback || callback || null;
this.otherTypeCallback = opts.otherTypeCallback || otherTypeCallback || function () {
throw new TypeError('You must supply an otherTypeCallback callback option ' + 'with the @other() operator.');
};
if (opts.autostart !== false) {
const args = {
path: optObj ? opts.path : expr
};
if (!optObj) {
args.json = obj;
} else if ('json' in opts) {
args.json = opts.json;
}
const ret = this.evaluate(args);
if (!ret || typeof ret !== 'object') {
throw new NewError(ret);
}
return ret;
}
} // PUBLIC METHODS
JSONPath.prototype.evaluate = function (expr, json, callback, otherTypeCallback) {
let currParent = this.parent,
currParentProperty = this.parentProperty;
let {
flatten,
wrap
} = this;
this.currResultType = this.resultType;
this.currPreventEval = this.preventEval;
this.currSandbox = this.sandbox;
callback = callback || this.callback;
this.currOtherTypeCallback = otherTypeCallback || this.otherTypeCallback;
json = json || this.json;
expr = expr || this.path;
if (expr && typeof expr === 'object' && !Array.isArray(expr)) {
if (!expr.path && expr.path !== '') {
throw new TypeError('You must supply a "path" property when providing an object ' + 'argument to JSONPath.evaluate().');
}
if (!hasOwnProp.call(expr, 'json')) {
throw new TypeError('You must supply a "json" property when providing an object ' + 'argument to JSONPath.evaluate().');
}
({
json
} = expr);
flatten = hasOwnProp.call(expr, 'flatten') ? expr.flatten : flatten;
this.currResultType = hasOwnProp.call(expr, 'resultType') ? expr.resultType : this.currResultType;
this.currSandbox = hasOwnProp.call(expr, 'sandbox') ? expr.sandbox : this.currSandbox;
wrap = hasOwnProp.call(expr, 'wrap') ? expr.wrap : wrap;
this.currPreventEval = hasOwnProp.call(expr, 'preventEval') ? expr.preventEval : this.currPreventEval;
callback = hasOwnProp.call(expr, 'callback') ? expr.callback : callback;
this.currOtherTypeCallback = hasOwnProp.call(expr, 'otherTypeCallback') ? expr.otherTypeCallback : this.currOtherTypeCallback;
currParent = hasOwnProp.call(expr, 'parent') ? expr.parent : currParent;
currParentProperty = hasOwnProp.call(expr, 'parentProperty') ? expr.parentProperty : currParentProperty;
expr = expr.path;
}
currParent = currParent || null;
currParentProperty = currParentProperty || null;
if (Array.isArray(expr)) {
expr = JSONPath.toPathString(expr);
}
if (!expr && expr !== '' || !json) {
return undefined;
}
const exprList = JSONPath.toPathArray(expr);
if (exprList[0] === '$' && exprList.length > 1) {
exprList.shift();
}
this._hasParentSelector = null;
const result = this._trace(exprList, json, ['$'], currParent, currParentProperty, callback).filter(function (ea) {
return ea && !ea.isParentSelector;
});
if (!result.length) {
return wrap ? [] : undefined;
}
if (!wrap && result.length === 1 && !result[0].hasArrExpr) {
return this._getPreferredOutput(result[0]);
}
return result.reduce((rslt, ea) => {
const valOrPath = this._getPreferredOutput(ea);
if (flatten && Array.isArray(valOrPath)) {
rslt = rslt.concat(valOrPath);
} else {
rslt.push(valOrPath);
}
return rslt;
}, []);
}; // PRIVATE METHODS
JSONPath.prototype._getPreferredOutput = function (ea) {
const resultType = this.currResultType;
switch (resultType) {
case 'all':
{
const path = Array.isArray(ea.path) ? ea.path : JSONPath.toPathArray(ea.path);
ea.pointer = JSONPath.toPointer(path);
ea.path = typeof ea.path === 'string' ? ea.path : JSONPath.toPathString(ea.path);
return ea;
}
case 'value':
case 'parent':
case 'parentProperty':
return ea[resultType];
case 'path':
return JSONPath.toPathString(ea[resultType]);
case 'pointer':
return JSONPath.toPointer(ea.path);
default:
throw new TypeError('Unknown result type');
}
};
JSONPath.prototype._handleCallback = function (fullRetObj, callback, type) {
if (callback) {
const preferredOutput = this._getPreferredOutput(fullRetObj);
fullRetObj.path = typeof fullRetObj.path === 'string' ? fullRetObj.path : JSONPath.toPathString(fullRetObj.path); // eslint-disable-next-line node/callback-return
callback(preferredOutput, type, fullRetObj);
}
};
/**
*
* @param {string} expr
* @param {JSONObject} val
* @param {string} path
* @param {PlainObject|GenericArray} parent
* @param {string} parentPropName
* @param {JSONPathCallback} callback
* @param {boolean} hasArrExpr
* @param {boolean} literalPriority
* @returns {ReturnObject|ReturnObject[]}
*/
JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, callback, hasArrExpr, literalPriority) {
// No expr to follow? return path and value as the result of
// this trace branch
let retObj;
if (!expr.length) {
retObj = {
path,
value: val,
parent,
parentProperty: parentPropName,
hasArrExpr
};
this._handleCallback(retObj, callback, 'value');
return retObj;
}
const loc = expr[0],
x = expr.slice(1); // We need to gather the return value of recursive trace calls in order to
// do the parent sel computation.
const ret = [];
/**
*
* @param {ReturnObject|ReturnObject[]} elems
* @returns {void}
*/
function addRet(elems) {
if (Array.isArray(elems)) {
// This was causing excessive stack size in Node (with or
// without Babel) against our performance test:
// `ret.push(...elems);`
elems.forEach(t => {
ret.push(t);
});
} else {
ret.push(elems);
}
}
if ((typeof loc !== 'string' || literalPriority) && val && hasOwnProp.call(val, loc)) {
// simple case--directly follow property
addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, hasArrExpr));
} else if (loc === '*') {
// all child properties
this._walk(loc, x, val, path, parent, parentPropName, callback, (m, l, _x, v, p, par, pr, cb) => {
addRet(this._trace(unshift(m, _x), v, p, par, pr, cb, true, true));
});
} else if (loc === '..') {
// all descendent parent properties
// Check remaining expression with val's immediate children
addRet(this._trace(x, val, path, parent, parentPropName, callback, hasArrExpr));
this._walk(loc, x, val, path, parent, parentPropName, callback, (m, l, _x, v, p, par, pr, cb) => {
// We don't join m and x here because we only want parents,
// not scalar values
if (typeof v[m] === 'object') {
// Keep going with recursive descent on val's
// object children
addRet(this._trace(unshift(l, _x), v[m], push(p, m), v, m, cb, true));
}
}); // The parent sel computation is handled in the frame above using the
// ancestor object of val
} else if (loc === '^') {
// This is not a final endpoint, so we do not invoke the callback here
this._hasParentSelector = true;
return {
path: path.slice(0, -1),
expr: x,
isParentSelector: true
};
} else if (loc === '~') {
// property name
retObj = {
path: push(path, loc),
value: parentPropName,
parent,
parentProperty: null
};
this._handleCallback(retObj, callback, 'property');
return retObj;
} else if (loc === '$') {
// root only
addRet(this._trace(x, val, path, null, null, callback, hasArrExpr));
} else if (/^(-?\d*):(-?\d*):?(\d*)$/u.test(loc)) {
// [start:end:step] Python slice syntax
addRet(this._slice(loc, x, val, path, parent, parentPropName, callback));
} else if (loc.indexOf('?(') === 0) {
// [?(expr)] (filtering)
if (this.currPreventEval) {
throw new Error('Eval [?(expr)] prevented in JSONPath expression.');
}
this._walk(loc, x, val, path, parent, parentPropName, callback, (m, l, _x, v, p, par, pr, cb) => {
if (this._eval(l.replace(/^\?\((.*?)\)$/u, '$1'), v[m], m, p, par, pr)) {
addRet(this._trace(unshift(m, _x), v, p, par, pr, cb, true));
}
});
} else if (loc[0] === '(') {
// [(expr)] (dynamic property/index)
if (this.currPreventEval) {
throw new Error('Eval [(expr)] prevented in JSONPath expression.');
} // As this will resolve to a property name (but we don't know it
// yet), property and parent information is relative to the
// parent of the property to which this expression will resolve
addRet(this._trace(unshift(this._eval(loc, val, path[path.length - 1], path.slice(0, -1), parent, parentPropName), x), val, path, parent, parentPropName, callback, hasArrExpr));
} else if (loc[0] === '@') {
// value type: @boolean(), etc.
let addType = false;
const valueType = loc.slice(1, -2);
switch (valueType) {
case 'scalar':
if (!val || !['object', 'function'].includes(typeof val)) {
addType = true;
}
break;
case 'boolean':
case 'string':
case 'undefined':
case 'function':
// eslint-disable-next-line valid-typeof
if (typeof val === valueType) {
addType = true;
}
break;
case 'integer':
if (Number.isFinite(val) && !(val % 1)) {
addType = true;
}
break;
case 'number':
if (Number.isFinite(val)) {
addType = true;
}
break;
case 'nonFinite':
if (typeof val === 'number' && !Number.isFinite(val)) {
addType = true;
}
break;
case 'object':
// eslint-disable-next-line valid-typeof
if (val && typeof val === valueType) {
addType = true;
}
break;
case 'array':
if (Array.isArray(val)) {
addType = true;
}
break;
case 'other':
addType = this.currOtherTypeCallback(val, path, parent, parentPropName);
break;
case 'null':
if (val === null) {
addType = true;
}
break;
/* istanbul ignore next */
default:
throw new TypeError('Unknown value type ' + valueType);
}
if (addType) {
retObj = {
path,
value: val,
parent,
parentProperty: parentPropName
};
this._handleCallback(retObj, callback, 'value');
return retObj;
} // `-escaped property
} else if (loc[0] === '`' && val && hasOwnProp.call(val, loc.slice(1))) {
const locProp = loc.slice(1);
addRet(this._trace(x, val[locProp], push(path, locProp), val, locProp, callback, hasArrExpr, true));
} else if (loc.includes(',')) {
// [name1,name2,...]
const parts = loc.split(',');
for (const part of parts) {
addRet(this._trace(unshift(part, x), val, path, parent, parentPropName, callback, true));
} // simple case--directly follow property
} else if (!literalPriority && val && hasOwnProp.call(val, loc)) {
addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, hasArrExpr, true));
} // 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
if (this._hasParentSelector) {
for (let t = 0; t < ret.length; t++) {
const rett = ret[t];
if (rett && rett.isParentSelector) {
const tmp = this._trace(rett.expr, val, rett.path, parent, parentPropName, callback, hasArrExpr);
if (Array.isArray(tmp)) {
ret[t] = tmp[0];
const tl = tmp.length;
for (let tt = 1; tt < tl; tt++) {
t++;
ret.splice(t, 0, tmp[tt]);
}
} else {
ret[t] = tmp;
}
}
}
}
return ret;
};
JSONPath.prototype._walk = function (loc, expr, val, path, parent, parentPropName, callback, f) {
if (Array.isArray(val)) {
const n = val.length;
for (let i = 0; i < n; i++) {
f(i, loc, expr, val, path, parent, parentPropName, callback);
}
} else if (val && typeof val === 'object') {
Object.keys(val).forEach(m => {
f(m, loc, expr, val, path, parent, parentPropName, callback);
});
}
};
JSONPath.prototype._slice = function (loc, expr, val, path, parent, parentPropName, callback) {
if (!Array.isArray(val)) {
return undefined;
}
const len = val.length,
parts = loc.split(':'),
step = parts[2] && Number.parseInt(parts[2]) || 1;
let start = parts[0] && Number.parseInt(parts[0]) || 0,
end = parts[1] && Number.parseInt(parts[1]) || len;
start = start < 0 ? Math.max(0, start + len) : Math.min(len, start);
end = end < 0 ? Math.max(0, end + len) : Math.min(len, end);
const ret = [];
for (let i = start; i < end; i += step) {
const tmp = this._trace(unshift(i, expr), val, path, parent, parentPropName, callback, true); // Should only be possible to be an array here since first part of
// ``unshift(i, expr)` passed in above would not be empty, nor `~`,
// nor begin with `@` (as could return objects)
// This was causing excessive stack size in Node (with or
// without Babel) against our performance test: `ret.push(...tmp);`
tmp.forEach(t => {
ret.push(t);
});
}
return ret;
};
JSONPath.prototype._eval = function (code, _v, _vname, path, parent, parentPropName) {
if (code.includes('@parentProperty')) {
this.currSandbox._$_parentProperty = parentPropName;
code = code.replace(/@parentProperty/gu, '_$_parentProperty');
}
if (code.includes('@parent')) {
this.currSandbox._$_parent = parent;
code = code.replace(/@parent/gu, '_$_parent');
}
if (code.includes('@property')) {
this.currSandbox._$_property = _vname;
code = code.replace(/@property/gu, '_$_property');
}
if (code.includes('@path')) {
this.currSandbox._$_path = JSONPath.toPathString(path.concat([_vname]));
code = code.replace(/@path/gu, '_$_path');
}
if (code.includes('@root')) {
this.currSandbox._$_root = this.json;
code = code.replace(/@root/gu, '_$_root');
}
if (/@([.\s)[])/u.test(code)) {
this.currSandbox._$_v = _v;
code = code.replace(/@([.\s)[])/gu, '_$_v$1');
}
try {
return this.vm.runInNewContext(code, this.currSandbox);
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
throw new Error('jsonPath: ' + e.message + ': ' + code);
}
}; // PUBLIC CLASS PROPERTIES AND METHODS
// Could store the cache object itself
JSONPath.cache = {};
/**
* @param {string[]} pathArr Array to convert
* @returns {string} The path string
*/
JSONPath.toPathString = function (pathArr) {
const x = pathArr,
n = x.length;
let p = '$';
for (let i = 1; i < n; i++) {
if (!/^(~|\^|@.*?\(\))$/u.test(x[i])) {
p += /^[0-9*]+$/u.test(x[i]) ? '[' + x[i] + ']' : "['" + x[i] + "']";
}
}
return p;
};
/**
* @param {string} pointer JSON Path
* @returns {string} JSON Pointer
*/
JSONPath.toPointer = function (pointer) {
const x = pointer,
n = x.length;
let p = '';
for (let i = 1; i < n; i++) {
if (!/^(~|\^|@.*?\(\))$/u.test(x[i])) {
p += '/' + x[i].toString().replace(/~/gu, '~0').replace(/\//gu, '~1');
}
}
return p;
};
/**
* @param {string} expr Expression to convert
* @returns {string[]}
*/
JSONPath.toPathArray = function (expr) {
const {
cache
} = JSONPath;
if (cache[expr]) {
return cache[expr].concat();
}
const subx = [];
const normalized = expr // Properties
.replace(/@(?:null|boolean|number|string|integer|undefined|nonFinite|scalar|array|object|function|other)\(\)/gu, ';$&;') // Parenthetical evaluations (filtering and otherwise), directly
// within brackets or single quotes
.replace(/[['](\??\(.*?\))[\]']/gu, function ($0, $1) {
return '[#' + (subx.push($1) - 1) + ']';
}) // Escape periods and tildes within properties
.replace(/\['([^'\]]*)'\]/gu, function ($0, prop) {
return "['" + prop.replace(/\./gu, '%@%').replace(/~/gu, '%%@@%%') + "']";
}) // Properties operator
.replace(/~/gu, ';~;') // Split by property boundaries
.replace(/'?\.'?(?![^[]*\])|\['?/gu, ';') // Reinsert periods within properties
.replace(/%@%/gu, '.') // Reinsert tildes within properties
.replace(/%%@@%%/gu, '~') // Parent
.replace(/(?:;)?(\^+)(?:;)?/gu, function ($0, ups) {
return ';' + ups.split('').join(';') + ';';
}) // Descendents
.replace(/;;;|;;/gu, ';..;') // Remove trailing
.replace(/;$|'?\]|'$/gu, '');
const exprList = normalized.split(';').map(function (exp) {
const match = exp.match(/#(\d+)/u);
return !match || !match[1] ? exp : subx[match[1]];
});
cache[expr] = exprList;
return cache[expr].concat();
};
JSONPath.prototype.vm = vm;
export { JSONPath };