UNPKG

@aws-lambda-powertools/jmespath

Version:

A type safe and modern jmespath module to parse and extract data from JSON documents using JMESPath

246 lines (245 loc) 9.39 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.VariadicArityError = exports.UnknownFunctionError = exports.ParseError = exports.LexerError = exports.JMESPathTypeError = exports.JMESPathError = exports.IncompleteExpressionError = exports.EmptyExpressionError = exports.ArityError = void 0; /** * Base class for errors thrown during expression parsing and evaluation. */ class JMESPathError extends Error { /** * Expression that was being parsed when the error occurred. * Can be set by whatever catches the error. */ expression; constructor(message) { super(message); this.name = 'JMESPathError'; this.message = message; } /** * Set the expression that was being parsed when the error occurred. * * The separate method allows the expression to be set after the error is * thrown. In some instances the expression is not known until after the * error is thrown (i.e. the error is thrown down the call stack). * * @param expression The expression that was being parsed when the error occurred. */ setExpression(expression) { this.expression = expression; // Set the message to include the expression. this.message = `${this.message} in expression: ${this.expression}`; } } exports.JMESPathError = JMESPathError; /** * Error thrown when an unknown token is encountered during the AST construction. */ class LexerError extends JMESPathError { /** * Position in the expression where the error occurred. */ lexerPosition; /** * Token value where the error occurred. */ lexerValue; constructor(lexerPosition, lexerValue) { super('Bad jmespath expression'); this.name = 'LexerError'; this.lexerPosition = lexerPosition; this.lexerValue = lexerValue; // Set the message to include the lexer position and value. this.message = `${this.message}: unknown token "${this.lexerValue}" at column ${this.lexerPosition}`; } } exports.LexerError = LexerError; /** * Error thrown when an invalid or unexpected token type or value is encountered during parsing. */ class ParseError extends JMESPathError { /** * Position in the expression where the error occurred. */ lexPosition; /** * Additional information about the error. */ reason; /** * Token type where the error occurred. */ tokenType; /** * Token value where the error occurred. */ tokenValue; constructor(options) { super('Invalid jmespath expression'); this.name = 'ParseError'; this.lexPosition = options.lexPosition; this.tokenValue = options.tokenValue; this.tokenType = options.tokenType; this.reason = options.reason; // Set the message to include the lexer position and token info. let issue; if (this.reason) { issue = this.reason; } else if (this.tokenType === 'eof') { issue = 'found unexpected end of expression (EOF)'; } else { issue = `found unexpected token "${this.tokenValue}" (${this.tokenType})`; } this.message = `${this.message}: parse error at column ${this.lexPosition}, ${issue}`; } } exports.ParseError = ParseError; /** * Error thrown when an incomplete expression is encountered during parsing. */ class IncompleteExpressionError extends ParseError { constructor(options) { super(options); this.name = 'IncompleteExpressionError'; } } exports.IncompleteExpressionError = IncompleteExpressionError; /** * Error thrown when an empty expression is encountered during parsing. */ class EmptyExpressionError extends JMESPathError { constructor() { super('Invalid JMESPath expression: cannot be empty.'); this.name = 'EmptyExpressionError'; } } exports.EmptyExpressionError = EmptyExpressionError; /** * Base class for errors thrown during function execution. * * When writing a JMESPath expression, you can use functions to transform the * data. For example, the `abs()` function returns the absolute value of a number. * * If an error occurs during function execution, the error is thrown as a * subclass of `FunctionError`. The subclass is determined by the type of error * that occurred. * * Errors can be thrown while validating the arguments passed to a function, or * while executing the function itself. */ class FunctionError extends JMESPathError { /** * Function that was being executed when the error occurred. * Can be set by whatever catches the error. */ functionName; constructor(message) { super(message); this.name = 'FunctionError'; } /** * Set the function that was being validated or executed when the error occurred. * * The separate method allows the name to be set after the error is * thrown. In most cases the error is thrown down the call stack, but we want * to show the actual function name used in the expression rather than an internal * alias. To avoid passing the function name down the call stack, we set it * after the error is thrown. * * @param functionName The function that was being validated or executed when the error occurred. */ setEvaluatedFunctionName(functionName) { this.message = this.message.replace('for function undefined', `for function ${functionName}()`); } } /** * Error thrown when an unexpected argument is passed to a function. * * Function arguments are validated before the function is executed. If an * invalid argument is passed, the error is thrown. For example, the `abs()` * function expects exactly one argument. If more than one argument is passed, * an `ArityError` is thrown. */ class ArityError extends FunctionError { actualArity; expectedArity; constructor(options) { super('Invalid arity for JMESPath function'); this.name = 'ArityError'; this.actualArity = options.actualArity; this.expectedArity = options.expectedArity; const arityParticle = this.actualArity > this.expectedArity ? 'at most' : 'at least'; // Set the message to include the error info. this.message = `Expected ${arityParticle} ${this.expectedArity} ${this.pluralize('argument', this.expectedArity)} for function ${this.functionName}, received ${this.actualArity}`; } pluralize(word, count) { return count === 1 ? word : `${word}s`; } } exports.ArityError = ArityError; /** * Error thrown when an unexpected number of arguments is passed to a variadic function. * * Variadic functions are functions that accept a variable number of arguments. * For example, the `max()` function accepts any number of arguments and returns * the largest one. If no arguments are passed, it returns `null`. * * If the number of arguments passed to a variadic function is not within the * expected range, this error is thrown. */ class VariadicArityError extends ArityError { constructor(options) { super(options); this.name = 'VariadicArityError'; // Set the message to include the error info. this.message = `Expected ${this.expectedArity} ${this.pluralize('argument', this.expectedArity)} for function ${this.functionName}, received ${this.actualArity}`; } } exports.VariadicArityError = VariadicArityError; /** * Error thrown when an invalid argument type is passed to a built-in function. * * Function arguments are validated before the function is executed. If an * invalid argument type is found, this error is thrown. For example, the * `abs()` function expects a number as its argument. If a string is passed * instead, this error is thrown. */ class JMESPathTypeError extends FunctionError { actualType; currentValue; expectedTypes; constructor(options) { super('Invalid type for JMESPath expression'); this.name = 'JMESPathTypeError'; this.currentValue = options.currentValue; this.actualType = options.actualType; this.expectedTypes = options.expectedTypes; // Set the message to include the error info. this.message = `Invalid argument type for function ${this.functionName}, expected ${this.serializeExpectedTypes()} but found "${this.actualType}"`; } serializeExpectedTypes() { const types = []; for (const type of this.expectedTypes) { types.push(`"${type}"`); } return types.length === 1 ? types[0] : `one of ${types.join(', ')}`; } } exports.JMESPathTypeError = JMESPathTypeError; /** * Error thrown when an unknown function is used in an expression. * * When evaluating a JMESPath expression, the interpreter looks up the function * name in a table of built-in functions, as well as any custom functions * provided by the user. If the function name is not found, this error is thrown. */ class UnknownFunctionError extends FunctionError { constructor(funcName) { super('Unknown function'); this.name = 'UnknownFunctionError'; // Set the message to include the error info. this.message = `Unknown function: ${funcName}()`; } } exports.UnknownFunctionError = UnknownFunctionError;