From 1e9ffbd305a614a2359fc4ee9b4e19ba9549e47e Mon Sep 17 00:00:00 2001 From: Joaquin Santana Date: Tue, 16 Sep 2025 17:36:35 +0200 Subject: [PATCH] chore: build dist (#14) --- dist/index.js | 4249 ++++++++++++++++++++++++------------------------- 1 file changed, 2117 insertions(+), 2132 deletions(-) diff --git a/dist/index.js b/dist/index.js index 3f76eca..581462a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -10890,7 +10890,13 @@ class GitHub { var _a, _b, _c, _d; const apiUrl = (_a = options.apiUrl) !== null && _a !== void 0 ? _a : exports.GH_API_URL; const graphqlUrl = (_b = options.graphqlUrl) !== null && _b !== void 0 ? _b : exports.GH_GRAPHQL_URL; - const releasePleaseVersion = (__nccwpck_require__(67034)/* .version */ .i8); + let releasePleaseVersion; + try { + releasePleaseVersion = (__nccwpck_require__(67034)/* .version */ .i8); + } + catch (e) { + releasePleaseVersion = (__nccwpck_require__(85594).version); + } const apis = (_c = options.octokitAPIs) !== null && _c !== void 0 ? _c : { octokit: new rest_1.Octokit({ baseUrl: apiUrl, @@ -11400,63 +11406,17 @@ class GitHub { var _a, _b, _c, _d, _e, _f, _g; var _h; this.logger.debug(`Fetching merge commits on branch ${targetBranch} with cursor: ${cursor}`); - const query = `query pullRequestsSince($owner: String!, $repo: String!, $num: Int!, $maxFilesChanged: Int, $targetBranch: String!, $cursor: String) { - repository(owner: $owner, name: $repo) { - ref(qualifiedName: $targetBranch) { - target { - ... on Commit { - history(first: $num, after: $cursor) { - nodes { - associatedPullRequests(first: 10) { - nodes { - number - title - baseRefName - headRefName - labels(first: 10) { - nodes { - name - } - } - body - mergeCommit { - oid - } - files(first: $maxFilesChanged) { - nodes { - path - } - pageInfo { - endCursor - hasNextPage - } - } - } - } - sha: oid - message - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - } - } - }`; const params = { - cursor, owner: this.repository.owner, repo: this.repository.repo, limit: options.maxResults, - targetBranch, - maxFilesChanged: 100, // max is 100 + page: cursor, + sha: targetBranch, }; + // @ts-ignore const response = await this.octokit.repos.listCommits({ ...params }); if (!response) { - this.logger.warn(`Did not receive a response for query: ${query}`, params); + this.logger.warn(`Did not receive a response for querying commits on branch ${targetBranch}: ${params}`, params); return null; } // if the branch does exist, return null @@ -11543,11 +11503,16 @@ class GitHub { if (((_f = (_e = mergePullRequest.files) === null || _e === void 0 ? void 0 : _e.pageInfo) === null || _f === void 0 ? void 0 : _f.hasNextPage) && options.backfillFiles) { this.logger.info(`PR #${mergePullRequest.number} has many files, backfilling`); - commit.files = await this.getCommitFiles(graphCommit.sha); + this.logger.debug(`Backfilling file list for commit: ${graphCommit.sha}`); + let found = response.data.find(filter => filter.sha === graphCommit.sha); + commit.files = (found === null || found === void 0 ? void 0 : found.files) + ? found.files.map(map => map.filename) + : undefined; } else { // We cannot directly fetch files on commits via graphql, only provide file // information for commits with associated pull requests + this.logger.debug(`Backfilling file list for commit: ${graphCommit.sha}`); commit.files = (((_g = mergePullRequest.files) === null || _g === void 0 ? void 0 : _g.nodes) || []).map(node => node.path); } } @@ -11556,13 +11521,18 @@ class GitHub { // merge commit, a rebase merge commit, or a direct commit to the branch. // Fallback to fetching the list of commits from the REST API. In the future // we can perhaps lazy load these. - commit.files = await this.getCommitFiles(graphCommit.sha); + let found = response.data.find(filter => filter.sha === graphCommit.sha); + commit.files = (found === null || found === void 0 ? void 0 : found.files) + ? found.files.map(map => map.filename) + : undefined; } commitData.push(commit); } return { - // pageInfo: history.pageInfo, - pageInfo: { hasNextPage: false, endCursor: undefined }, + pageInfo: { + hasNextPage: response.headers['x-hasmore'] == 'true', + endCursor: String(Number(response.headers['x-page']) + 1), + }, data: commitData, }; } @@ -12789,7 +12759,9 @@ function isPublishedVersion(strategy, version) { * @param github GitHub client instance. * @param {string} targetBranch Name of the scanned branch. * @param releaseFilter Validator function for release version. Used to filter-out SNAPSHOT releases for Java strategy. + * @param config * @param {string} prefix Limit the release to a specific component. + * @param logger */ async function latestReleaseVersion(github, targetBranch, releaseFilter, config, prefix, logger = logger_1.logger) { const branchPrefix = prefix @@ -18771,7 +18743,7 @@ exports.AppJson = AppJson; // limitations under the License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GenericJson = void 0; -const jsonpath_plus_1 = __nccwpck_require__(2452); +const jsonpath_plus_1 = __nccwpck_require__(24697); const json_stringify_1 = __nccwpck_require__(61480); const logger_1 = __nccwpck_require__(18792); const VERSION_REGEX = /(?\d+)\.(?\d+)\.(?\d+)(-(?[\w.]+))?(\+(?[-\w.]+))?/; @@ -18832,7 +18804,7 @@ exports.GenericJson = GenericJson; // limitations under the License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GenericToml = void 0; -const jsonpath_plus_1 = __nccwpck_require__(2452); +const jsonpath_plus_1 = __nccwpck_require__(24697); const toml_edit_1 = __nccwpck_require__(42237); const logger_1 = __nccwpck_require__(18792); /** @@ -18950,7 +18922,7 @@ exports.GenericXml = GenericXml; // limitations under the License. Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GenericYaml = void 0; -const jsonpath_plus_1 = __nccwpck_require__(2452); +const jsonpath_plus_1 = __nccwpck_require__(24697); const yaml = __nccwpck_require__(21917); const logger_1 = __nccwpck_require__(18792); const DOCUMENT_SEPARATOR = '---\n'; @@ -110087,6 +110059,14 @@ try { } catch (er) {} +/***/ }), + +/***/ 85594: +/***/ ((module) => { + +module.exports = eval("require")("../package.json"); + + /***/ }), /***/ 39491: @@ -118790,2079 +118770,6 @@ exports.visit = visit; exports.visitAsync = visitAsync; -/***/ }), - -/***/ 2452: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -var vm = __nccwpck_require__(26144); - -/** - * @implements {IHooks} - */ -class Hooks { - /** - * @callback HookCallback - * @this {*|Jsep} this - * @param {Jsep} env - * @returns: void - */ - /** - * Adds the given callback to the list of callbacks for the given hook. - * - * The callback will be invoked when the hook it is registered for is run. - * - * One callback function can be registered to multiple hooks and the same hook multiple times. - * - * @param {string|object} name The name of the hook, or an object of callbacks keyed by name - * @param {HookCallback|boolean} callback The callback function which is given environment variables. - * @param {?boolean} [first=false] Will add the hook to the top of the list (defaults to the bottom) - * @public - */ - add(name, callback, first) { - if (typeof arguments[0] != 'string') { - // Multiple hook callbacks, keyed by name - for (let name in arguments[0]) { - this.add(name, arguments[0][name], arguments[1]); - } - } else { - (Array.isArray(name) ? name : [name]).forEach(function (name) { - this[name] = this[name] || []; - if (callback) { - this[name][first ? 'unshift' : 'push'](callback); - } - }, this); - } - } - - /** - * Runs a hook invoking all registered callbacks with the given environment variables. - * - * Callbacks will be invoked synchronously and in the order in which they were registered. - * - * @param {string} name The name of the hook. - * @param {Object} env The environment variables of the hook passed to all callbacks registered. - * @public - */ - run(name, env) { - this[name] = this[name] || []; - this[name].forEach(function (callback) { - callback.call(env && env.context ? env.context : env, env); - }); - } -} - -/** - * @implements {IPlugins} - */ -class Plugins { - constructor(jsep) { - this.jsep = jsep; - this.registered = {}; - } - - /** - * @callback PluginSetup - * @this {Jsep} jsep - * @returns: void - */ - /** - * Adds the given plugin(s) to the registry - * - * @param {object} plugins - * @param {string} plugins.name The name of the plugin - * @param {PluginSetup} plugins.init The init function - * @public - */ - register(...plugins) { - plugins.forEach(plugin => { - if (typeof plugin !== 'object' || !plugin.name || !plugin.init) { - throw new Error('Invalid JSEP plugin format'); - } - if (this.registered[plugin.name]) { - // already registered. Ignore. - return; - } - plugin.init(this.jsep); - this.registered[plugin.name] = plugin; - }); - } -} - -// JavaScript Expression Parser (JSEP) 1.4.0 - -class Jsep { - /** - * @returns {string} - */ - static get version() { - // To be filled in by the template - return '1.4.0'; - } - - /** - * @returns {string} - */ - static toString() { - return 'JavaScript Expression Parser (JSEP) v' + Jsep.version; - } - // ==================== CONFIG ================================ - /** - * @method addUnaryOp - * @param {string} op_name The name of the unary op to add - * @returns {Jsep} - */ - static addUnaryOp(op_name) { - Jsep.max_unop_len = Math.max(op_name.length, Jsep.max_unop_len); - Jsep.unary_ops[op_name] = 1; - return Jsep; - } - - /** - * @method jsep.addBinaryOp - * @param {string} op_name The name of the binary op to add - * @param {number} precedence The precedence of the binary op (can be a float). Higher number = higher precedence - * @param {boolean} [isRightAssociative=false] whether operator is right-associative - * @returns {Jsep} - */ - static addBinaryOp(op_name, precedence, isRightAssociative) { - Jsep.max_binop_len = Math.max(op_name.length, Jsep.max_binop_len); - Jsep.binary_ops[op_name] = precedence; - if (isRightAssociative) { - Jsep.right_associative.add(op_name); - } else { - Jsep.right_associative.delete(op_name); - } - return Jsep; - } - - /** - * @method addIdentifierChar - * @param {string} char The additional character to treat as a valid part of an identifier - * @returns {Jsep} - */ - static addIdentifierChar(char) { - Jsep.additional_identifier_chars.add(char); - return Jsep; - } - - /** - * @method addLiteral - * @param {string} literal_name The name of the literal to add - * @param {*} literal_value The value of the literal - * @returns {Jsep} - */ - static addLiteral(literal_name, literal_value) { - Jsep.literals[literal_name] = literal_value; - return Jsep; - } - - /** - * @method removeUnaryOp - * @param {string} op_name The name of the unary op to remove - * @returns {Jsep} - */ - static removeUnaryOp(op_name) { - delete Jsep.unary_ops[op_name]; - if (op_name.length === Jsep.max_unop_len) { - Jsep.max_unop_len = Jsep.getMaxKeyLen(Jsep.unary_ops); - } - return Jsep; - } - - /** - * @method removeAllUnaryOps - * @returns {Jsep} - */ - static removeAllUnaryOps() { - Jsep.unary_ops = {}; - Jsep.max_unop_len = 0; - return Jsep; - } - - /** - * @method removeIdentifierChar - * @param {string} char The additional character to stop treating as a valid part of an identifier - * @returns {Jsep} - */ - static removeIdentifierChar(char) { - Jsep.additional_identifier_chars.delete(char); - return Jsep; - } - - /** - * @method removeBinaryOp - * @param {string} op_name The name of the binary op to remove - * @returns {Jsep} - */ - static removeBinaryOp(op_name) { - delete Jsep.binary_ops[op_name]; - if (op_name.length === Jsep.max_binop_len) { - Jsep.max_binop_len = Jsep.getMaxKeyLen(Jsep.binary_ops); - } - Jsep.right_associative.delete(op_name); - return Jsep; - } - - /** - * @method removeAllBinaryOps - * @returns {Jsep} - */ - static removeAllBinaryOps() { - Jsep.binary_ops = {}; - Jsep.max_binop_len = 0; - return Jsep; - } - - /** - * @method removeLiteral - * @param {string} literal_name The name of the literal to remove - * @returns {Jsep} - */ - static removeLiteral(literal_name) { - delete Jsep.literals[literal_name]; - return Jsep; - } - - /** - * @method removeAllLiterals - * @returns {Jsep} - */ - static removeAllLiterals() { - Jsep.literals = {}; - return Jsep; - } - // ==================== END CONFIG ============================ - - /** - * @returns {string} - */ - get char() { - return this.expr.charAt(this.index); - } - - /** - * @returns {number} - */ - get code() { - return this.expr.charCodeAt(this.index); - } - /** - * @param {string} expr a string with the passed in express - * @returns Jsep - */ - constructor(expr) { - // `index` stores the character number we are currently at - // All of the gobbles below will modify `index` as we move along - this.expr = expr; - this.index = 0; - } - - /** - * static top-level parser - * @returns {jsep.Expression} - */ - static parse(expr) { - return new Jsep(expr).parse(); - } - - /** - * Get the longest key length of any object - * @param {object} obj - * @returns {number} - */ - static getMaxKeyLen(obj) { - return Math.max(0, ...Object.keys(obj).map(k => k.length)); - } - - /** - * `ch` is a character code in the next three functions - * @param {number} ch - * @returns {boolean} - */ - static isDecimalDigit(ch) { - return ch >= 48 && ch <= 57; // 0...9 - } - - /** - * Returns the precedence of a binary operator or `0` if it isn't a binary operator. Can be float. - * @param {string} op_val - * @returns {number} - */ - static binaryPrecedence(op_val) { - return Jsep.binary_ops[op_val] || 0; - } - - /** - * Looks for start of identifier - * @param {number} ch - * @returns {boolean} - */ - static isIdentifierStart(ch) { - return ch >= 65 && ch <= 90 || - // A...Z - ch >= 97 && ch <= 122 || - // a...z - ch >= 128 && !Jsep.binary_ops[String.fromCharCode(ch)] || - // any non-ASCII that is not an operator - Jsep.additional_identifier_chars.has(String.fromCharCode(ch)); // additional characters - } - - /** - * @param {number} ch - * @returns {boolean} - */ - static isIdentifierPart(ch) { - return Jsep.isIdentifierStart(ch) || Jsep.isDecimalDigit(ch); - } - - /** - * throw error at index of the expression - * @param {string} message - * @throws - */ - throwError(message) { - const error = new Error(message + ' at character ' + this.index); - error.index = this.index; - error.description = message; - throw error; - } - - /** - * Run a given hook - * @param {string} name - * @param {jsep.Expression|false} [node] - * @returns {?jsep.Expression} - */ - runHook(name, node) { - if (Jsep.hooks[name]) { - const env = { - context: this, - node - }; - Jsep.hooks.run(name, env); - return env.node; - } - return node; - } - - /** - * Runs a given hook until one returns a node - * @param {string} name - * @returns {?jsep.Expression} - */ - searchHook(name) { - if (Jsep.hooks[name]) { - const env = { - context: this - }; - Jsep.hooks[name].find(function (callback) { - callback.call(env.context, env); - return env.node; - }); - return env.node; - } - } - - /** - * Push `index` up to the next non-space character - */ - gobbleSpaces() { - let ch = this.code; - // Whitespace - while (ch === Jsep.SPACE_CODE || ch === Jsep.TAB_CODE || ch === Jsep.LF_CODE || ch === Jsep.CR_CODE) { - ch = this.expr.charCodeAt(++this.index); - } - this.runHook('gobble-spaces'); - } - - /** - * Top-level method to parse all expressions and returns compound or single node - * @returns {jsep.Expression} - */ - parse() { - this.runHook('before-all'); - const nodes = this.gobbleExpressions(); - - // If there's only one expression just try returning the expression - const node = nodes.length === 1 ? nodes[0] : { - type: Jsep.COMPOUND, - body: nodes - }; - return this.runHook('after-all', node); - } - - /** - * top-level parser (but can be reused within as well) - * @param {number} [untilICode] - * @returns {jsep.Expression[]} - */ - gobbleExpressions(untilICode) { - let nodes = [], - ch_i, - node; - while (this.index < this.expr.length) { - ch_i = this.code; - - // Expressions can be separated by semicolons, commas, or just inferred without any - // separators - if (ch_i === Jsep.SEMCOL_CODE || ch_i === Jsep.COMMA_CODE) { - this.index++; // ignore separators - } else { - // Try to gobble each expression individually - if (node = this.gobbleExpression()) { - nodes.push(node); - // If we weren't able to find a binary expression and are out of room, then - // the expression passed in probably has too much - } else if (this.index < this.expr.length) { - if (ch_i === untilICode) { - break; - } - this.throwError('Unexpected "' + this.char + '"'); - } - } - } - return nodes; - } - - /** - * The main parsing function. - * @returns {?jsep.Expression} - */ - gobbleExpression() { - const node = this.searchHook('gobble-expression') || this.gobbleBinaryExpression(); - this.gobbleSpaces(); - return this.runHook('after-expression', node); - } - - /** - * Search for the operation portion of the string (e.g. `+`, `===`) - * Start by taking the longest possible binary operations (3 characters: `===`, `!==`, `>>>`) - * and move down from 3 to 2 to 1 character until a matching binary operation is found - * then, return that binary operation - * @returns {string|boolean} - */ - gobbleBinaryOp() { - this.gobbleSpaces(); - let to_check = this.expr.substr(this.index, Jsep.max_binop_len); - let tc_len = to_check.length; - while (tc_len > 0) { - // Don't accept a binary op when it is an identifier. - // Binary ops that start with a identifier-valid character must be followed - // by a non identifier-part valid character - if (Jsep.binary_ops.hasOwnProperty(to_check) && (!Jsep.isIdentifierStart(this.code) || this.index + to_check.length < this.expr.length && !Jsep.isIdentifierPart(this.expr.charCodeAt(this.index + to_check.length)))) { - this.index += tc_len; - return to_check; - } - to_check = to_check.substr(0, --tc_len); - } - return false; - } - - /** - * This function is responsible for gobbling an individual expression, - * e.g. `1`, `1+2`, `a+(b*2)-Math.sqrt(2)` - * @returns {?jsep.BinaryExpression} - */ - gobbleBinaryExpression() { - let node, biop, prec, stack, biop_info, left, right, i, cur_biop; - - // First, try to get the leftmost thing - // Then, check to see if there's a binary operator operating on that leftmost thing - // Don't gobbleBinaryOp without a left-hand-side - left = this.gobbleToken(); - if (!left) { - return left; - } - biop = this.gobbleBinaryOp(); - - // If there wasn't a binary operator, just return the leftmost node - if (!biop) { - return left; - } - - // Otherwise, we need to start a stack to properly place the binary operations in their - // precedence structure - biop_info = { - value: biop, - prec: Jsep.binaryPrecedence(biop), - right_a: Jsep.right_associative.has(biop) - }; - right = this.gobbleToken(); - if (!right) { - this.throwError("Expected expression after " + biop); - } - stack = [left, biop_info, right]; - - // Properly deal with precedence using [recursive descent](http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm) - while (biop = this.gobbleBinaryOp()) { - prec = Jsep.binaryPrecedence(biop); - if (prec === 0) { - this.index -= biop.length; - break; - } - biop_info = { - value: biop, - prec, - right_a: Jsep.right_associative.has(biop) - }; - cur_biop = biop; - - // Reduce: make a binary expression from the three topmost entries. - const comparePrev = prev => biop_info.right_a && prev.right_a ? prec > prev.prec : prec <= prev.prec; - while (stack.length > 2 && comparePrev(stack[stack.length - 2])) { - right = stack.pop(); - biop = stack.pop().value; - left = stack.pop(); - node = { - type: Jsep.BINARY_EXP, - operator: biop, - left, - right - }; - stack.push(node); - } - node = this.gobbleToken(); - if (!node) { - this.throwError("Expected expression after " + cur_biop); - } - stack.push(biop_info, node); - } - i = stack.length - 1; - node = stack[i]; - while (i > 1) { - node = { - type: Jsep.BINARY_EXP, - operator: stack[i - 1].value, - left: stack[i - 2], - right: node - }; - i -= 2; - } - return node; - } - - /** - * An individual part of a binary expression: - * e.g. `foo.bar(baz)`, `1`, `"abc"`, `(a % 2)` (because it's in parenthesis) - * @returns {boolean|jsep.Expression} - */ - gobbleToken() { - let ch, to_check, tc_len, node; - this.gobbleSpaces(); - node = this.searchHook('gobble-token'); - if (node) { - return this.runHook('after-token', node); - } - ch = this.code; - if (Jsep.isDecimalDigit(ch) || ch === Jsep.PERIOD_CODE) { - // Char code 46 is a dot `.` which can start off a numeric literal - return this.gobbleNumericLiteral(); - } - if (ch === Jsep.SQUOTE_CODE || ch === Jsep.DQUOTE_CODE) { - // Single or double quotes - node = this.gobbleStringLiteral(); - } else if (ch === Jsep.OBRACK_CODE) { - node = this.gobbleArray(); - } else { - to_check = this.expr.substr(this.index, Jsep.max_unop_len); - tc_len = to_check.length; - while (tc_len > 0) { - // Don't accept an unary op when it is an identifier. - // Unary ops that start with a identifier-valid character must be followed - // by a non identifier-part valid character - if (Jsep.unary_ops.hasOwnProperty(to_check) && (!Jsep.isIdentifierStart(this.code) || this.index + to_check.length < this.expr.length && !Jsep.isIdentifierPart(this.expr.charCodeAt(this.index + to_check.length)))) { - this.index += tc_len; - const argument = this.gobbleToken(); - if (!argument) { - this.throwError('missing unaryOp argument'); - } - return this.runHook('after-token', { - type: Jsep.UNARY_EXP, - operator: to_check, - argument, - prefix: true - }); - } - to_check = to_check.substr(0, --tc_len); - } - if (Jsep.isIdentifierStart(ch)) { - node = this.gobbleIdentifier(); - if (Jsep.literals.hasOwnProperty(node.name)) { - node = { - type: Jsep.LITERAL, - value: Jsep.literals[node.name], - raw: node.name - }; - } else if (node.name === Jsep.this_str) { - node = { - type: Jsep.THIS_EXP - }; - } - } else if (ch === Jsep.OPAREN_CODE) { - // open parenthesis - node = this.gobbleGroup(); - } - } - if (!node) { - return this.runHook('after-token', false); - } - node = this.gobbleTokenProperty(node); - return this.runHook('after-token', node); - } - - /** - * Gobble properties of of identifiers/strings/arrays/groups. - * e.g. `foo`, `bar.baz`, `foo['bar'].baz` - * It also gobbles function calls: - * e.g. `Math.acos(obj.angle)` - * @param {jsep.Expression} node - * @returns {jsep.Expression} - */ - gobbleTokenProperty(node) { - this.gobbleSpaces(); - let ch = this.code; - while (ch === Jsep.PERIOD_CODE || ch === Jsep.OBRACK_CODE || ch === Jsep.OPAREN_CODE || ch === Jsep.QUMARK_CODE) { - let optional; - if (ch === Jsep.QUMARK_CODE) { - if (this.expr.charCodeAt(this.index + 1) !== Jsep.PERIOD_CODE) { - break; - } - optional = true; - this.index += 2; - this.gobbleSpaces(); - ch = this.code; - } - this.index++; - if (ch === Jsep.OBRACK_CODE) { - node = { - type: Jsep.MEMBER_EXP, - computed: true, - object: node, - property: this.gobbleExpression() - }; - if (!node.property) { - this.throwError('Unexpected "' + this.char + '"'); - } - this.gobbleSpaces(); - ch = this.code; - if (ch !== Jsep.CBRACK_CODE) { - this.throwError('Unclosed ['); - } - this.index++; - } else if (ch === Jsep.OPAREN_CODE) { - // A function call is being made; gobble all the arguments - node = { - type: Jsep.CALL_EXP, - 'arguments': this.gobbleArguments(Jsep.CPAREN_CODE), - callee: node - }; - } else if (ch === Jsep.PERIOD_CODE || optional) { - if (optional) { - this.index--; - } - this.gobbleSpaces(); - node = { - type: Jsep.MEMBER_EXP, - computed: false, - object: node, - property: this.gobbleIdentifier() - }; - } - if (optional) { - node.optional = true; - } // else leave undefined for compatibility with esprima - - this.gobbleSpaces(); - ch = this.code; - } - return node; - } - - /** - * Parse simple numeric literals: `12`, `3.4`, `.5`. Do this by using a string to - * keep track of everything in the numeric literal and then calling `parseFloat` on that string - * @returns {jsep.Literal} - */ - gobbleNumericLiteral() { - let number = '', - ch, - chCode; - while (Jsep.isDecimalDigit(this.code)) { - number += this.expr.charAt(this.index++); - } - if (this.code === Jsep.PERIOD_CODE) { - // can start with a decimal marker - number += this.expr.charAt(this.index++); - while (Jsep.isDecimalDigit(this.code)) { - number += this.expr.charAt(this.index++); - } - } - ch = this.char; - if (ch === 'e' || ch === 'E') { - // exponent marker - number += this.expr.charAt(this.index++); - ch = this.char; - if (ch === '+' || ch === '-') { - // exponent sign - number += this.expr.charAt(this.index++); - } - while (Jsep.isDecimalDigit(this.code)) { - // exponent itself - number += this.expr.charAt(this.index++); - } - if (!Jsep.isDecimalDigit(this.expr.charCodeAt(this.index - 1))) { - this.throwError('Expected exponent (' + number + this.char + ')'); - } - } - chCode = this.code; - - // Check to make sure this isn't a variable name that start with a number (123abc) - if (Jsep.isIdentifierStart(chCode)) { - this.throwError('Variable names cannot start with a number (' + number + this.char + ')'); - } else if (chCode === Jsep.PERIOD_CODE || number.length === 1 && number.charCodeAt(0) === Jsep.PERIOD_CODE) { - this.throwError('Unexpected period'); - } - return { - type: Jsep.LITERAL, - value: parseFloat(number), - raw: number - }; - } - - /** - * Parses a string literal, staring with single or double quotes with basic support for escape codes - * e.g. `"hello world"`, `'this is\nJSEP'` - * @returns {jsep.Literal} - */ - gobbleStringLiteral() { - let str = ''; - const startIndex = this.index; - const quote = this.expr.charAt(this.index++); - let closed = false; - while (this.index < this.expr.length) { - let ch = this.expr.charAt(this.index++); - if (ch === quote) { - closed = true; - break; - } else if (ch === '\\') { - // Check for all of the common escape codes - ch = this.expr.charAt(this.index++); - switch (ch) { - case 'n': - str += '\n'; - break; - case 'r': - str += '\r'; - break; - case 't': - str += '\t'; - break; - case 'b': - str += '\b'; - break; - case 'f': - str += '\f'; - break; - case 'v': - str += '\x0B'; - break; - default: - str += ch; - } - } else { - str += ch; - } - } - if (!closed) { - this.throwError('Unclosed quote after "' + str + '"'); - } - return { - type: Jsep.LITERAL, - value: str, - raw: this.expr.substring(startIndex, this.index) - }; - } - - /** - * Gobbles only identifiers - * e.g.: `foo`, `_value`, `$x1` - * Also, this function checks if that identifier is a literal: - * (e.g. `true`, `false`, `null`) or `this` - * @returns {jsep.Identifier} - */ - gobbleIdentifier() { - let ch = this.code, - start = this.index; - if (Jsep.isIdentifierStart(ch)) { - this.index++; - } else { - this.throwError('Unexpected ' + this.char); - } - while (this.index < this.expr.length) { - ch = this.code; - if (Jsep.isIdentifierPart(ch)) { - this.index++; - } else { - break; - } - } - return { - type: Jsep.IDENTIFIER, - name: this.expr.slice(start, this.index) - }; - } - - /** - * Gobbles a list of arguments within the context of a function call - * or array literal. This function also assumes that the opening character - * `(` or `[` has already been gobbled, and gobbles expressions and commas - * until the terminator character `)` or `]` is encountered. - * e.g. `foo(bar, baz)`, `my_func()`, or `[bar, baz]` - * @param {number} termination - * @returns {jsep.Expression[]} - */ - gobbleArguments(termination) { - const args = []; - let closed = false; - let separator_count = 0; - while (this.index < this.expr.length) { - this.gobbleSpaces(); - let ch_i = this.code; - if (ch_i === termination) { - // done parsing - closed = true; - this.index++; - if (termination === Jsep.CPAREN_CODE && separator_count && separator_count >= args.length) { - this.throwError('Unexpected token ' + String.fromCharCode(termination)); - } - break; - } else if (ch_i === Jsep.COMMA_CODE) { - // between expressions - this.index++; - separator_count++; - if (separator_count !== args.length) { - // missing argument - if (termination === Jsep.CPAREN_CODE) { - this.throwError('Unexpected token ,'); - } else if (termination === Jsep.CBRACK_CODE) { - for (let arg = args.length; arg < separator_count; arg++) { - args.push(null); - } - } - } - } else if (args.length !== separator_count && separator_count !== 0) { - // NOTE: `&& separator_count !== 0` allows for either all commas, or all spaces as arguments - this.throwError('Expected comma'); - } else { - const node = this.gobbleExpression(); - if (!node || node.type === Jsep.COMPOUND) { - this.throwError('Expected comma'); - } - args.push(node); - } - } - if (!closed) { - this.throwError('Expected ' + String.fromCharCode(termination)); - } - return args; - } - - /** - * Responsible for parsing a group of things within parentheses `()` - * that have no identifier in front (so not a function call) - * This function assumes that it needs to gobble the opening parenthesis - * and then tries to gobble everything within that parenthesis, assuming - * that the next thing it should see is the close parenthesis. If not, - * then the expression probably doesn't have a `)` - * @returns {boolean|jsep.Expression} - */ - gobbleGroup() { - this.index++; - let nodes = this.gobbleExpressions(Jsep.CPAREN_CODE); - if (this.code === Jsep.CPAREN_CODE) { - this.index++; - if (nodes.length === 1) { - return nodes[0]; - } else if (!nodes.length) { - return false; - } else { - return { - type: Jsep.SEQUENCE_EXP, - expressions: nodes - }; - } - } else { - this.throwError('Unclosed ('); - } - } - - /** - * Responsible for parsing Array literals `[1, 2, 3]` - * This function assumes that it needs to gobble the opening bracket - * and then tries to gobble the expressions as arguments. - * @returns {jsep.ArrayExpression} - */ - gobbleArray() { - this.index++; - return { - type: Jsep.ARRAY_EXP, - elements: this.gobbleArguments(Jsep.CBRACK_CODE) - }; - } -} - -// Static fields: -const hooks = new Hooks(); -Object.assign(Jsep, { - hooks, - plugins: new Plugins(Jsep), - // Node Types - // ---------- - // This is the full set of types that any JSEP node can be. - // Store them here to save space when minified - COMPOUND: 'Compound', - SEQUENCE_EXP: 'SequenceExpression', - IDENTIFIER: 'Identifier', - MEMBER_EXP: 'MemberExpression', - LITERAL: 'Literal', - THIS_EXP: 'ThisExpression', - CALL_EXP: 'CallExpression', - UNARY_EXP: 'UnaryExpression', - BINARY_EXP: 'BinaryExpression', - ARRAY_EXP: 'ArrayExpression', - TAB_CODE: 9, - LF_CODE: 10, - CR_CODE: 13, - SPACE_CODE: 32, - PERIOD_CODE: 46, - // '.' - COMMA_CODE: 44, - // ',' - SQUOTE_CODE: 39, - // single quote - DQUOTE_CODE: 34, - // double quotes - OPAREN_CODE: 40, - // ( - CPAREN_CODE: 41, - // ) - OBRACK_CODE: 91, - // [ - CBRACK_CODE: 93, - // ] - QUMARK_CODE: 63, - // ? - SEMCOL_CODE: 59, - // ; - COLON_CODE: 58, - // : - - // Operations - // ---------- - // Use a quickly-accessible map to store all of the unary operators - // Values are set to `1` (it really doesn't matter) - unary_ops: { - '-': 1, - '!': 1, - '~': 1, - '+': 1 - }, - // Also use a map for the binary operations but set their values to their - // binary precedence for quick reference (higher number = higher precedence) - // see [Order of operations](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) - binary_ops: { - '||': 1, - '??': 1, - '&&': 2, - '|': 3, - '^': 4, - '&': 5, - '==': 6, - '!=': 6, - '===': 6, - '!==': 6, - '<': 7, - '>': 7, - '<=': 7, - '>=': 7, - '<<': 8, - '>>': 8, - '>>>': 8, - '+': 9, - '-': 9, - '*': 10, - '/': 10, - '%': 10, - '**': 11 - }, - // sets specific binary_ops as right-associative - right_associative: new Set(['**']), - // Additional valid identifier chars, apart from a-z, A-Z and 0-9 (except on the starting char) - additional_identifier_chars: new Set(['$', '_']), - // Literals - // ---------- - // Store the values to return for the various literals we may encounter - literals: { - 'true': true, - 'false': false, - 'null': null - }, - // Except for `this`, which is special. This could be changed to something like `'self'` as well - this_str: 'this' -}); -Jsep.max_unop_len = Jsep.getMaxKeyLen(Jsep.unary_ops); -Jsep.max_binop_len = Jsep.getMaxKeyLen(Jsep.binary_ops); - -// Backward Compatibility: -const jsep = expr => new Jsep(expr).parse(); -const stdClassProps = Object.getOwnPropertyNames(class Test {}); -Object.getOwnPropertyNames(Jsep).filter(prop => !stdClassProps.includes(prop) && jsep[prop] === undefined).forEach(m => { - jsep[m] = Jsep[m]; -}); -jsep.Jsep = Jsep; // allows for const { Jsep } = require('jsep'); - -const CONDITIONAL_EXP = 'ConditionalExpression'; -var ternary = { - name: 'ternary', - init(jsep) { - // Ternary expression: test ? consequent : alternate - jsep.hooks.add('after-expression', function gobbleTernary(env) { - if (env.node && this.code === jsep.QUMARK_CODE) { - this.index++; - const test = env.node; - const consequent = this.gobbleExpression(); - if (!consequent) { - this.throwError('Expected expression'); - } - this.gobbleSpaces(); - if (this.code === jsep.COLON_CODE) { - this.index++; - const alternate = this.gobbleExpression(); - if (!alternate) { - this.throwError('Expected expression'); - } - env.node = { - type: CONDITIONAL_EXP, - test, - consequent, - alternate - }; - - // check for operators of higher priority than ternary (i.e. assignment) - // jsep sets || at 1, and assignment at 0.9, and conditional should be between them - if (test.operator && jsep.binary_ops[test.operator] <= 0.9) { - let newTest = test; - while (newTest.right.operator && jsep.binary_ops[newTest.right.operator] <= 0.9) { - newTest = newTest.right; - } - env.node.test = newTest.right; - newTest.right = env.node; - env.node = test; - } - } else { - this.throwError('Expected :'); - } - } - }); - } -}; - -// Add default plugins: - -jsep.plugins.register(ternary); - -const FSLASH_CODE = 47; // '/' -const BSLASH_CODE = 92; // '\\' - -var index = { - name: 'regex', - init(jsep) { - // Regex literal: /abc123/ig - jsep.hooks.add('gobble-token', function gobbleRegexLiteral(env) { - if (this.code === FSLASH_CODE) { - const patternIndex = ++this.index; - let inCharSet = false; - while (this.index < this.expr.length) { - if (this.code === FSLASH_CODE && !inCharSet) { - const pattern = this.expr.slice(patternIndex, this.index); - let flags = ''; - while (++this.index < this.expr.length) { - const code = this.code; - if (code >= 97 && code <= 122 // a...z - || code >= 65 && code <= 90 // A...Z - || code >= 48 && code <= 57) { - // 0-9 - flags += this.char; - } else { - break; - } - } - let value; - try { - value = new RegExp(pattern, flags); - } catch (e) { - this.throwError(e.message); - } - env.node = { - type: jsep.LITERAL, - value, - raw: this.expr.slice(patternIndex - 1, this.index) - }; - - // allow . [] and () after regex: /regex/.test(a) - env.node = this.gobbleTokenProperty(env.node); - return env.node; - } - if (this.code === jsep.OBRACK_CODE) { - inCharSet = true; - } else if (inCharSet && this.code === jsep.CBRACK_CODE) { - inCharSet = false; - } - this.index += this.code === BSLASH_CODE ? 2 : 1; - } - this.throwError('Unclosed Regex'); - } - }); - } -}; - -const PLUS_CODE = 43; // + -const MINUS_CODE = 45; // - - -const plugin = { - name: 'assignment', - assignmentOperators: new Set(['=', '*=', '**=', '/=', '%=', '+=', '-=', '<<=', '>>=', '>>>=', '&=', '^=', '|=', '||=', '&&=', '??=']), - updateOperators: [PLUS_CODE, MINUS_CODE], - assignmentPrecedence: 0.9, - init(jsep) { - const updateNodeTypes = [jsep.IDENTIFIER, jsep.MEMBER_EXP]; - plugin.assignmentOperators.forEach(op => jsep.addBinaryOp(op, plugin.assignmentPrecedence, true)); - jsep.hooks.add('gobble-token', function gobbleUpdatePrefix(env) { - const code = this.code; - if (plugin.updateOperators.some(c => c === code && c === this.expr.charCodeAt(this.index + 1))) { - this.index += 2; - env.node = { - type: 'UpdateExpression', - operator: code === PLUS_CODE ? '++' : '--', - argument: this.gobbleTokenProperty(this.gobbleIdentifier()), - prefix: true - }; - if (!env.node.argument || !updateNodeTypes.includes(env.node.argument.type)) { - this.throwError(`Unexpected ${env.node.operator}`); - } - } - }); - jsep.hooks.add('after-token', function gobbleUpdatePostfix(env) { - if (env.node) { - const code = this.code; - if (plugin.updateOperators.some(c => c === code && c === this.expr.charCodeAt(this.index + 1))) { - if (!updateNodeTypes.includes(env.node.type)) { - this.throwError(`Unexpected ${env.node.operator}`); - } - this.index += 2; - env.node = { - type: 'UpdateExpression', - operator: code === PLUS_CODE ? '++' : '--', - argument: env.node, - prefix: false - }; - } - } - }); - jsep.hooks.add('after-expression', function gobbleAssignment(env) { - if (env.node) { - // Note: Binaries can be chained in a single expression to respect - // operator precedence (i.e. a = b = 1 + 2 + 3) - // Update all binary assignment nodes in the tree - updateBinariesToAssignments(env.node); - } - }); - function updateBinariesToAssignments(node) { - if (plugin.assignmentOperators.has(node.operator)) { - node.type = 'AssignmentExpression'; - updateBinariesToAssignments(node.left); - updateBinariesToAssignments(node.right); - } else if (!node.operator) { - Object.values(node).forEach(val => { - if (val && typeof val === 'object') { - updateBinariesToAssignments(val); - } - }); - } - } - } -}; - -/* eslint-disable no-bitwise -- Convenient */ - -// register plugins -jsep.plugins.register(index, plugin); -jsep.addUnaryOp('typeof'); -jsep.addLiteral('null', null); -jsep.addLiteral('undefined', undefined); -const BLOCKED_PROTO_PROPERTIES = new Set(['constructor', '__proto__', '__defineGetter__', '__defineSetter__']); -const SafeEval = { - /** - * @param {jsep.Expression} ast - * @param {Record} subs - */ - evalAst(ast, subs) { - switch (ast.type) { - case 'BinaryExpression': - case 'LogicalExpression': - return SafeEval.evalBinaryExpression(ast, subs); - case 'Compound': - return SafeEval.evalCompound(ast, subs); - case 'ConditionalExpression': - return SafeEval.evalConditionalExpression(ast, subs); - case 'Identifier': - return SafeEval.evalIdentifier(ast, subs); - case 'Literal': - return SafeEval.evalLiteral(ast, subs); - case 'MemberExpression': - return SafeEval.evalMemberExpression(ast, subs); - case 'UnaryExpression': - return SafeEval.evalUnaryExpression(ast, subs); - case 'ArrayExpression': - return SafeEval.evalArrayExpression(ast, subs); - case 'CallExpression': - return SafeEval.evalCallExpression(ast, subs); - case 'AssignmentExpression': - return SafeEval.evalAssignmentExpression(ast, subs); - default: - throw SyntaxError('Unexpected expression', ast); - } - }, - evalBinaryExpression(ast, subs) { - const result = { - '||': (a, b) => a || b(), - '&&': (a, b) => a && b(), - '|': (a, b) => a | b(), - '^': (a, b) => a ^ b(), - '&': (a, b) => a & b(), - // eslint-disable-next-line eqeqeq -- API - '==': (a, b) => a == b(), - // eslint-disable-next-line eqeqeq -- API - '!=': (a, b) => a != b(), - '===': (a, b) => a === b(), - '!==': (a, b) => a !== b(), - '<': (a, b) => a < b(), - '>': (a, b) => a > b(), - '<=': (a, b) => a <= b(), - '>=': (a, b) => a >= b(), - '<<': (a, b) => a << b(), - '>>': (a, b) => a >> b(), - '>>>': (a, b) => a >>> b(), - '+': (a, b) => a + b(), - '-': (a, b) => a - b(), - '*': (a, b) => a * b(), - '/': (a, b) => a / b(), - '%': (a, b) => a % b() - }[ast.operator](SafeEval.evalAst(ast.left, subs), () => SafeEval.evalAst(ast.right, subs)); - return result; - }, - evalCompound(ast, subs) { - let last; - for (let i = 0; i < ast.body.length; i++) { - if (ast.body[i].type === 'Identifier' && ['var', 'let', 'const'].includes(ast.body[i].name) && ast.body[i + 1] && ast.body[i + 1].type === 'AssignmentExpression') { - // var x=2; is detected as - // [{Identifier var}, {AssignmentExpression x=2}] - // eslint-disable-next-line @stylistic/max-len -- Long - // eslint-disable-next-line sonarjs/updated-loop-counter -- Convenient - i += 1; - } - const expr = ast.body[i]; - last = SafeEval.evalAst(expr, subs); - } - return last; - }, - evalConditionalExpression(ast, subs) { - if (SafeEval.evalAst(ast.test, subs)) { - return SafeEval.evalAst(ast.consequent, subs); - } - return SafeEval.evalAst(ast.alternate, subs); - }, - evalIdentifier(ast, subs) { - if (Object.hasOwn(subs, ast.name)) { - return subs[ast.name]; - } - throw ReferenceError(`${ast.name} is not defined`); - }, - evalLiteral(ast) { - return ast.value; - }, - evalMemberExpression(ast, subs) { - const prop = ast.computed ? SafeEval.evalAst(ast.property) // `object[property]` - : ast.property.name; // `object.property` property is Identifier - const obj = SafeEval.evalAst(ast.object, subs); - if (obj === undefined || obj === null) { - throw TypeError(`Cannot read properties of ${obj} (reading '${prop}')`); - } - if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) { - throw TypeError(`Cannot read properties of ${obj} (reading '${prop}')`); - } - const result = obj[prop]; - if (typeof result === 'function') { - return result.bind(obj); // arrow functions aren't affected by bind. - } - return result; - }, - evalUnaryExpression(ast, subs) { - const result = { - '-': a => -SafeEval.evalAst(a, subs), - '!': a => !SafeEval.evalAst(a, subs), - '~': a => ~SafeEval.evalAst(a, subs), - // eslint-disable-next-line no-implicit-coercion -- API - '+': a => +SafeEval.evalAst(a, subs), - typeof: a => typeof SafeEval.evalAst(a, subs) - }[ast.operator](ast.argument); - return result; - }, - evalArrayExpression(ast, subs) { - return ast.elements.map(el => SafeEval.evalAst(el, subs)); - }, - evalCallExpression(ast, subs) { - const args = ast.arguments.map(arg => SafeEval.evalAst(arg, subs)); - const func = SafeEval.evalAst(ast.callee, subs); - // if (func === Function) { - // throw new Error('Function constructor is disabled'); - // } - return func(...args); - }, - evalAssignmentExpression(ast, subs) { - if (ast.left.type !== 'Identifier') { - throw SyntaxError('Invalid left-hand side in assignment'); - } - const id = ast.left.name; - const value = SafeEval.evalAst(ast.right, subs); - subs[id] = value; - return subs[id]; - } -}; - -/** - * A replacement for NodeJS' VM.Script which is also {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP | Content Security Policy} friendly. - */ -class SafeScript { - /** - * @param {string} expr Expression to evaluate - */ - constructor(expr) { - this.code = expr; - this.ast = jsep(this.code); - } - - /** - * @param {object} context Object whose items will be added - * to evaluation - * @returns {EvaluatedResult} Result of evaluated code - */ - runInNewContext(context) { - // `Object.create(null)` creates a prototypeless object - const keyMap = Object.assign(Object.create(null), context); - return SafeEval.evalAst(this.ast, keyMap); - } -} - -/* eslint-disable camelcase -- Convenient for escaping */ - - -/** - * @typedef {null|boolean|number|string|object|GenericArray} JSONObject - */ - -/** - * @typedef {any} AnyItem - */ - -/** - * @typedef {any} AnyResult - */ - -/** - * Copies array and then pushes item into it. - * @param {GenericArray} arr Array to copy and into which to push - * @param {AnyItem} 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 {AnyItem} 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 {AnyResult} 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 {object} ReturnObject -* @property {string} path -* @property {JSONObject} value -* @property {object|GenericArray} parent -* @property {string} parentProperty -*/ - -/** -* @callback JSONPathCallback -* @param {string|object} preferredOutput -* @param {"value"|"property"} type -* @param {ReturnObject} fullRetObj -* @returns {void} -*/ - -/** -* @callback OtherTypeCallback -* @param {JSONObject} val -* @param {string} path -* @param {object|GenericArray} parent -* @param {string} parentPropName -* @returns {boolean} -*/ - -/** - * @typedef {any} ContextItem - */ - -/** - * @typedef {any} EvaluatedResult - */ - -/** -* @callback EvalCallback -* @param {string} code -* @param {ContextItem} context -* @returns {EvaluatedResult} -*/ - -/** - * @typedef {typeof SafeScript} EvalClass - */ - -/** - * @typedef {object} 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 {object} [sandbox={}] - * @property {EvalCallback|EvalClass|'safe'|'native'| - * boolean} [eval = 'safe'] - * @property {object|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] - */ - -/** - * @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 -- Allow for pseudo-class - 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 = Object.hasOwn(opts, 'wrap') ? opts.wrap : true; - this.sandbox = opts.sandbox || {}; - this.eval = opts.eval === undefined ? 'safe' : opts.eval; - this.ignoreEvalErrors = typeof opts.ignoreEvalErrors === 'undefined' ? false : opts.ignoreEvalErrors; - 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.currEval = this.eval; - 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 (!Object.hasOwn(expr, 'json')) { - throw new TypeError('You must supply a "json" property when providing an object ' + 'argument to JSONPath.evaluate().'); - } - ({ - json - } = expr); - flatten = Object.hasOwn(expr, 'flatten') ? expr.flatten : flatten; - this.currResultType = Object.hasOwn(expr, 'resultType') ? expr.resultType : this.currResultType; - this.currSandbox = Object.hasOwn(expr, 'sandbox') ? expr.sandbox : this.currSandbox; - wrap = Object.hasOwn(expr, 'wrap') ? expr.wrap : wrap; - this.currEval = Object.hasOwn(expr, 'eval') ? expr.eval : this.currEval; - callback = Object.hasOwn(expr, 'callback') ? expr.callback : callback; - this.currOtherTypeCallback = Object.hasOwn(expr, 'otherTypeCallback') ? expr.otherTypeCallback : this.currOtherTypeCallback; - currParent = Object.hasOwn(expr, 'parent') ? expr.parent : currParent; - currParentProperty = Object.hasOwn(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 n/callback-return -- No need to return - callback(preferredOutput, type, fullRetObj); - } -}; - -/** - * - * @param {string} expr - * @param {JSONObject} val - * @param {string} path - * @param {object|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 && Object.hasOwn(val, loc)) { - // simple case--directly follow property - addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, hasArrExpr)); - // eslint-disable-next-line unicorn/prefer-switch -- Part of larger `if` - } else if (loc === '*') { - // all child properties - this._walk(val, m => { - addRet(this._trace(x, val[m], push(path, m), val, m, callback, 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(val, m => { - // We don't join m and x here because we only want parents, - // not scalar values - if (typeof val[m] === 'object') { - // Keep going with recursive descent on val's - // object children - addRet(this._trace(expr.slice(), val[m], push(path, m), val, m, callback, 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.currEval === false) { - throw new Error('Eval [?(expr)] prevented in JSONPath expression.'); - } - const safeLoc = loc.replace(/^\?\((.*?)\)$/u, '$1'); - // check for a nested filter expression - const nested = /@.?([^?]*)[['](\??\(.*?\))(?!.\)\])[\]']/gu.exec(safeLoc); - if (nested) { - // find if there are matches in the nested expression - // add them to the result set if there is at least one match - this._walk(val, m => { - const npath = [nested[2]]; - const nvalue = nested[1] ? val[m][nested[1]] : val[m]; - const filterResults = this._trace(npath, nvalue, path, parent, parentPropName, callback, true); - if (filterResults.length > 0) { - addRet(this._trace(x, val[m], push(path, m), val, m, callback, true)); - } - }); - } else { - this._walk(val, m => { - if (this._eval(safeLoc, val[m], m, path, parent, parentPropName)) { - addRet(this._trace(x, val[m], push(path, m), val, m, callback, true)); - } - }); - } - } else if (loc[0] === '(') { - // [(expr)] (dynamic property/index) - if (this.currEval === false) { - 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.at(-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': - 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': - 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; - /* c8 ignore next 2 */ - 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 && Object.hasOwn(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 && Object.hasOwn(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++) { - // eslint-disable-next-line @stylistic/max-len -- Long - // eslint-disable-next-line sonarjs/updated-loop-counter -- Convenient - t++; - ret.splice(t, 0, tmp[tt]); - } - } else { - ret[t] = tmp; - } - } - } - } - return ret; -}; -JSONPath.prototype._walk = function (val, f) { - if (Array.isArray(val)) { - const n = val.length; - for (let i = 0; i < n; i++) { - f(i); - } - } else if (val && typeof val === 'object') { - Object.keys(val).forEach(m => { - f(m); - }); - } -}; -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) { - this.currSandbox._$_parentProperty = parentPropName; - this.currSandbox._$_parent = parent; - this.currSandbox._$_property = _vname; - this.currSandbox._$_root = this.json; - this.currSandbox._$_v = _v; - const containsPath = code.includes('@path'); - if (containsPath) { - this.currSandbox._$_path = JSONPath.toPathString(path.concat([_vname])); - } - const scriptCacheKey = this.currEval + 'Script:' + code; - if (!JSONPath.cache[scriptCacheKey]) { - let script = code.replaceAll('@parentProperty', '_$_parentProperty').replaceAll('@parent', '_$_parent').replaceAll('@property', '_$_property').replaceAll('@root', '_$_root').replaceAll(/@([.\s)[])/gu, '_$_v$1'); - if (containsPath) { - script = script.replaceAll('@path', '_$_path'); - } - if (this.currEval === 'safe' || this.currEval === true || this.currEval === undefined) { - JSONPath.cache[scriptCacheKey] = new this.safeVm.Script(script); - } else if (this.currEval === 'native') { - JSONPath.cache[scriptCacheKey] = new this.vm.Script(script); - } else if (typeof this.currEval === 'function' && this.currEval.prototype && Object.hasOwn(this.currEval.prototype, 'runInNewContext')) { - const CurrEval = this.currEval; - JSONPath.cache[scriptCacheKey] = new CurrEval(script); - } else if (typeof this.currEval === 'function') { - JSONPath.cache[scriptCacheKey] = { - runInNewContext: context => this.currEval(script, context) - }; - } else { - throw new TypeError(`Unknown "eval" property "${this.currEval}"`); - } - } - try { - return JSONPath.cache[scriptCacheKey].runInNewContext(this.currSandbox); - } catch (e) { - if (this.ignoreEvalErrors) { - return false; - } - 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().replaceAll('~', '~0').replaceAll('/', '~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 - .replaceAll(/@(?:null|boolean|number|string|integer|undefined|nonFinite|scalar|array|object|function|other)\(\)/gu, ';$&;') - // Parenthetical evaluations (filtering and otherwise), directly - // within brackets or single quotes - .replaceAll(/[['](\??\(.*?\))[\]'](?!.\])/gu, function ($0, $1) { - return '[#' + (subx.push($1) - 1) + ']'; - }) - // Escape periods and tildes within properties - .replaceAll(/\[['"]([^'\]]*)['"]\]/gu, function ($0, prop) { - return "['" + prop.replaceAll('.', '%@%').replaceAll('~', '%%@@%%') + "']"; - }) - // Properties operator - .replaceAll('~', ';~;') - // Split by property boundaries - .replaceAll(/['"]?\.['"]?(?![^[]*\])|\[['"]?/gu, ';') - // Reinsert periods within properties - .replaceAll('%@%', '.') - // Reinsert tildes within properties - .replaceAll('%%@@%%', '~') - // Parent - .replaceAll(/(?:;)?(\^+)(?:;)?/gu, function ($0, ups) { - return ';' + ups.split('').join(';') + ';'; - }) - // Descendents - .replaceAll(/;;;|;;/gu, ';..;') - // Remove trailing - .replaceAll(/;$|'?\]|'$/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.safeVm = { - Script: SafeScript -}; - -JSONPath.prototype.vm = vm; - -exports.JSONPath = JSONPath; - - /***/ }), /***/ 26672: @@ -136484,13 +134391,2091 @@ exports.writeTag = writeTag; exports.writeTree = writeTree; +/***/ }), + +/***/ 24697: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +var vm = __nccwpck_require__(26144); + +/** + * @implements {IHooks} + */ +class Hooks { + /** + * @callback HookCallback + * @this {*|Jsep} this + * @param {Jsep} env + * @returns: void + */ + /** + * Adds the given callback to the list of callbacks for the given hook. + * + * The callback will be invoked when the hook it is registered for is run. + * + * One callback function can be registered to multiple hooks and the same hook multiple times. + * + * @param {string|object} name The name of the hook, or an object of callbacks keyed by name + * @param {HookCallback|boolean} callback The callback function which is given environment variables. + * @param {?boolean} [first=false] Will add the hook to the top of the list (defaults to the bottom) + * @public + */ + add(name, callback, first) { + if (typeof arguments[0] != 'string') { + // Multiple hook callbacks, keyed by name + for (let name in arguments[0]) { + this.add(name, arguments[0][name], arguments[1]); + } + } else { + (Array.isArray(name) ? name : [name]).forEach(function (name) { + this[name] = this[name] || []; + if (callback) { + this[name][first ? 'unshift' : 'push'](callback); + } + }, this); + } + } + + /** + * Runs a hook invoking all registered callbacks with the given environment variables. + * + * Callbacks will be invoked synchronously and in the order in which they were registered. + * + * @param {string} name The name of the hook. + * @param {Object} env The environment variables of the hook passed to all callbacks registered. + * @public + */ + run(name, env) { + this[name] = this[name] || []; + this[name].forEach(function (callback) { + callback.call(env && env.context ? env.context : env, env); + }); + } +} + +/** + * @implements {IPlugins} + */ +class Plugins { + constructor(jsep) { + this.jsep = jsep; + this.registered = {}; + } + + /** + * @callback PluginSetup + * @this {Jsep} jsep + * @returns: void + */ + /** + * Adds the given plugin(s) to the registry + * + * @param {object} plugins + * @param {string} plugins.name The name of the plugin + * @param {PluginSetup} plugins.init The init function + * @public + */ + register(...plugins) { + plugins.forEach(plugin => { + if (typeof plugin !== 'object' || !plugin.name || !plugin.init) { + throw new Error('Invalid JSEP plugin format'); + } + if (this.registered[plugin.name]) { + // already registered. Ignore. + return; + } + plugin.init(this.jsep); + this.registered[plugin.name] = plugin; + }); + } +} + +// JavaScript Expression Parser (JSEP) 1.4.0 + +class Jsep { + /** + * @returns {string} + */ + static get version() { + // To be filled in by the template + return '1.4.0'; + } + + /** + * @returns {string} + */ + static toString() { + return 'JavaScript Expression Parser (JSEP) v' + Jsep.version; + } + // ==================== CONFIG ================================ + /** + * @method addUnaryOp + * @param {string} op_name The name of the unary op to add + * @returns {Jsep} + */ + static addUnaryOp(op_name) { + Jsep.max_unop_len = Math.max(op_name.length, Jsep.max_unop_len); + Jsep.unary_ops[op_name] = 1; + return Jsep; + } + + /** + * @method jsep.addBinaryOp + * @param {string} op_name The name of the binary op to add + * @param {number} precedence The precedence of the binary op (can be a float). Higher number = higher precedence + * @param {boolean} [isRightAssociative=false] whether operator is right-associative + * @returns {Jsep} + */ + static addBinaryOp(op_name, precedence, isRightAssociative) { + Jsep.max_binop_len = Math.max(op_name.length, Jsep.max_binop_len); + Jsep.binary_ops[op_name] = precedence; + if (isRightAssociative) { + Jsep.right_associative.add(op_name); + } else { + Jsep.right_associative.delete(op_name); + } + return Jsep; + } + + /** + * @method addIdentifierChar + * @param {string} char The additional character to treat as a valid part of an identifier + * @returns {Jsep} + */ + static addIdentifierChar(char) { + Jsep.additional_identifier_chars.add(char); + return Jsep; + } + + /** + * @method addLiteral + * @param {string} literal_name The name of the literal to add + * @param {*} literal_value The value of the literal + * @returns {Jsep} + */ + static addLiteral(literal_name, literal_value) { + Jsep.literals[literal_name] = literal_value; + return Jsep; + } + + /** + * @method removeUnaryOp + * @param {string} op_name The name of the unary op to remove + * @returns {Jsep} + */ + static removeUnaryOp(op_name) { + delete Jsep.unary_ops[op_name]; + if (op_name.length === Jsep.max_unop_len) { + Jsep.max_unop_len = Jsep.getMaxKeyLen(Jsep.unary_ops); + } + return Jsep; + } + + /** + * @method removeAllUnaryOps + * @returns {Jsep} + */ + static removeAllUnaryOps() { + Jsep.unary_ops = {}; + Jsep.max_unop_len = 0; + return Jsep; + } + + /** + * @method removeIdentifierChar + * @param {string} char The additional character to stop treating as a valid part of an identifier + * @returns {Jsep} + */ + static removeIdentifierChar(char) { + Jsep.additional_identifier_chars.delete(char); + return Jsep; + } + + /** + * @method removeBinaryOp + * @param {string} op_name The name of the binary op to remove + * @returns {Jsep} + */ + static removeBinaryOp(op_name) { + delete Jsep.binary_ops[op_name]; + if (op_name.length === Jsep.max_binop_len) { + Jsep.max_binop_len = Jsep.getMaxKeyLen(Jsep.binary_ops); + } + Jsep.right_associative.delete(op_name); + return Jsep; + } + + /** + * @method removeAllBinaryOps + * @returns {Jsep} + */ + static removeAllBinaryOps() { + Jsep.binary_ops = {}; + Jsep.max_binop_len = 0; + return Jsep; + } + + /** + * @method removeLiteral + * @param {string} literal_name The name of the literal to remove + * @returns {Jsep} + */ + static removeLiteral(literal_name) { + delete Jsep.literals[literal_name]; + return Jsep; + } + + /** + * @method removeAllLiterals + * @returns {Jsep} + */ + static removeAllLiterals() { + Jsep.literals = {}; + return Jsep; + } + // ==================== END CONFIG ============================ + + /** + * @returns {string} + */ + get char() { + return this.expr.charAt(this.index); + } + + /** + * @returns {number} + */ + get code() { + return this.expr.charCodeAt(this.index); + } + /** + * @param {string} expr a string with the passed in express + * @returns Jsep + */ + constructor(expr) { + // `index` stores the character number we are currently at + // All of the gobbles below will modify `index` as we move along + this.expr = expr; + this.index = 0; + } + + /** + * static top-level parser + * @returns {jsep.Expression} + */ + static parse(expr) { + return new Jsep(expr).parse(); + } + + /** + * Get the longest key length of any object + * @param {object} obj + * @returns {number} + */ + static getMaxKeyLen(obj) { + return Math.max(0, ...Object.keys(obj).map(k => k.length)); + } + + /** + * `ch` is a character code in the next three functions + * @param {number} ch + * @returns {boolean} + */ + static isDecimalDigit(ch) { + return ch >= 48 && ch <= 57; // 0...9 + } + + /** + * Returns the precedence of a binary operator or `0` if it isn't a binary operator. Can be float. + * @param {string} op_val + * @returns {number} + */ + static binaryPrecedence(op_val) { + return Jsep.binary_ops[op_val] || 0; + } + + /** + * Looks for start of identifier + * @param {number} ch + * @returns {boolean} + */ + static isIdentifierStart(ch) { + return ch >= 65 && ch <= 90 || + // A...Z + ch >= 97 && ch <= 122 || + // a...z + ch >= 128 && !Jsep.binary_ops[String.fromCharCode(ch)] || + // any non-ASCII that is not an operator + Jsep.additional_identifier_chars.has(String.fromCharCode(ch)); // additional characters + } + + /** + * @param {number} ch + * @returns {boolean} + */ + static isIdentifierPart(ch) { + return Jsep.isIdentifierStart(ch) || Jsep.isDecimalDigit(ch); + } + + /** + * throw error at index of the expression + * @param {string} message + * @throws + */ + throwError(message) { + const error = new Error(message + ' at character ' + this.index); + error.index = this.index; + error.description = message; + throw error; + } + + /** + * Run a given hook + * @param {string} name + * @param {jsep.Expression|false} [node] + * @returns {?jsep.Expression} + */ + runHook(name, node) { + if (Jsep.hooks[name]) { + const env = { + context: this, + node + }; + Jsep.hooks.run(name, env); + return env.node; + } + return node; + } + + /** + * Runs a given hook until one returns a node + * @param {string} name + * @returns {?jsep.Expression} + */ + searchHook(name) { + if (Jsep.hooks[name]) { + const env = { + context: this + }; + Jsep.hooks[name].find(function (callback) { + callback.call(env.context, env); + return env.node; + }); + return env.node; + } + } + + /** + * Push `index` up to the next non-space character + */ + gobbleSpaces() { + let ch = this.code; + // Whitespace + while (ch === Jsep.SPACE_CODE || ch === Jsep.TAB_CODE || ch === Jsep.LF_CODE || ch === Jsep.CR_CODE) { + ch = this.expr.charCodeAt(++this.index); + } + this.runHook('gobble-spaces'); + } + + /** + * Top-level method to parse all expressions and returns compound or single node + * @returns {jsep.Expression} + */ + parse() { + this.runHook('before-all'); + const nodes = this.gobbleExpressions(); + + // If there's only one expression just try returning the expression + const node = nodes.length === 1 ? nodes[0] : { + type: Jsep.COMPOUND, + body: nodes + }; + return this.runHook('after-all', node); + } + + /** + * top-level parser (but can be reused within as well) + * @param {number} [untilICode] + * @returns {jsep.Expression[]} + */ + gobbleExpressions(untilICode) { + let nodes = [], + ch_i, + node; + while (this.index < this.expr.length) { + ch_i = this.code; + + // Expressions can be separated by semicolons, commas, or just inferred without any + // separators + if (ch_i === Jsep.SEMCOL_CODE || ch_i === Jsep.COMMA_CODE) { + this.index++; // ignore separators + } else { + // Try to gobble each expression individually + if (node = this.gobbleExpression()) { + nodes.push(node); + // If we weren't able to find a binary expression and are out of room, then + // the expression passed in probably has too much + } else if (this.index < this.expr.length) { + if (ch_i === untilICode) { + break; + } + this.throwError('Unexpected "' + this.char + '"'); + } + } + } + return nodes; + } + + /** + * The main parsing function. + * @returns {?jsep.Expression} + */ + gobbleExpression() { + const node = this.searchHook('gobble-expression') || this.gobbleBinaryExpression(); + this.gobbleSpaces(); + return this.runHook('after-expression', node); + } + + /** + * Search for the operation portion of the string (e.g. `+`, `===`) + * Start by taking the longest possible binary operations (3 characters: `===`, `!==`, `>>>`) + * and move down from 3 to 2 to 1 character until a matching binary operation is found + * then, return that binary operation + * @returns {string|boolean} + */ + gobbleBinaryOp() { + this.gobbleSpaces(); + let to_check = this.expr.substr(this.index, Jsep.max_binop_len); + let tc_len = to_check.length; + while (tc_len > 0) { + // Don't accept a binary op when it is an identifier. + // Binary ops that start with a identifier-valid character must be followed + // by a non identifier-part valid character + if (Jsep.binary_ops.hasOwnProperty(to_check) && (!Jsep.isIdentifierStart(this.code) || this.index + to_check.length < this.expr.length && !Jsep.isIdentifierPart(this.expr.charCodeAt(this.index + to_check.length)))) { + this.index += tc_len; + return to_check; + } + to_check = to_check.substr(0, --tc_len); + } + return false; + } + + /** + * This function is responsible for gobbling an individual expression, + * e.g. `1`, `1+2`, `a+(b*2)-Math.sqrt(2)` + * @returns {?jsep.BinaryExpression} + */ + gobbleBinaryExpression() { + let node, biop, prec, stack, biop_info, left, right, i, cur_biop; + + // First, try to get the leftmost thing + // Then, check to see if there's a binary operator operating on that leftmost thing + // Don't gobbleBinaryOp without a left-hand-side + left = this.gobbleToken(); + if (!left) { + return left; + } + biop = this.gobbleBinaryOp(); + + // If there wasn't a binary operator, just return the leftmost node + if (!biop) { + return left; + } + + // Otherwise, we need to start a stack to properly place the binary operations in their + // precedence structure + biop_info = { + value: biop, + prec: Jsep.binaryPrecedence(biop), + right_a: Jsep.right_associative.has(biop) + }; + right = this.gobbleToken(); + if (!right) { + this.throwError("Expected expression after " + biop); + } + stack = [left, biop_info, right]; + + // Properly deal with precedence using [recursive descent](http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm) + while (biop = this.gobbleBinaryOp()) { + prec = Jsep.binaryPrecedence(biop); + if (prec === 0) { + this.index -= biop.length; + break; + } + biop_info = { + value: biop, + prec, + right_a: Jsep.right_associative.has(biop) + }; + cur_biop = biop; + + // Reduce: make a binary expression from the three topmost entries. + const comparePrev = prev => biop_info.right_a && prev.right_a ? prec > prev.prec : prec <= prev.prec; + while (stack.length > 2 && comparePrev(stack[stack.length - 2])) { + right = stack.pop(); + biop = stack.pop().value; + left = stack.pop(); + node = { + type: Jsep.BINARY_EXP, + operator: biop, + left, + right + }; + stack.push(node); + } + node = this.gobbleToken(); + if (!node) { + this.throwError("Expected expression after " + cur_biop); + } + stack.push(biop_info, node); + } + i = stack.length - 1; + node = stack[i]; + while (i > 1) { + node = { + type: Jsep.BINARY_EXP, + operator: stack[i - 1].value, + left: stack[i - 2], + right: node + }; + i -= 2; + } + return node; + } + + /** + * An individual part of a binary expression: + * e.g. `foo.bar(baz)`, `1`, `"abc"`, `(a % 2)` (because it's in parenthesis) + * @returns {boolean|jsep.Expression} + */ + gobbleToken() { + let ch, to_check, tc_len, node; + this.gobbleSpaces(); + node = this.searchHook('gobble-token'); + if (node) { + return this.runHook('after-token', node); + } + ch = this.code; + if (Jsep.isDecimalDigit(ch) || ch === Jsep.PERIOD_CODE) { + // Char code 46 is a dot `.` which can start off a numeric literal + return this.gobbleNumericLiteral(); + } + if (ch === Jsep.SQUOTE_CODE || ch === Jsep.DQUOTE_CODE) { + // Single or double quotes + node = this.gobbleStringLiteral(); + } else if (ch === Jsep.OBRACK_CODE) { + node = this.gobbleArray(); + } else { + to_check = this.expr.substr(this.index, Jsep.max_unop_len); + tc_len = to_check.length; + while (tc_len > 0) { + // Don't accept an unary op when it is an identifier. + // Unary ops that start with a identifier-valid character must be followed + // by a non identifier-part valid character + if (Jsep.unary_ops.hasOwnProperty(to_check) && (!Jsep.isIdentifierStart(this.code) || this.index + to_check.length < this.expr.length && !Jsep.isIdentifierPart(this.expr.charCodeAt(this.index + to_check.length)))) { + this.index += tc_len; + const argument = this.gobbleToken(); + if (!argument) { + this.throwError('missing unaryOp argument'); + } + return this.runHook('after-token', { + type: Jsep.UNARY_EXP, + operator: to_check, + argument, + prefix: true + }); + } + to_check = to_check.substr(0, --tc_len); + } + if (Jsep.isIdentifierStart(ch)) { + node = this.gobbleIdentifier(); + if (Jsep.literals.hasOwnProperty(node.name)) { + node = { + type: Jsep.LITERAL, + value: Jsep.literals[node.name], + raw: node.name + }; + } else if (node.name === Jsep.this_str) { + node = { + type: Jsep.THIS_EXP + }; + } + } else if (ch === Jsep.OPAREN_CODE) { + // open parenthesis + node = this.gobbleGroup(); + } + } + if (!node) { + return this.runHook('after-token', false); + } + node = this.gobbleTokenProperty(node); + return this.runHook('after-token', node); + } + + /** + * Gobble properties of of identifiers/strings/arrays/groups. + * e.g. `foo`, `bar.baz`, `foo['bar'].baz` + * It also gobbles function calls: + * e.g. `Math.acos(obj.angle)` + * @param {jsep.Expression} node + * @returns {jsep.Expression} + */ + gobbleTokenProperty(node) { + this.gobbleSpaces(); + let ch = this.code; + while (ch === Jsep.PERIOD_CODE || ch === Jsep.OBRACK_CODE || ch === Jsep.OPAREN_CODE || ch === Jsep.QUMARK_CODE) { + let optional; + if (ch === Jsep.QUMARK_CODE) { + if (this.expr.charCodeAt(this.index + 1) !== Jsep.PERIOD_CODE) { + break; + } + optional = true; + this.index += 2; + this.gobbleSpaces(); + ch = this.code; + } + this.index++; + if (ch === Jsep.OBRACK_CODE) { + node = { + type: Jsep.MEMBER_EXP, + computed: true, + object: node, + property: this.gobbleExpression() + }; + if (!node.property) { + this.throwError('Unexpected "' + this.char + '"'); + } + this.gobbleSpaces(); + ch = this.code; + if (ch !== Jsep.CBRACK_CODE) { + this.throwError('Unclosed ['); + } + this.index++; + } else if (ch === Jsep.OPAREN_CODE) { + // A function call is being made; gobble all the arguments + node = { + type: Jsep.CALL_EXP, + 'arguments': this.gobbleArguments(Jsep.CPAREN_CODE), + callee: node + }; + } else if (ch === Jsep.PERIOD_CODE || optional) { + if (optional) { + this.index--; + } + this.gobbleSpaces(); + node = { + type: Jsep.MEMBER_EXP, + computed: false, + object: node, + property: this.gobbleIdentifier() + }; + } + if (optional) { + node.optional = true; + } // else leave undefined for compatibility with esprima + + this.gobbleSpaces(); + ch = this.code; + } + return node; + } + + /** + * Parse simple numeric literals: `12`, `3.4`, `.5`. Do this by using a string to + * keep track of everything in the numeric literal and then calling `parseFloat` on that string + * @returns {jsep.Literal} + */ + gobbleNumericLiteral() { + let number = '', + ch, + chCode; + while (Jsep.isDecimalDigit(this.code)) { + number += this.expr.charAt(this.index++); + } + if (this.code === Jsep.PERIOD_CODE) { + // can start with a decimal marker + number += this.expr.charAt(this.index++); + while (Jsep.isDecimalDigit(this.code)) { + number += this.expr.charAt(this.index++); + } + } + ch = this.char; + if (ch === 'e' || ch === 'E') { + // exponent marker + number += this.expr.charAt(this.index++); + ch = this.char; + if (ch === '+' || ch === '-') { + // exponent sign + number += this.expr.charAt(this.index++); + } + while (Jsep.isDecimalDigit(this.code)) { + // exponent itself + number += this.expr.charAt(this.index++); + } + if (!Jsep.isDecimalDigit(this.expr.charCodeAt(this.index - 1))) { + this.throwError('Expected exponent (' + number + this.char + ')'); + } + } + chCode = this.code; + + // Check to make sure this isn't a variable name that start with a number (123abc) + if (Jsep.isIdentifierStart(chCode)) { + this.throwError('Variable names cannot start with a number (' + number + this.char + ')'); + } else if (chCode === Jsep.PERIOD_CODE || number.length === 1 && number.charCodeAt(0) === Jsep.PERIOD_CODE) { + this.throwError('Unexpected period'); + } + return { + type: Jsep.LITERAL, + value: parseFloat(number), + raw: number + }; + } + + /** + * Parses a string literal, staring with single or double quotes with basic support for escape codes + * e.g. `"hello world"`, `'this is\nJSEP'` + * @returns {jsep.Literal} + */ + gobbleStringLiteral() { + let str = ''; + const startIndex = this.index; + const quote = this.expr.charAt(this.index++); + let closed = false; + while (this.index < this.expr.length) { + let ch = this.expr.charAt(this.index++); + if (ch === quote) { + closed = true; + break; + } else if (ch === '\\') { + // Check for all of the common escape codes + ch = this.expr.charAt(this.index++); + switch (ch) { + case 'n': + str += '\n'; + break; + case 'r': + str += '\r'; + break; + case 't': + str += '\t'; + break; + case 'b': + str += '\b'; + break; + case 'f': + str += '\f'; + break; + case 'v': + str += '\x0B'; + break; + default: + str += ch; + } + } else { + str += ch; + } + } + if (!closed) { + this.throwError('Unclosed quote after "' + str + '"'); + } + return { + type: Jsep.LITERAL, + value: str, + raw: this.expr.substring(startIndex, this.index) + }; + } + + /** + * Gobbles only identifiers + * e.g.: `foo`, `_value`, `$x1` + * Also, this function checks if that identifier is a literal: + * (e.g. `true`, `false`, `null`) or `this` + * @returns {jsep.Identifier} + */ + gobbleIdentifier() { + let ch = this.code, + start = this.index; + if (Jsep.isIdentifierStart(ch)) { + this.index++; + } else { + this.throwError('Unexpected ' + this.char); + } + while (this.index < this.expr.length) { + ch = this.code; + if (Jsep.isIdentifierPart(ch)) { + this.index++; + } else { + break; + } + } + return { + type: Jsep.IDENTIFIER, + name: this.expr.slice(start, this.index) + }; + } + + /** + * Gobbles a list of arguments within the context of a function call + * or array literal. This function also assumes that the opening character + * `(` or `[` has already been gobbled, and gobbles expressions and commas + * until the terminator character `)` or `]` is encountered. + * e.g. `foo(bar, baz)`, `my_func()`, or `[bar, baz]` + * @param {number} termination + * @returns {jsep.Expression[]} + */ + gobbleArguments(termination) { + const args = []; + let closed = false; + let separator_count = 0; + while (this.index < this.expr.length) { + this.gobbleSpaces(); + let ch_i = this.code; + if (ch_i === termination) { + // done parsing + closed = true; + this.index++; + if (termination === Jsep.CPAREN_CODE && separator_count && separator_count >= args.length) { + this.throwError('Unexpected token ' + String.fromCharCode(termination)); + } + break; + } else if (ch_i === Jsep.COMMA_CODE) { + // between expressions + this.index++; + separator_count++; + if (separator_count !== args.length) { + // missing argument + if (termination === Jsep.CPAREN_CODE) { + this.throwError('Unexpected token ,'); + } else if (termination === Jsep.CBRACK_CODE) { + for (let arg = args.length; arg < separator_count; arg++) { + args.push(null); + } + } + } + } else if (args.length !== separator_count && separator_count !== 0) { + // NOTE: `&& separator_count !== 0` allows for either all commas, or all spaces as arguments + this.throwError('Expected comma'); + } else { + const node = this.gobbleExpression(); + if (!node || node.type === Jsep.COMPOUND) { + this.throwError('Expected comma'); + } + args.push(node); + } + } + if (!closed) { + this.throwError('Expected ' + String.fromCharCode(termination)); + } + return args; + } + + /** + * Responsible for parsing a group of things within parentheses `()` + * that have no identifier in front (so not a function call) + * This function assumes that it needs to gobble the opening parenthesis + * and then tries to gobble everything within that parenthesis, assuming + * that the next thing it should see is the close parenthesis. If not, + * then the expression probably doesn't have a `)` + * @returns {boolean|jsep.Expression} + */ + gobbleGroup() { + this.index++; + let nodes = this.gobbleExpressions(Jsep.CPAREN_CODE); + if (this.code === Jsep.CPAREN_CODE) { + this.index++; + if (nodes.length === 1) { + return nodes[0]; + } else if (!nodes.length) { + return false; + } else { + return { + type: Jsep.SEQUENCE_EXP, + expressions: nodes + }; + } + } else { + this.throwError('Unclosed ('); + } + } + + /** + * Responsible for parsing Array literals `[1, 2, 3]` + * This function assumes that it needs to gobble the opening bracket + * and then tries to gobble the expressions as arguments. + * @returns {jsep.ArrayExpression} + */ + gobbleArray() { + this.index++; + return { + type: Jsep.ARRAY_EXP, + elements: this.gobbleArguments(Jsep.CBRACK_CODE) + }; + } +} + +// Static fields: +const hooks = new Hooks(); +Object.assign(Jsep, { + hooks, + plugins: new Plugins(Jsep), + // Node Types + // ---------- + // This is the full set of types that any JSEP node can be. + // Store them here to save space when minified + COMPOUND: 'Compound', + SEQUENCE_EXP: 'SequenceExpression', + IDENTIFIER: 'Identifier', + MEMBER_EXP: 'MemberExpression', + LITERAL: 'Literal', + THIS_EXP: 'ThisExpression', + CALL_EXP: 'CallExpression', + UNARY_EXP: 'UnaryExpression', + BINARY_EXP: 'BinaryExpression', + ARRAY_EXP: 'ArrayExpression', + TAB_CODE: 9, + LF_CODE: 10, + CR_CODE: 13, + SPACE_CODE: 32, + PERIOD_CODE: 46, + // '.' + COMMA_CODE: 44, + // ',' + SQUOTE_CODE: 39, + // single quote + DQUOTE_CODE: 34, + // double quotes + OPAREN_CODE: 40, + // ( + CPAREN_CODE: 41, + // ) + OBRACK_CODE: 91, + // [ + CBRACK_CODE: 93, + // ] + QUMARK_CODE: 63, + // ? + SEMCOL_CODE: 59, + // ; + COLON_CODE: 58, + // : + + // Operations + // ---------- + // Use a quickly-accessible map to store all of the unary operators + // Values are set to `1` (it really doesn't matter) + unary_ops: { + '-': 1, + '!': 1, + '~': 1, + '+': 1 + }, + // Also use a map for the binary operations but set their values to their + // binary precedence for quick reference (higher number = higher precedence) + // see [Order of operations](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) + binary_ops: { + '||': 1, + '??': 1, + '&&': 2, + '|': 3, + '^': 4, + '&': 5, + '==': 6, + '!=': 6, + '===': 6, + '!==': 6, + '<': 7, + '>': 7, + '<=': 7, + '>=': 7, + '<<': 8, + '>>': 8, + '>>>': 8, + '+': 9, + '-': 9, + '*': 10, + '/': 10, + '%': 10, + '**': 11 + }, + // sets specific binary_ops as right-associative + right_associative: new Set(['**']), + // Additional valid identifier chars, apart from a-z, A-Z and 0-9 (except on the starting char) + additional_identifier_chars: new Set(['$', '_']), + // Literals + // ---------- + // Store the values to return for the various literals we may encounter + literals: { + 'true': true, + 'false': false, + 'null': null + }, + // Except for `this`, which is special. This could be changed to something like `'self'` as well + this_str: 'this' +}); +Jsep.max_unop_len = Jsep.getMaxKeyLen(Jsep.unary_ops); +Jsep.max_binop_len = Jsep.getMaxKeyLen(Jsep.binary_ops); + +// Backward Compatibility: +const jsep = expr => new Jsep(expr).parse(); +const stdClassProps = Object.getOwnPropertyNames(class Test {}); +Object.getOwnPropertyNames(Jsep).filter(prop => !stdClassProps.includes(prop) && jsep[prop] === undefined).forEach(m => { + jsep[m] = Jsep[m]; +}); +jsep.Jsep = Jsep; // allows for const { Jsep } = require('jsep'); + +const CONDITIONAL_EXP = 'ConditionalExpression'; +var ternary = { + name: 'ternary', + init(jsep) { + // Ternary expression: test ? consequent : alternate + jsep.hooks.add('after-expression', function gobbleTernary(env) { + if (env.node && this.code === jsep.QUMARK_CODE) { + this.index++; + const test = env.node; + const consequent = this.gobbleExpression(); + if (!consequent) { + this.throwError('Expected expression'); + } + this.gobbleSpaces(); + if (this.code === jsep.COLON_CODE) { + this.index++; + const alternate = this.gobbleExpression(); + if (!alternate) { + this.throwError('Expected expression'); + } + env.node = { + type: CONDITIONAL_EXP, + test, + consequent, + alternate + }; + + // check for operators of higher priority than ternary (i.e. assignment) + // jsep sets || at 1, and assignment at 0.9, and conditional should be between them + if (test.operator && jsep.binary_ops[test.operator] <= 0.9) { + let newTest = test; + while (newTest.right.operator && jsep.binary_ops[newTest.right.operator] <= 0.9) { + newTest = newTest.right; + } + env.node.test = newTest.right; + newTest.right = env.node; + env.node = test; + } + } else { + this.throwError('Expected :'); + } + } + }); + } +}; + +// Add default plugins: + +jsep.plugins.register(ternary); + +const FSLASH_CODE = 47; // '/' +const BSLASH_CODE = 92; // '\\' + +var index = { + name: 'regex', + init(jsep) { + // Regex literal: /abc123/ig + jsep.hooks.add('gobble-token', function gobbleRegexLiteral(env) { + if (this.code === FSLASH_CODE) { + const patternIndex = ++this.index; + let inCharSet = false; + while (this.index < this.expr.length) { + if (this.code === FSLASH_CODE && !inCharSet) { + const pattern = this.expr.slice(patternIndex, this.index); + let flags = ''; + while (++this.index < this.expr.length) { + const code = this.code; + if (code >= 97 && code <= 122 // a...z + || code >= 65 && code <= 90 // A...Z + || code >= 48 && code <= 57) { + // 0-9 + flags += this.char; + } else { + break; + } + } + let value; + try { + value = new RegExp(pattern, flags); + } catch (e) { + this.throwError(e.message); + } + env.node = { + type: jsep.LITERAL, + value, + raw: this.expr.slice(patternIndex - 1, this.index) + }; + + // allow . [] and () after regex: /regex/.test(a) + env.node = this.gobbleTokenProperty(env.node); + return env.node; + } + if (this.code === jsep.OBRACK_CODE) { + inCharSet = true; + } else if (inCharSet && this.code === jsep.CBRACK_CODE) { + inCharSet = false; + } + this.index += this.code === BSLASH_CODE ? 2 : 1; + } + this.throwError('Unclosed Regex'); + } + }); + } +}; + +const PLUS_CODE = 43; // + +const MINUS_CODE = 45; // - + +const plugin = { + name: 'assignment', + assignmentOperators: new Set(['=', '*=', '**=', '/=', '%=', '+=', '-=', '<<=', '>>=', '>>>=', '&=', '^=', '|=', '||=', '&&=', '??=']), + updateOperators: [PLUS_CODE, MINUS_CODE], + assignmentPrecedence: 0.9, + init(jsep) { + const updateNodeTypes = [jsep.IDENTIFIER, jsep.MEMBER_EXP]; + plugin.assignmentOperators.forEach(op => jsep.addBinaryOp(op, plugin.assignmentPrecedence, true)); + jsep.hooks.add('gobble-token', function gobbleUpdatePrefix(env) { + const code = this.code; + if (plugin.updateOperators.some(c => c === code && c === this.expr.charCodeAt(this.index + 1))) { + this.index += 2; + env.node = { + type: 'UpdateExpression', + operator: code === PLUS_CODE ? '++' : '--', + argument: this.gobbleTokenProperty(this.gobbleIdentifier()), + prefix: true + }; + if (!env.node.argument || !updateNodeTypes.includes(env.node.argument.type)) { + this.throwError(`Unexpected ${env.node.operator}`); + } + } + }); + jsep.hooks.add('after-token', function gobbleUpdatePostfix(env) { + if (env.node) { + const code = this.code; + if (plugin.updateOperators.some(c => c === code && c === this.expr.charCodeAt(this.index + 1))) { + if (!updateNodeTypes.includes(env.node.type)) { + this.throwError(`Unexpected ${env.node.operator}`); + } + this.index += 2; + env.node = { + type: 'UpdateExpression', + operator: code === PLUS_CODE ? '++' : '--', + argument: env.node, + prefix: false + }; + } + } + }); + jsep.hooks.add('after-expression', function gobbleAssignment(env) { + if (env.node) { + // Note: Binaries can be chained in a single expression to respect + // operator precedence (i.e. a = b = 1 + 2 + 3) + // Update all binary assignment nodes in the tree + updateBinariesToAssignments(env.node); + } + }); + function updateBinariesToAssignments(node) { + if (plugin.assignmentOperators.has(node.operator)) { + node.type = 'AssignmentExpression'; + updateBinariesToAssignments(node.left); + updateBinariesToAssignments(node.right); + } else if (!node.operator) { + Object.values(node).forEach(val => { + if (val && typeof val === 'object') { + updateBinariesToAssignments(val); + } + }); + } + } + } +}; + +/* eslint-disable no-bitwise -- Convenient */ + +// register plugins +jsep.plugins.register(index, plugin); +jsep.addUnaryOp('typeof'); +jsep.addLiteral('null', null); +jsep.addLiteral('undefined', undefined); +const BLOCKED_PROTO_PROPERTIES = new Set(['constructor', '__proto__', '__defineGetter__', '__defineSetter__']); +const SafeEval = { + /** + * @param {jsep.Expression} ast + * @param {Record} subs + */ + evalAst(ast, subs) { + switch (ast.type) { + case 'BinaryExpression': + case 'LogicalExpression': + return SafeEval.evalBinaryExpression(ast, subs); + case 'Compound': + return SafeEval.evalCompound(ast, subs); + case 'ConditionalExpression': + return SafeEval.evalConditionalExpression(ast, subs); + case 'Identifier': + return SafeEval.evalIdentifier(ast, subs); + case 'Literal': + return SafeEval.evalLiteral(ast, subs); + case 'MemberExpression': + return SafeEval.evalMemberExpression(ast, subs); + case 'UnaryExpression': + return SafeEval.evalUnaryExpression(ast, subs); + case 'ArrayExpression': + return SafeEval.evalArrayExpression(ast, subs); + case 'CallExpression': + return SafeEval.evalCallExpression(ast, subs); + case 'AssignmentExpression': + return SafeEval.evalAssignmentExpression(ast, subs); + default: + throw SyntaxError('Unexpected expression', ast); + } + }, + evalBinaryExpression(ast, subs) { + const result = { + '||': (a, b) => a || b(), + '&&': (a, b) => a && b(), + '|': (a, b) => a | b(), + '^': (a, b) => a ^ b(), + '&': (a, b) => a & b(), + // eslint-disable-next-line eqeqeq -- API + '==': (a, b) => a == b(), + // eslint-disable-next-line eqeqeq -- API + '!=': (a, b) => a != b(), + '===': (a, b) => a === b(), + '!==': (a, b) => a !== b(), + '<': (a, b) => a < b(), + '>': (a, b) => a > b(), + '<=': (a, b) => a <= b(), + '>=': (a, b) => a >= b(), + '<<': (a, b) => a << b(), + '>>': (a, b) => a >> b(), + '>>>': (a, b) => a >>> b(), + '+': (a, b) => a + b(), + '-': (a, b) => a - b(), + '*': (a, b) => a * b(), + '/': (a, b) => a / b(), + '%': (a, b) => a % b() + }[ast.operator](SafeEval.evalAst(ast.left, subs), () => SafeEval.evalAst(ast.right, subs)); + return result; + }, + evalCompound(ast, subs) { + let last; + for (let i = 0; i < ast.body.length; i++) { + if (ast.body[i].type === 'Identifier' && ['var', 'let', 'const'].includes(ast.body[i].name) && ast.body[i + 1] && ast.body[i + 1].type === 'AssignmentExpression') { + // var x=2; is detected as + // [{Identifier var}, {AssignmentExpression x=2}] + // eslint-disable-next-line @stylistic/max-len -- Long + // eslint-disable-next-line sonarjs/updated-loop-counter -- Convenient + i += 1; + } + const expr = ast.body[i]; + last = SafeEval.evalAst(expr, subs); + } + return last; + }, + evalConditionalExpression(ast, subs) { + if (SafeEval.evalAst(ast.test, subs)) { + return SafeEval.evalAst(ast.consequent, subs); + } + return SafeEval.evalAst(ast.alternate, subs); + }, + evalIdentifier(ast, subs) { + if (Object.hasOwn(subs, ast.name)) { + return subs[ast.name]; + } + throw ReferenceError(`${ast.name} is not defined`); + }, + evalLiteral(ast) { + return ast.value; + }, + evalMemberExpression(ast, subs) { + const prop = String( + // NOTE: `String(value)` throws error when + // value has overwritten the toString method to return non-string + // i.e. `value = {toString: () => []}` + ast.computed ? SafeEval.evalAst(ast.property) // `object[property]` + : ast.property.name // `object.property` property is Identifier + ); + const obj = SafeEval.evalAst(ast.object, subs); + if (obj === undefined || obj === null) { + throw TypeError(`Cannot read properties of ${obj} (reading '${prop}')`); + } + if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) { + throw TypeError(`Cannot read properties of ${obj} (reading '${prop}')`); + } + const result = obj[prop]; + if (typeof result === 'function') { + return result.bind(obj); // arrow functions aren't affected by bind. + } + return result; + }, + evalUnaryExpression(ast, subs) { + const result = { + '-': a => -SafeEval.evalAst(a, subs), + '!': a => !SafeEval.evalAst(a, subs), + '~': a => ~SafeEval.evalAst(a, subs), + // eslint-disable-next-line no-implicit-coercion -- API + '+': a => +SafeEval.evalAst(a, subs), + typeof: a => typeof SafeEval.evalAst(a, subs) + }[ast.operator](ast.argument); + return result; + }, + evalArrayExpression(ast, subs) { + return ast.elements.map(el => SafeEval.evalAst(el, subs)); + }, + evalCallExpression(ast, subs) { + const args = ast.arguments.map(arg => SafeEval.evalAst(arg, subs)); + const func = SafeEval.evalAst(ast.callee, subs); + // if (func === Function) { + // throw new Error('Function constructor is disabled'); + // } + return func(...args); + }, + evalAssignmentExpression(ast, subs) { + if (ast.left.type !== 'Identifier') { + throw SyntaxError('Invalid left-hand side in assignment'); + } + const id = ast.left.name; + const value = SafeEval.evalAst(ast.right, subs); + subs[id] = value; + return subs[id]; + } +}; + +/** + * A replacement for NodeJS' VM.Script which is also {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP | Content Security Policy} friendly. + */ +class SafeScript { + /** + * @param {string} expr Expression to evaluate + */ + constructor(expr) { + this.code = expr; + this.ast = jsep(this.code); + } + + /** + * @param {object} context Object whose items will be added + * to evaluation + * @returns {EvaluatedResult} Result of evaluated code + */ + runInNewContext(context) { + // `Object.create(null)` creates a prototypeless object + const keyMap = Object.assign(Object.create(null), context); + return SafeEval.evalAst(this.ast, keyMap); + } +} + +/* eslint-disable camelcase -- Convenient for escaping */ + + +/** + * @typedef {null|boolean|number|string|object|GenericArray} JSONObject + */ + +/** + * @typedef {any} AnyItem + */ + +/** + * @typedef {any} AnyResult + */ + +/** + * Copies array and then pushes item into it. + * @param {GenericArray} arr Array to copy and into which to push + * @param {AnyItem} 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 {AnyItem} 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 {AnyResult} 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 {object} ReturnObject +* @property {string} path +* @property {JSONObject} value +* @property {object|GenericArray} parent +* @property {string} parentProperty +*/ + +/** +* @callback JSONPathCallback +* @param {string|object} preferredOutput +* @param {"value"|"property"} type +* @param {ReturnObject} fullRetObj +* @returns {void} +*/ + +/** +* @callback OtherTypeCallback +* @param {JSONObject} val +* @param {string} path +* @param {object|GenericArray} parent +* @param {string} parentPropName +* @returns {boolean} +*/ + +/** + * @typedef {any} ContextItem + */ + +/** + * @typedef {any} EvaluatedResult + */ + +/** +* @callback EvalCallback +* @param {string} code +* @param {ContextItem} context +* @returns {EvaluatedResult} +*/ + +/** + * @typedef {typeof SafeScript} EvalClass + */ + +/** + * @typedef {object} 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 {object} [sandbox={}] + * @property {EvalCallback|EvalClass|'safe'|'native'| + * boolean} [eval = 'safe'] + * @property {object|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] + */ + +/** + * @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 -- Allow for pseudo-class + 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 = Object.hasOwn(opts, 'wrap') ? opts.wrap : true; + this.sandbox = opts.sandbox || {}; + this.eval = opts.eval === undefined ? 'safe' : opts.eval; + this.ignoreEvalErrors = typeof opts.ignoreEvalErrors === 'undefined' ? false : opts.ignoreEvalErrors; + 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.currEval = this.eval; + 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 (!Object.hasOwn(expr, 'json')) { + throw new TypeError('You must supply a "json" property when providing an object ' + 'argument to JSONPath.evaluate().'); + } + ({ + json + } = expr); + flatten = Object.hasOwn(expr, 'flatten') ? expr.flatten : flatten; + this.currResultType = Object.hasOwn(expr, 'resultType') ? expr.resultType : this.currResultType; + this.currSandbox = Object.hasOwn(expr, 'sandbox') ? expr.sandbox : this.currSandbox; + wrap = Object.hasOwn(expr, 'wrap') ? expr.wrap : wrap; + this.currEval = Object.hasOwn(expr, 'eval') ? expr.eval : this.currEval; + callback = Object.hasOwn(expr, 'callback') ? expr.callback : callback; + this.currOtherTypeCallback = Object.hasOwn(expr, 'otherTypeCallback') ? expr.otherTypeCallback : this.currOtherTypeCallback; + currParent = Object.hasOwn(expr, 'parent') ? expr.parent : currParent; + currParentProperty = Object.hasOwn(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 n/callback-return -- No need to return + callback(preferredOutput, type, fullRetObj); + } +}; + +/** + * + * @param {string} expr + * @param {JSONObject} val + * @param {string} path + * @param {object|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 && Object.hasOwn(val, loc)) { + // simple case--directly follow property + addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, hasArrExpr)); + // eslint-disable-next-line unicorn/prefer-switch -- Part of larger `if` + } else if (loc === '*') { + // all child properties + this._walk(val, m => { + addRet(this._trace(x, val[m], push(path, m), val, m, callback, 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(val, m => { + // We don't join m and x here because we only want parents, + // not scalar values + if (typeof val[m] === 'object') { + // Keep going with recursive descent on val's + // object children + addRet(this._trace(expr.slice(), val[m], push(path, m), val, m, callback, 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.currEval === false) { + throw new Error('Eval [?(expr)] prevented in JSONPath expression.'); + } + const safeLoc = loc.replace(/^\?\((.*?)\)$/u, '$1'); + // check for a nested filter expression + const nested = /@.?([^?]*)[['](\??\(.*?\))(?!.\)\])[\]']/gu.exec(safeLoc); + if (nested) { + // find if there are matches in the nested expression + // add them to the result set if there is at least one match + this._walk(val, m => { + const npath = [nested[2]]; + const nvalue = nested[1] ? val[m][nested[1]] : val[m]; + const filterResults = this._trace(npath, nvalue, path, parent, parentPropName, callback, true); + if (filterResults.length > 0) { + addRet(this._trace(x, val[m], push(path, m), val, m, callback, true)); + } + }); + } else { + this._walk(val, m => { + if (this._eval(safeLoc, val[m], m, path, parent, parentPropName)) { + addRet(this._trace(x, val[m], push(path, m), val, m, callback, true)); + } + }); + } + } else if (loc[0] === '(') { + // [(expr)] (dynamic property/index) + if (this.currEval === false) { + 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.at(-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': + 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': + 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; + /* c8 ignore next 2 */ + 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 && Object.hasOwn(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 && Object.hasOwn(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++) { + // eslint-disable-next-line @stylistic/max-len -- Long + // eslint-disable-next-line sonarjs/updated-loop-counter -- Convenient + t++; + ret.splice(t, 0, tmp[tt]); + } + } else { + ret[t] = tmp; + } + } + } + } + return ret; +}; +JSONPath.prototype._walk = function (val, f) { + if (Array.isArray(val)) { + const n = val.length; + for (let i = 0; i < n; i++) { + f(i); + } + } else if (val && typeof val === 'object') { + Object.keys(val).forEach(m => { + f(m); + }); + } +}; +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) { + this.currSandbox._$_parentProperty = parentPropName; + this.currSandbox._$_parent = parent; + this.currSandbox._$_property = _vname; + this.currSandbox._$_root = this.json; + this.currSandbox._$_v = _v; + const containsPath = code.includes('@path'); + if (containsPath) { + this.currSandbox._$_path = JSONPath.toPathString(path.concat([_vname])); + } + const scriptCacheKey = this.currEval + 'Script:' + code; + if (!JSONPath.cache[scriptCacheKey]) { + let script = code.replaceAll('@parentProperty', '_$_parentProperty').replaceAll('@parent', '_$_parent').replaceAll('@property', '_$_property').replaceAll('@root', '_$_root').replaceAll(/@([.\s)[])/gu, '_$_v$1'); + if (containsPath) { + script = script.replaceAll('@path', '_$_path'); + } + if (this.currEval === 'safe' || this.currEval === true || this.currEval === undefined) { + JSONPath.cache[scriptCacheKey] = new this.safeVm.Script(script); + } else if (this.currEval === 'native') { + JSONPath.cache[scriptCacheKey] = new this.vm.Script(script); + } else if (typeof this.currEval === 'function' && this.currEval.prototype && Object.hasOwn(this.currEval.prototype, 'runInNewContext')) { + const CurrEval = this.currEval; + JSONPath.cache[scriptCacheKey] = new CurrEval(script); + } else if (typeof this.currEval === 'function') { + JSONPath.cache[scriptCacheKey] = { + runInNewContext: context => this.currEval(script, context) + }; + } else { + throw new TypeError(`Unknown "eval" property "${this.currEval}"`); + } + } + try { + return JSONPath.cache[scriptCacheKey].runInNewContext(this.currSandbox); + } catch (e) { + if (this.ignoreEvalErrors) { + return false; + } + 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().replaceAll('~', '~0').replaceAll('/', '~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 + .replaceAll(/@(?:null|boolean|number|string|integer|undefined|nonFinite|scalar|array|object|function|other)\(\)/gu, ';$&;') + // Parenthetical evaluations (filtering and otherwise), directly + // within brackets or single quotes + .replaceAll(/[['](\??\(.*?\))[\]'](?!.\])/gu, function ($0, $1) { + return '[#' + (subx.push($1) - 1) + ']'; + }) + // Escape periods and tildes within properties + .replaceAll(/\[['"]([^'\]]*)['"]\]/gu, function ($0, prop) { + return "['" + prop.replaceAll('.', '%@%').replaceAll('~', '%%@@%%') + "']"; + }) + // Properties operator + .replaceAll('~', ';~;') + // Split by property boundaries + .replaceAll(/['"]?\.['"]?(?![^[]*\])|\[['"]?/gu, ';') + // Reinsert periods within properties + .replaceAll('%@%', '.') + // Reinsert tildes within properties + .replaceAll('%%@@%%', '~') + // Parent + .replaceAll(/(?:;)?(\^+)(?:;)?/gu, function ($0, ups) { + return ';' + ups.split('').join(';') + ';'; + }) + // Descendents + .replaceAll(/;;;|;;/gu, ';..;') + // Remove trailing + .replaceAll(/;$|'?\]|'$/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.safeVm = { + Script: SafeScript +}; + +JSONPath.prototype.vm = vm; + +exports.JSONPath = JSONPath; + + /***/ }), /***/ 67034: /***/ ((module) => { "use strict"; -module.exports = {"i8":"16.15.1"}; +module.exports = {"i8":"16.15.3"}; /***/ }),