From 28f898a5c1fc2349851b38b63c087cec7bab168b Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 04:21:53 -0400 Subject: [PATCH 01/92] rebase: start rebasing project for typescript --- src/errors.js | 41 ----- src/evaluate.js | 66 ------- src/helpers/has.ts | 3 + src/helpers/is-primitive.ts | 7 + src/helpers/misc.js | 10 - src/helpers/potential-tokenizer.ts | 124 +++++++++++++ src/helpers/remove-whitespace.ts | 9 + src/helpers/split.js | 192 -------------------- src/helpers/to-number.ts | 9 + src/helpers/to-text.ts | 15 ++ src/helpers/token-types.js | 8 - src/helpers/unicode-safe-split.ts | 95 ++++++++++ src/helpers/wildcard-to-regexp.js | 100 ---------- src/operators/compare.js | 143 --------------- src/operators/logical.js | 34 ---- src/tokens/arguments.js | 94 ---------- src/tokens/base.js | 16 -- src/tokens/base.ts | 38 ++++ src/tokens/block-escape.js | 67 ------- src/tokens/comparison.js | 142 --------------- src/tokens/comparison/base.ts | 44 +++++ src/tokens/comparison/equal-loose.ts | 40 ++++ src/tokens/comparison/equal-strict.ts | 22 +++ src/tokens/comparison/exists.ts | 16 ++ src/tokens/comparison/greater-than-equal.ts | 29 +++ src/tokens/comparison/greater-than.ts | 29 +++ src/tokens/comparison/less-than-equal.ts | 29 +++ src/tokens/comparison/less-than.ts | 29 +++ src/tokens/comparison/numerical.ts | 51 ++++++ src/tokens/comparison/regex.ts | 37 ++++ src/tokens/comparison/wildcard.ts | 154 ++++++++++++++++ src/tokens/if.js | 159 ---------------- src/tokens/index.js | 52 ------ src/tokens/logic-operator.js | 104 ----------- src/tokens/logical/base.ts | 14 ++ src/tokens/logical/logical-and.ts | 21 +++ src/tokens/logical/logical-not.ts | 16 ++ src/tokens/logical/logical-or.ts | 21 +++ src/tokens/operator.ts | 28 +++ src/tokens/text.js | 114 ------------ src/tokens/text.ts | 7 + src/tokens/token-list.ts | 64 +++++++ src/tokens/variable.js | 103 ----------- src/types/operator-types.ts | 5 + src/types/options.ts | 13 ++ src/types/token-types.ts | 10 + 46 files changed, 979 insertions(+), 1445 deletions(-) delete mode 100644 src/errors.js delete mode 100644 src/evaluate.js create mode 100644 src/helpers/has.ts create mode 100644 src/helpers/is-primitive.ts delete mode 100644 src/helpers/misc.js create mode 100644 src/helpers/potential-tokenizer.ts create mode 100644 src/helpers/remove-whitespace.ts delete mode 100644 src/helpers/split.js create mode 100644 src/helpers/to-number.ts create mode 100644 src/helpers/to-text.ts delete mode 100644 src/helpers/token-types.js create mode 100644 src/helpers/unicode-safe-split.ts delete mode 100644 src/helpers/wildcard-to-regexp.js delete mode 100644 src/operators/compare.js delete mode 100644 src/operators/logical.js delete mode 100644 src/tokens/arguments.js delete mode 100644 src/tokens/base.js create mode 100644 src/tokens/base.ts delete mode 100644 src/tokens/block-escape.js delete mode 100644 src/tokens/comparison.js create mode 100644 src/tokens/comparison/base.ts create mode 100644 src/tokens/comparison/equal-loose.ts create mode 100644 src/tokens/comparison/equal-strict.ts create mode 100644 src/tokens/comparison/exists.ts create mode 100644 src/tokens/comparison/greater-than-equal.ts create mode 100644 src/tokens/comparison/greater-than.ts create mode 100644 src/tokens/comparison/less-than-equal.ts create mode 100644 src/tokens/comparison/less-than.ts create mode 100644 src/tokens/comparison/numerical.ts create mode 100644 src/tokens/comparison/regex.ts create mode 100644 src/tokens/comparison/wildcard.ts delete mode 100644 src/tokens/if.js delete mode 100644 src/tokens/index.js delete mode 100644 src/tokens/logic-operator.js create mode 100644 src/tokens/logical/base.ts create mode 100644 src/tokens/logical/logical-and.ts create mode 100644 src/tokens/logical/logical-not.ts create mode 100644 src/tokens/logical/logical-or.ts create mode 100644 src/tokens/operator.ts delete mode 100644 src/tokens/text.js create mode 100644 src/tokens/text.ts create mode 100644 src/tokens/token-list.ts delete mode 100644 src/tokens/variable.js create mode 100644 src/types/operator-types.ts create mode 100644 src/types/options.ts create mode 100644 src/types/token-types.ts diff --git a/src/errors.js b/src/errors.js deleted file mode 100644 index 2cbc355..0000000 --- a/src/errors.js +++ /dev/null @@ -1,41 +0,0 @@ -class ExpressionError extends Error { - constructor(message, position) { - super(message); - - Error.captureStackTrace(this, this.constructor); - this.message = message; - this.position = position || 0; - } -} -class ExpressionSyntaxError extends ExpressionError { - constructor(message, position, character) { - super(message, position); - - Error.captureStackTrace(this, this.constructor); - this.character = character; - } -} -class ExpressionVariableError extends ExpressionError { - constructor(message, position, varname) { - super(message, position); - - Error.captureStackTrace(this, this.constructor); - this.varname = varname; - } -} -class ExpressionArgumentsError extends ExpressionError { - constructor(message, position, index, varname) { - super(message, position); - - Error.captureStackTrace(this, this.constructor); - this.index = index || -1; - this.varname = varname; - } -} - -module.exports = { - ExpressionError, - ExpressionSyntaxError, - ExpressionVariableError, - ExpressionArgumentsError -} \ No newline at end of file diff --git a/src/evaluate.js b/src/evaluate.js deleted file mode 100644 index 9d6e4bd..0000000 --- a/src/evaluate.js +++ /dev/null @@ -1,66 +0,0 @@ -const { - ExpressionError, - ExpressionSyntaxError, - ExpressionVariableError, - ExpressionArgumentsError -} = require('./errors.js'); - -const tokenize = require('./tokens/index.js'); - -/** -** @param {object} options -** @param {Map} options.handlers -** @param {string} options.expression -** @param {object} options.metadata -** @param {!any} options.trigger -*/ -async function evaluate(options) { - - if (options == null) { - throw new TypeError('options not specified'); - } - - // validate handlers list - if (options.handlers == null) { - throw new TypeError('handlers list null'); - } - if (!(options.handlers instanceof Map)) { - throw new TypeError('handlers list is not a Map'); - } - - // validate options.trigger - if (options.trigger == null) { - throw new TypeError('No trigger defined in options'); - } - - // Validate expression - if (options.expression == null) { - throw new TypeError('expression not specified'); - } - if (typeof options.expression !== 'string') { - throw new TypeError('expression must be a string'); - } - - // tokenize expression - const tokens = tokenize(options.expression); - - // evaluate - const result = []; - for (let idx = 0; idx < tokens.length; idx += 1) { - let token = await tokens[idx].evaluate(options); - if (token == null) { - result.push(''); - } else { - result.push(token); - } - } - - // return result - return result.join(''); -} - -module.exports = evaluate; -module.exports.ExpressionError = ExpressionError; -module.exports.ExpressionSyntaxError = ExpressionSyntaxError; -module.exports.ExpressionVariableError = ExpressionVariableError; -module.exports.ExpressionArgumentsError = ExpressionArgumentsError; \ No newline at end of file diff --git a/src/helpers/has.ts b/src/helpers/has.ts new file mode 100644 index 0000000..d2c9df2 --- /dev/null +++ b/src/helpers/has.ts @@ -0,0 +1,3 @@ +const hasOwnProperty = Object.prototype.hasOwnProperty; + +export default (subject: any, key: string) => hasOwnProperty.call(subject, key); \ No newline at end of file diff --git a/src/helpers/is-primitive.ts b/src/helpers/is-primitive.ts new file mode 100644 index 0000000..d6a6b0b --- /dev/null +++ b/src/helpers/is-primitive.ts @@ -0,0 +1,7 @@ +export default (subject: any) : boolean => { + return ( + typeof subject === 'boolean' || + (typeof subject === 'number' && Number.isFinite(subject)) || + typeof subject === 'string' + ); +} \ No newline at end of file diff --git a/src/helpers/misc.js b/src/helpers/misc.js deleted file mode 100644 index 9eaf40a..0000000 --- a/src/helpers/misc.js +++ /dev/null @@ -1,10 +0,0 @@ -const hasOwnProperty = Object.prototype.hasOwnProperty; -module.exports.has = (subject, key) => hasOwnProperty.call(subject, key); - -module.exports.removeWhitespace = tokens => { - let result = ''; - while (tokens.length && tokens[0].value === ' ') { - result += tokens.shift().value; - } - return result; -}; \ No newline at end of file diff --git a/src/helpers/potential-tokenizer.ts b/src/helpers/potential-tokenizer.ts new file mode 100644 index 0000000..e765a12 --- /dev/null +++ b/src/helpers/potential-tokenizer.ts @@ -0,0 +1,124 @@ +import type { IBaseToken } from '../tokens/base'; + +import { + HIGH_SURROGATE_START, + HIGH_SURROGATE_END, + REGIONAL_INDICATOR_START, + REGIONAL_INDICATOR_END, + FITZPATRICK_MODIFIER_START, + FITZPATRICK_MODIFIER_END, + VARIATION_MODIFIER_START, + VARIATION_MODIFIER_END, + DIACRITICAL_MARKS_START, + DIACRITICAL_MARKS_END, + ZWJ, + GRAPHEMS, + betweenInclusive, + codePointFromSurrogatePair +} from './unicode-safe-split'; + +/** Split input string into array of potential tokens */ +export default (subject: string) : IBaseToken[] => { + + if (typeof subject !== 'string') { + throw new Error('string cannot be undefined or null') + } + + const result : IBaseToken[] = []; + let token : IBaseToken | null = null; + let idx = 0; + let inc = 0; + + while (idx < subject.length) { + const idxInc = idx + inc; + const current = subject[idxInc]; + if ( + idxInc < (subject.length - 1) && + current && + betweenInclusive(current.charCodeAt(0), HIGH_SURROGATE_START, HIGH_SURROGATE_END) + ) { + const currPair = codePointFromSurrogatePair(current + subject[idxInc + 1]); + const nextPair = codePointFromSurrogatePair(subject.substring(idxInc + 2, idxInc + 5)); + if ( + betweenInclusive(currPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) && + betweenInclusive(nextPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) + ) { + inc += 4; + } else if (betweenInclusive(nextPair, FITZPATRICK_MODIFIER_START, FITZPATRICK_MODIFIER_END)) { + inc += 4; + } else { + inc += 2; + } + } else { + inc += 1; + } + if (GRAPHEMS.has((subject[idx + inc] + '').charCodeAt(0))) { + inc += 1; + } + if (betweenInclusive((subject[idx + inc] + '').charCodeAt(0), VARIATION_MODIFIER_START, VARIATION_MODIFIER_END)) { + inc += 1; + } + if (betweenInclusive((subject[idx + inc] + '').charCodeAt(0), DIACRITICAL_MARKS_START, DIACRITICAL_MARKS_END)) { + inc += 1; + } + if ((subject[idx + inc] + '').charCodeAt(0) === ZWJ) { + inc += 1; + continue; + } + + const char = subject.substring(idx, idx + inc); + + // emoji + if (inc > 1) { + if (token == null) { + token = { + position: idx, + value: char + }; + } + token.value += char; + + // possibily a multi token + } else if ( + (char === '&' && subject[idx + 1] === '&') || + (char === '|' && subject[idx + 1] === '|') || + (char === '`' && subject[idx + 1] === '`') || + (char === '\\' && subject[idx + 1] != null) + ) { + if (token !== null) { + result.push(token); + token = null; + } + result.push({ + position: idx, + value: `${char}${subject[idx + 1]}` + }); + inc += 1; + + // possibly significant character + } else if (!/^[a-z\d]$/i.test(char)) { + if (token !== null) { + result.push(token); + token = null; + } + result.push({ + position: idx, + value: char + }); + + // non significant non emoji character + } else if (token == null) { + token = {position: idx, value: char}; + } else { + token.value += char; + } + + idx += inc; + inc = 0; + } + if (token != null) { + result.push(token); + } + + return result; +}; \ No newline at end of file diff --git a/src/helpers/remove-whitespace.ts b/src/helpers/remove-whitespace.ts new file mode 100644 index 0000000..03218f8 --- /dev/null +++ b/src/helpers/remove-whitespace.ts @@ -0,0 +1,9 @@ +import type { IBaseToken } from '../tokens/base'; + +export default (tokens: IBaseToken[]) : string => { + let result = ''; + while (tokens.length && tokens[0].value === ' ') { + result += (tokens.shift()).value; + } + return result; +}; \ No newline at end of file diff --git a/src/helpers/split.js b/src/helpers/split.js deleted file mode 100644 index 04cd4b6..0000000 --- a/src/helpers/split.js +++ /dev/null @@ -1,192 +0,0 @@ -/* -The MIT License (MIT) @ Copyright (c) 2016 Justin Sippel, Vitaly Domnikov -https://github.com/bluelovers/runes/blob/8013b6e4021a41d6b579d76b3332c87389c5f092/LICENSE -*/ -const HIGH_SURROGATE_START = 0xd800; -const HIGH_SURROGATE_END = 0xdbff; -const LOW_SURROGATE_START = 0xdc00; -const REGIONAL_INDICATOR_START = 0x1f1e6; -const REGIONAL_INDICATOR_END = 0x1f1ff; -const FITZPATRICK_MODIFIER_START = 0x1f3fb; -const FITZPATRICK_MODIFIER_END = 0x1f3ff; -const VARIATION_MODIFIER_START = 0xfe00; -const VARIATION_MODIFIER_END = 0xfe0f; -const DIACRITICAL_MARKS_START = 0x20d0; -const DIACRITICAL_MARKS_END = 0x20ff; -const ZWJ = 0x200d; -const GRAPHEMS = new Set([ - 0x0308, // ( ◌̈ ) COMBINING DIAERESIS - 0x0937, // ( ष ) DEVANAGARI LETTER SSA - 0x093F, // ( ि ) DEVANAGARI VOWEL SIGN I - 0x0BA8, // ( ந ) TAMIL LETTER NA - 0x0BBF, // ( ி ) TAMIL VOWEL SIGN I - 0x0BCD, // ( ◌்) TAMIL SIGN VIRAMA - 0x0E31, // ( ◌ั ) THAI CHARACTER MAI HAN-AKAT - 0x0E33, // ( ำ ) THAI CHARACTER SARA AM - 0x0E40, // ( เ ) THAI CHARACTER SARA E - 0x0E49, // ( เ ) THAI CHARACTER MAI THO - 0x1100, // ( ᄀ ) HANGUL CHOSEONG KIYEOK - 0x1161, // ( ᅡ ) HANGUL JUNGSEONG A - 0x11A8 // ( ᆨ ) HANGUL JONGSEONG KIYEOK -]); - -const betweenInclusive = (value, lower, upper) => { - return (value >= lower && value <= upper); -}; - -const codePointFromSurrogatePair = pair => { - const highOffset = pair.charCodeAt(0) - HIGH_SURROGATE_START; - const lowOffset = pair.charCodeAt(1) - LOW_SURROGATE_START; - return (highOffset << 10) + lowOffset + 0x10000; -}; - -const isSignificant = char => ( - char === '"' || - char === '\\' || - char === '$' || - char === '[' || - char === ',' || - char === ']' || - char === ' ' -); - -// Unicode-safe splitting -module.exports.split = string => { - if (typeof string !== 'string') { - throw new Error('string cannot be undefined or null') - } - const result = []; - let idx = 0; - let inc = 0; - while (idx < string.length) { - const idxInc = idx + inc; - const current = string[idxInc]; - if ( - idxInc < (string.length - 1) && - current && - betweenInclusive(current.charCodeAt(0), HIGH_SURROGATE_START, HIGH_SURROGATE_END) - ) { - const currPair = codePointFromSurrogatePair(current + string[idxInc + 1]); - const nextPair = codePointFromSurrogatePair(string.substring(idxInc + 2, idxInc + 5)); - if ( - betweenInclusive(currPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) && - betweenInclusive(nextPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) - ) { - inc += 4; - } else if (betweenInclusive(nextPair, FITZPATRICK_MODIFIER_START, FITZPATRICK_MODIFIER_END)) { - inc += 4; - } else { - inc += 2; - } - } else { - inc += 1; - } - if (GRAPHEMS.has((string[idx + inc] + '').charCodeAt(0))) { - inc += 1; - } - if (betweenInclusive((string[idx + inc] + '').charCodeAt(0), VARIATION_MODIFIER_START, VARIATION_MODIFIER_END)) { - inc += 1; - } - if (betweenInclusive((string[idx + inc] + '').charCodeAt(0), DIACRITICAL_MARKS_START, DIACRITICAL_MARKS_END)) { - inc += 1; - } - if ((string[idx + inc] + '').charCodeAt(0) === ZWJ) { - inc += 1; - continue; - } - result.push(string.substring(idx, idx + inc)); - idx += inc; - inc = 0; - } - return result; -}; - -// Unicode safe tokenizer -module.exports.tokenize = input => { - - if (typeof input !== 'string') { - throw new Error('string cannot be undefined or null') - } - - const result = []; - let idx = 0; - let inc = 0; - let tok = null; - while (idx < input.length) { - const idxInc = idx + inc; - const current = input[idxInc]; - if ( - idxInc < (input.length - 1) && - current && - betweenInclusive(current.charCodeAt(0), HIGH_SURROGATE_START, HIGH_SURROGATE_END) - ) { - const currPair = codePointFromSurrogatePair(current + input[idxInc + 1]); - const nextPair = codePointFromSurrogatePair(input.substring(idxInc + 2, idxInc + 5)); - if ( - betweenInclusive(currPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) && - betweenInclusive(nextPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) - ) { - inc += 4; - } else if (betweenInclusive(nextPair, FITZPATRICK_MODIFIER_START, FITZPATRICK_MODIFIER_END)) { - inc += 4; - } else { - inc += 2; - } - } else { - inc += 1; - } - if (GRAPHEMS.has((input[idx + inc] + '').charCodeAt(0))) { - inc += 1; - } - if (betweenInclusive((input[idx + inc] + '').charCodeAt(0), VARIATION_MODIFIER_START, VARIATION_MODIFIER_END)) { - inc += 1; - } - if (betweenInclusive((input[idx + inc] + '').charCodeAt(0), DIACRITICAL_MARKS_START, DIACRITICAL_MARKS_END)) { - inc += 1; - } - if ((input[idx + inc] + '').charCodeAt(0) === ZWJ) { - inc += 1; - continue; - } - - // Emoji - if (inc > 1) { - if (tok == null) { - tok = {position: idx, value: ''}; - } - tok.value += input.substring(idx, idx + inc); - - // `` - } else if (input[idx] === '`' && input[idx + 1] === '`') { - if (tok != null) { - result.push(tok); - tok = null; - } - - result.push({position: idx, value: '``'}); - idx += 1; - - // Significant Characters - } else if (isSignificant(input[idx])) { - if (tok != null) { - result.push(tok); - tok = null; - } - result.push({position: idx, value: input[idx] === '\n' ? ' ' : input[idx]}); - - // Non-emoji, Non-significant characters - } else if (tok == null) { - tok = {position: idx, value: input[idx]}; - } else { - tok.value += input[idx]; - } - - idx += inc; - inc = 0; - } - if (tok != null) { - result.push(tok); - } - - return result; -}; \ No newline at end of file diff --git a/src/helpers/to-number.ts b/src/helpers/to-number.ts new file mode 100644 index 0000000..54b261e --- /dev/null +++ b/src/helpers/to-number.ts @@ -0,0 +1,9 @@ +export default (subject: any) : null | number => { + if (subject != null && subject !== '') { + subject = Number(subject); + if (Number.isFinite(subject)) { + return subject; + } + } + return null; +} \ No newline at end of file diff --git a/src/helpers/to-text.ts b/src/helpers/to-text.ts new file mode 100644 index 0000000..abc3f9d --- /dev/null +++ b/src/helpers/to-text.ts @@ -0,0 +1,15 @@ +import isPrimitive from "./is-primitive"; + +export default (subject: any) : string | void => { + if (subject != null) { + + if (isPrimitive(subject)) { + return String(subject); + } + + subject = JSON.stringify(subject); + if (subject != null) { + return subject; + } + } +}; \ No newline at end of file diff --git a/src/helpers/token-types.js b/src/helpers/token-types.js deleted file mode 100644 index 679f48f..0000000 --- a/src/helpers/token-types.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - TEXT: 'TEXT', - VARIABLE: 'VARIABLE', - IF: 'IF', - LOGICAL: 'LOGICAL', - CONDITION: 'CONDITION', - UNKNOWN: 'UNKNOWN' -}; diff --git a/src/helpers/unicode-safe-split.ts b/src/helpers/unicode-safe-split.ts new file mode 100644 index 0000000..2799330 --- /dev/null +++ b/src/helpers/unicode-safe-split.ts @@ -0,0 +1,95 @@ +/* +The MIT License (MIT) @ Copyright (c) 2016 Justin Sippel, Vitaly Domnikov +https://github.com/bluelovers/runes/blob/8013b6e4021a41d6b579d76b3332c87389c5f092/LICENSE +*/ +export const HIGH_SURROGATE_START = 0xd800; +export const HIGH_SURROGATE_END = 0xdbff; +export const LOW_SURROGATE_START = 0xdc00; +export const REGIONAL_INDICATOR_START = 0x1f1e6; +export const REGIONAL_INDICATOR_END = 0x1f1ff; +export const FITZPATRICK_MODIFIER_START = 0x1f3fb; +export const FITZPATRICK_MODIFIER_END = 0x1f3ff; +export const VARIATION_MODIFIER_START = 0xfe00; +export const VARIATION_MODIFIER_END = 0xfe0f; +export const DIACRITICAL_MARKS_START = 0x20d0; +export const DIACRITICAL_MARKS_END = 0x20ff; +export const ZWJ = 0x200d; +export const GRAPHEMS = new Set([ + 0x0308, // ( ◌̈ ) COMBINING DIAERESIS + 0x0937, // ( ष ) DEVANAGARI LETTER SSA + 0x093F, // ( ि ) DEVANAGARI VOWEL SIGN I + 0x0BA8, // ( ந ) TAMIL LETTER NA + 0x0BBF, // ( ி ) TAMIL VOWEL SIGN I + 0x0BCD, // ( ◌்) TAMIL SIGN VIRAMA + 0x0E31, // ( ◌ั ) THAI CHARACTER MAI HAN-AKAT + 0x0E33, // ( ำ ) THAI CHARACTER SARA AM + 0x0E40, // ( เ ) THAI CHARACTER SARA E + 0x0E49, // ( เ ) THAI CHARACTER MAI THO + 0x1100, // ( ᄀ ) HANGUL CHOSEONG KIYEOK + 0x1161, // ( ᅡ ) HANGUL JUNGSEONG A + 0x11A8 // ( ᆨ ) HANGUL JONGSEONG KIYEOK +]); + +export const betweenInclusive = (value: number, lower: number, upper: number) : boolean => { + return (value >= lower && value <= upper); +}; + +export const codePointFromSurrogatePair = (pair: string) : number => { + const highOffset = pair.charCodeAt(0) - HIGH_SURROGATE_START; + const lowOffset = pair.charCodeAt(1) - LOW_SURROGATE_START; + return (highOffset << 10) + lowOffset + 0x10000; +}; + +/** Splits input text into an array of characters */ +export default (subject : string) : string[] => { + + if (typeof subject !== 'string') { + throw new Error('string cannot be undefined or null') + } + + const result : string[] = []; + let idx = 0; + let inc = 0; + while (idx < subject.length) { + const idxInc = idx + inc; + const current = subject[idxInc]; + if ( + idxInc < (subject.length - 1) && + current && + betweenInclusive(current.charCodeAt(0), HIGH_SURROGATE_START, HIGH_SURROGATE_END) + ) { + const currPair = codePointFromSurrogatePair(current + subject[idxInc + 1]); + const nextPair = codePointFromSurrogatePair(subject.substring(idxInc + 2, idxInc + 5)); + if ( + betweenInclusive(currPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) && + betweenInclusive(nextPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) + ) { + inc += 4; + } else if (betweenInclusive(nextPair, FITZPATRICK_MODIFIER_START, FITZPATRICK_MODIFIER_END)) { + inc += 4; + } else { + inc += 2; + } + } else { + inc += 1; + } + if (GRAPHEMS.has((subject[idx + inc] + '').charCodeAt(0))) { + inc += 1; + } + if (betweenInclusive((subject[idx + inc] + '').charCodeAt(0), VARIATION_MODIFIER_START, VARIATION_MODIFIER_END)) { + inc += 1; + } + if (betweenInclusive((subject[idx + inc] + '').charCodeAt(0), DIACRITICAL_MARKS_START, DIACRITICAL_MARKS_END)) { + inc += 1; + } + if ((subject[idx + inc] + '').charCodeAt(0) === ZWJ) { + inc += 1; + continue; + } + result.push(subject.substring(idx, idx + inc)); + idx += inc; + inc = 0; + } + + return result; +}; \ No newline at end of file diff --git a/src/helpers/wildcard-to-regexp.js b/src/helpers/wildcard-to-regexp.js deleted file mode 100644 index ca3ac2b..0000000 --- a/src/helpers/wildcard-to-regexp.js +++ /dev/null @@ -1,100 +0,0 @@ -const { split } = require('./split.js'); - -module.exports = (input, caseSensitive) => { - let wc = split(input); - - let isStart = true; - let startAnchor = true; - let endAnchor = true; - let tokenQCount = 0; - let tokenACount = 0; - let pattern = ''; - let idx = 0; - let processTokens = false; - while (idx < wc.length) { - - let char = wc[idx]; - - // Char needs to be escaped - if ( - char === '^' || - char === '.' || - char === '-' || - char === '+' || - char === '\\' || - char === '/' || - char === '|' || - char === '(' || - char === ')' || - char === '[' || - char === ']' || - char === '{' || - char === '}' || - char === '$' - ) { - char = `\\${char}`; - processTokens = true; - - // Wildcard characters - } else if (char === '*') { - tokenACount += 1; - char = null; - - } else if (char === '?') { - tokenQCount += 1; - char = null; - - // Non-special character - } else { - processTokens = true; - } - - const isEnd = (idx + 1) === wc.length; - - if (processTokens || (isEnd && (tokenQCount || tokenACount))) { - let useAstericks = false; - - if (tokenACount) { - if (isStart) { - startAnchor = false; - } else if (isEnd) { - endAnchor = false; - } else { - useAstericks = true; - } - } - - if (useAstericks) { - if (tokenQCount) { - pattern = pattern + '.'.repeat(tokenQCount) + '+'; - } else { - pattern = pattern + '.*'; - } - } else if (tokenQCount) { - pattern = pattern + '.'.repeat(tokenQCount); - } - - if (char != null) { - pattern += char; - } - - tokenQCount = 0; - tokenACount = 0; - isStart = false; - processTokens = false; - } else if (char) { - pattern += char; - } - - idx += 1; - } - - if (startAnchor) { - pattern = '^' + pattern; - } - if (endAnchor) { - pattern = pattern + '$'; - } - let res = new RegExp(pattern, caseSensitive ? '' : 'i'); - return res; -}; \ No newline at end of file diff --git a/src/operators/compare.js b/src/operators/compare.js deleted file mode 100644 index 2be30dc..0000000 --- a/src/operators/compare.js +++ /dev/null @@ -1,143 +0,0 @@ -const wildcardToRegExp = require('../helpers/wildcard-to-regexp.js'); - -const isRange = /^((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))-((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))$/; - -const toNumber = (v1, v2) => { - if (v1 === '' || v2 === '') { - return [v1, v2]; - } - - const v1Num = Number(v1); - if (Number.isNaN(v1Num)) { - return [v1, v2]; - } - - const v2Num = Number(v2); - if (Number.isNaN(v2Num)) { - return [v1, v2]; - } - return [v1Num, v2Num]; -} - -const isStrictEqual = (v1, v2) => { - return v1 === v2; -}; - -const isLooseEqual = (v1, v2) => { - if (v1 === v2) { - return true; - } - if (v1.toLowerCase() === v2.toLowerCase()) { - return true; - } - const [v1Num, v2Num] = toNumber(v1, v2); - return v1Num === v2Num; -}; - -const exists = (v1) => { - return v1 != null && v1 !== ''; -}; - -const isNumber = (v1, v2) => { - if (v1 === '') { - return false; - } - v1 = Number(v1); - - if (Number.isNaN(v1)) { - return false; - } - if (v2 == null || v2 === '') { - return true; - } - const range = isRange.exec(v2); - - if (!range) { - return false; - } - - let r1 = Number(range[1]), - r2 = Number(range[2]); - - if (r1 > r2) { - return (r2 <= v1 && v1 <= r1); - } - return (r1 <= v1 && v1 <= r2); -}; - -const isRegexMatch = (v1, v2) => { - const parts = /^\/(.*)\/([a-z]*)$/i.exec(v2); - if (parts) { - return (new RegExp(parts[1], parts[2])).test(v1); - } - return (new RegExp(v2)).test(v1); -}; - -const isWildcardMatch = (v1, v2) => { - if (v2 === null || v2 == '') { - return false; - } - return wildcardToRegExp(v2, false).test(v1); -} - -const isWildcardMatchCaseSensitive = (v1, v2) => { - if (v2 === null || v2 == '') { - return false; - } - return wildcardToRegExp(v2, true).test(v1); -}; - -module.exports = new Map([ - ['===', isStrictEqual], - ['!==', (...args) => !(isStrictEqual(...args))], - - ['==', isLooseEqual], - ['!=', (...args) => !(isLooseEqual(...args))], - - ['<', (v1, v2) => { - [v1, v2] = toNumber(v1, v2); - if (typeof v1 !== 'number' || typeof v2 !== 'number') { - return false; - } - return v1 < v2; - }], - ['<=', (v1, v2) => { - [v1, v2] = toNumber(v1, v2); - if (typeof v1 !== 'number' || typeof v2 !== 'number') { - return false; - } - return v1 <= v2; - }], - ['>', (v1, v2) => { - [v1, v2] = toNumber(v1, v2); - if (typeof v1 !== 'number' || typeof v2 !== 'number') { - return false; - } - return v1 > v2; - }], - ['>=', (v1, v2) => { - [v1, v2] = toNumber(v1, v2); - if (typeof v1 !== 'number' || typeof v2 !== 'number') { - return false; - } - return v1 >= v2; - }], - - ['exists', exists], - ['!exists', (...args) => !(exists(...args))], - - ['isnumber', isNumber], - ['isnum', isNumber], - - ['!isnumber', (...args) => !(isNumber(...args))], - ['!isnum', (...args) => !(isNumber(...args))], - - ['regex', isRegexMatch], - ['!regex', (...args) => !(isRegexMatch(...args))], - - ['iswcm', isWildcardMatch], - ['!iswcm', (...args) => !(isWildcardMatch(...args))], - - ['iswcmcs', isWildcardMatchCaseSensitive], - ['!iswcmcs', (...args) => !(isWildcardMatchCaseSensitive(...args))] -]); \ No newline at end of file diff --git a/src/operators/logical.js b/src/operators/logical.js deleted file mode 100644 index 36057c4..0000000 --- a/src/operators/logical.js +++ /dev/null @@ -1,34 +0,0 @@ -const $not = arg => (arg == null || arg === false || arg === ''); - -const $and = (...args) => { - if (!args || !args.length) { - return false; - } - return !args.some(item => $not(item)); -}; - -const $or = (...args) => { - if (!args || !args.length) { - return false; - } - return args.some(item => (item != null && item !== false && item !== '')); -} - -const $nand = (...args) => $not($and(...args)); -const $nor = (...args) => $not($or(...args)); - -module.exports = new Map([ - ['$NOT', $not], - - ['$AND', $and], - ['$ALL', $and], - - ['$OR', $or], - ['$ANY', $or], - - ['$NAND', $nand], - ['$NALL', $nand], - - ['$NOR', $nor], - ['$NANY', $nor] -]); \ No newline at end of file diff --git a/src/tokens/arguments.js b/src/tokens/arguments.js deleted file mode 100644 index 8f14e5d..0000000 --- a/src/tokens/arguments.js +++ /dev/null @@ -1,94 +0,0 @@ -const types = require('../helpers/token-types.js'); - -const { removeWhitespace } = require('../helpers/misc.js'); - - -const { ExpressionSyntaxError } = require('../errors.js'); -const { tokenizeEscape, tokenizeQuote, TextToken } = require('./text.js'); -const blockEscapeHandler = require('./block-escape.js'); - -const ifHandler = require('./if.js'); -const varHandler = require('./variable.js'); - -// tokenizeArguments(); -module.exports.tokenize = (output, tokens) => { - if (!tokens.length || tokens[0].value !== '[') { - return false; - } - - const openToken = tokens.shift(); - removeWhitespace(tokens); - - let parts = []; - while (tokens.length) { - let position = tokens[0].position, - whitespace = removeWhitespace(tokens); - - // End of arguments list - if (tokens[0].value === ']') { - tokens.shift(); - if (!parts.length) { - parts = [new TextToken({position, value: ''})] - } - output.push(parts); - return true; - } - - // End of argument - if (tokens[0].value === ',') { - tokens.shift(); - removeWhitespace(tokens); - if (!parts.length) { - parts = [new TextToken({position, value: ''})]; - } - output.push(parts); - parts = []; - continue; - } - - if (blockEscapeHandler.tokenize(parts, tokens)) { - continue; - } - - // Add whitespace to argument part's list - if (whitespace) { - if (parts.length && parts[parts.length - 1].type === types.TEXT) { - parts[parts.length - 1].value += whitespace; - - } else { - parts.push(new TextToken({position, value: whitespace})); - } - } - - // Consume tokens - if (tokenizeEscape(parts, tokens, '"$,\\]')) { - continue; - } - - if (tokenizeQuote(parts, tokens)) { - continue; - } - - if (ifHandler.tokenize(parts, tokens)) { - continue; - } - - if (varHandler.tokenize(parts, tokens)) { - continue; - } - - // Consume all other tokens as text - const token = tokens.shift(); - if (parts.length && parts[parts.length - 1].type === types.TEXT) { - parts[parts.length - 1].value += token.value; - } else { - parts.push(new TextToken(token)); - } - } - - - if (!tokens.length) { - throw new ExpressionSyntaxError('Unexpected end of expression', openToken.position); - } - throw new ExpressionSyntaxError('expected end of variable arguments', openToken.position); -}; \ No newline at end of file diff --git a/src/tokens/base.js b/src/tokens/base.js deleted file mode 100644 index c738fae..0000000 --- a/src/tokens/base.js +++ /dev/null @@ -1,16 +0,0 @@ -const types = require('../helpers/token-types.js'); - -module.exports = class Token { - - constructor({type, position, value}) { - this.type = type || types.UNKNOWN; - this.position = position == null ? -1 : position; - if (value != null) { - this.value = value; - } - } - - evaluate() { - return this.value == null ? '' : this.value; - } -} \ No newline at end of file diff --git a/src/tokens/base.ts b/src/tokens/base.ts new file mode 100644 index 0000000..aaaa93f --- /dev/null +++ b/src/tokens/base.ts @@ -0,0 +1,38 @@ +import { TokenType } from '../types/token-types'; + +import toText from '../helpers/to-text'; + +export interface IToken { + type?: TokenType; + position: number; + value: any; +} + +export default class Token { + + protected type: TokenType; + protected position : number; + protected value : any; + + constructor(token: IToken) { + this.type = token.type == null ? TokenType.UNKNOWN : token.type; + this.position = token.position != null ? token.position : -1; + this.value = token.value; + } + + async evaluate(options: any, meta?: any) : Promise { + return this.value == null ? '' : this.value; + } + + toString() : string { + return JSON.stringify(this.toToken()); + } + + toToken() : object { + return { + type: this.type, + position: this.position, + value: this.value + }; + } +} \ No newline at end of file diff --git a/src/tokens/block-escape.js b/src/tokens/block-escape.js deleted file mode 100644 index 17176bf..0000000 --- a/src/tokens/block-escape.js +++ /dev/null @@ -1,67 +0,0 @@ -const types = require('../helpers/token-types.js'); - -const { TextToken } = require('./text.js'); - -const ifHandler = require('./if.js'); -const variableHandler = require('./variable.js'); - -/** - * - * @param {Array} output - * @param {string[]} tokens - * @returns - */ -const tokenize = (output, tokens) => { - if ( - output.length > 0 || - !tokens.length || - tokens[0].value !== '``' - ) { - return false; - } - - // copy tokens list, remove opening `` - let tokensCopy = tokens.slice(1); - // tokensCopy.shift(); - - let result = []; - while (tokensCopy.length) { - if (tokensCopy[0].value === '``') { - break; - } - - if (ifHandler.tokenize(result, tokensCopy)) { - continue; - } - if (variableHandler.tokenize(result, tokensCopy)) { - continue; - } - - if (result.length && result[result.length - 1].type === types.TEXT) { - result[result.length - 1].value += tokensCopy.shift().value; - } else { - tokensCopy[0].type = types.TEXT; - result.push(new TextToken(tokensCopy.shift())) - } - } - - // Remove closing `` - if (!tokensCopy.length || tokensCopy[0].value !== '``') { - return false; - } - tokensCopy.shift(); - - // Escape block not the only token in the argument - while (tokensCopy.length && tokensCopy[0].value === ' ') { - tokensCopy.shift(); - } - if (!tokensCopy.length || (tokensCopy[0].value !== ',' && tokensCopy[0].value !== ']')) { - return false; - } - - tokens.splice(0, tokens.length - tokensCopy.length); - output.push(...result); - return true; -}; - -module.exports.tokenize = tokenize; \ No newline at end of file diff --git a/src/tokens/comparison.js b/src/tokens/comparison.js deleted file mode 100644 index 91a392c..0000000 --- a/src/tokens/comparison.js +++ /dev/null @@ -1,142 +0,0 @@ -const types = require('../helpers/token-types.js'); - -const { removeWhitespace } = require('../helpers/misc.js'); -const operators = require('../operators/compare.js'); - -const BaseToken = require('./base.js'); -const variableHandler = require('./variable.js'); -const ifHandler = require('./if.js'); -const { - TextToken, - tokenizeEscape, - tokenizeQuote -} = require('./text.js'); - -class ComparisonToken extends BaseToken { - - constructor(options) { - super({ - ...options, - type: types.CONDITION - }); - this.arguments = options.arguments; - } - - async evaluate(options) { - if (!this.value) { - this.value = 'exists'; - } - const operator = operators.get(this.value); - - if (operator == null) { - return false; - } - - let args = []; - if (this.arguments && this.arguments.length) { - for (let idx = 0; idx < this.arguments.length; idx += 1) { - let accumulator = ''; - const parts = this.arguments[idx]; - for (let partsIdx = 0; partsIdx < parts.length; partsIdx += 1) { - let res = await parts[partsIdx].evaluate(options); - if (res != null) { - accumulator += res; - } - } - args.push(accumulator); - } - } - - if (options.onlyValidate) { - return false; - } - - return operator(...args); - } -} - -// tokenizeComparison() -module.exports.tokenize = (tokens) => { - - // nothing to consume - if (!tokens.length || tokens[0].value === ',' || tokens[0].value === ']') { - return; - } - - const leadingWs = removeWhitespace(tokens), - position = tokens[0].position, - left = [], - right = []; - - let value; - while (tokens.length) { - const pos = tokens[0].position, - ws = removeWhitespace(tokens); - - // end of condition block - if (!tokens.length || tokens[0].value === ',' || tokens[0].value === ']') { - break; - } - - // consume operator: must be prefixed with whitespace and suffixed with whitespace or end-of-conditional - if ( - value == null && - (leadingWs || ws) && - operators.has(tokens[0].value) && - ( - !tokens[1] || - tokens[1].value === ' ' || - tokens[1].value === ',' || - tokens[1].value === ']' - ) - ) { - value = tokens[0].value; - tokens.shift(); - removeWhitespace(tokens); - continue; - } - - const side = value == null ? left : right; - - // Add whitespace to side token array - if (ws) { - if (side.length && side[side.length - 1].type === types.TEXT) { - side[side.length - 1].value += ws; - } else { - side.push(new TextToken({value: ws, position: pos})); - } - } - - if (tokenizeEscape(side, tokens)) { - continue; - } - - if (tokenizeQuote(side, tokens)) { - continue; - } - - if (ifHandler.tokenize(side, tokens)) { - continue; - } - - if (variableHandler.tokenize(side, tokens)) { - continue; - } - - // Treat all other tokens as plain text - const token = tokens.shift(); - if (side.length && side[side.length - 1].type === types.TEXT) { - side[side.length - 1].value += token.value; - } else { - side.push(new TextToken(token)); - } - } - - return new ComparisonToken({ - position, - value, - arguments: [left, right] - }); -}; - -module.exports.ComparisonToken = ComparisonToken; \ No newline at end of file diff --git a/src/tokens/comparison/base.ts b/src/tokens/comparison/base.ts new file mode 100644 index 0000000..bd1a505 --- /dev/null +++ b/src/tokens/comparison/base.ts @@ -0,0 +1,44 @@ +import { ParserOptions } from '../../types/options'; +import { TokenType } from '../../types/token-types'; + +import { default as OperatorToken, IOperatorToken } from '../operator'; + +export interface IComparisonToken extends IOperatorToken { + inverted?: boolean; +} + +export default class ComparisonToken extends OperatorToken { + protected inverted : boolean; + + constructor(token: IComparisonToken) { + super({ + type: TokenType.COMPARISON, + ...token + }); + + this.inverted = !!token.inverted; + } + + async handle(options: ParserOptions, meta?: any) : Promise { + return false; + } + + async handleInverse(options: ParserOptions, meta?: any) : Promise { + const result = await this.handle(options, meta); + return !result; + } + + async evaluate(options: ParserOptions, meta?: any) : Promise { + if (this.inverted) { + return this.handleInverse(options, meta); + } + return this.handle(options, meta); + } + + toToken() : object { + return { + ...(super.toToken()), + inverted: this.inverted + } + } +} \ No newline at end of file diff --git a/src/tokens/comparison/equal-loose.ts b/src/tokens/comparison/equal-loose.ts new file mode 100644 index 0000000..5d28134 --- /dev/null +++ b/src/tokens/comparison/equal-loose.ts @@ -0,0 +1,40 @@ +import { ParserOptions } from '../../types/options'; +import { default as ComparisonToken, IComparisonToken } from './base'; +import toNumber from '../../helpers/to-number'; +import isPrimitive from '../../helpers/is-primitive'; + +export default class EqualLooseToken extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'equal-loose' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + const v1 = await this.arguments[0].evaluate(options, meta); + const v2 = await this.arguments[1].evaluate(options, meta); + + if ( + v1 === v2 || + (v1 == null && v2 == null) || + (Number.isNaN(v1) && Number.isNaN(v2)) + ) { + return true; + } + + if (isPrimitive(v1) && isPrimitive(v2)) { + if (String(v1).toLowerCase() === String(v2).toLowerCase()) { + return true; + } + + const v1Num = toNumber(v1); + const v2Num = toNumber(v2); + + if (v1Num != null && v2Num != null) { + return v1Num === v2Num; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/tokens/comparison/equal-strict.ts b/src/tokens/comparison/equal-strict.ts new file mode 100644 index 0000000..758a4d4 --- /dev/null +++ b/src/tokens/comparison/equal-strict.ts @@ -0,0 +1,22 @@ +import { ParserOptions } from '../../types/options'; +import { default as ComparisonToken, IComparisonToken } from './base'; + +export default class EqualStrictToken extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'equal' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + const v1 = await this.arguments[0].evaluate(options, meta); + const v2 = await this.arguments[1].evaluate(options, meta); + + return ( + v1 === v2 || + (v1 == null && v2 == null) || + (Number.isNaN(v1) && Number.isNaN(v2)) + ); + } +} \ No newline at end of file diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts new file mode 100644 index 0000000..c5d8d07 --- /dev/null +++ b/src/tokens/comparison/exists.ts @@ -0,0 +1,16 @@ +import { ParserOptions } from '../../types/options'; +import { default as ComparisonToken, IComparisonToken } from './base'; + +export default class ExistsToken extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'exists' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.arguments[0].evaluate(options, meta); + return v1 != null && v1 !== ''; + } +} \ No newline at end of file diff --git a/src/tokens/comparison/greater-than-equal.ts b/src/tokens/comparison/greater-than-equal.ts new file mode 100644 index 0000000..f7bc8b1 --- /dev/null +++ b/src/tokens/comparison/greater-than-equal.ts @@ -0,0 +1,29 @@ +import { ParserOptions } from '../../types/options'; +import { default as ComparisonToken, IComparisonToken } from './base'; +import toNumber from '../../helpers/to-number'; + +export default class GreaterThanEqualToken extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'greater-than-or-equal' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.arguments[0].evaluate(options, meta); + let v2 = await this.arguments[1].evaluate(options, meta); + + v1 = toNumber(v1); + if (v1 == null) { + return false; + } + + v2 = toNumber(v2); + if (v2 == null) { + return false; + } + + return v1 >= v2; + } +} \ No newline at end of file diff --git a/src/tokens/comparison/greater-than.ts b/src/tokens/comparison/greater-than.ts new file mode 100644 index 0000000..d230fa1 --- /dev/null +++ b/src/tokens/comparison/greater-than.ts @@ -0,0 +1,29 @@ +import { ParserOptions } from '../../types/options'; +import { default as ComparisonToken, IComparisonToken } from './base'; +import toNumber from '../../helpers/to-number'; + +export default class GreaterThanToken extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'greater-than' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.arguments[0].evaluate(options, meta); + let v2 = await this.arguments[1].evaluate(options, meta); + + v1 = toNumber(v1); + if (v1 == null) { + return false; + } + + v2 = toNumber(v2); + if (v2 == null) { + return false; + } + + return v1 > v2; + } +} \ No newline at end of file diff --git a/src/tokens/comparison/less-than-equal.ts b/src/tokens/comparison/less-than-equal.ts new file mode 100644 index 0000000..db7e4cf --- /dev/null +++ b/src/tokens/comparison/less-than-equal.ts @@ -0,0 +1,29 @@ +import { ParserOptions } from '../../types/options'; +import { default as ComparisonToken, IComparisonToken } from './base'; +import toNumber from '../../helpers/to-number'; + +export default class LessThanEqualToken extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'less-than-or-equal' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.arguments[0].evaluate(options, meta); + let v2 = await this.arguments[1].evaluate(options, meta); + + v1 = toNumber(v1); + if (v1 == null) { + return false; + } + + v2 = toNumber(v2); + if (v2 == null) { + return false; + } + + return v1 <= v2; + } +} \ No newline at end of file diff --git a/src/tokens/comparison/less-than.ts b/src/tokens/comparison/less-than.ts new file mode 100644 index 0000000..a86464f --- /dev/null +++ b/src/tokens/comparison/less-than.ts @@ -0,0 +1,29 @@ +import { ParserOptions } from '../../types/options'; +import { default as ComparisonToken, IComparisonToken } from './base'; +import toNumber from '../../helpers/to-number'; + +export default class LessThanToken extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'less-than' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.arguments[0].evaluate(options, meta); + let v2 = await this.arguments[1].evaluate(options, meta); + + v1 = toNumber(v1); + if (v1 == null) { + return false; + } + + v2 = toNumber(v2); + if (v2 == null) { + return false; + } + + return v1 < v2; + } +} \ No newline at end of file diff --git a/src/tokens/comparison/numerical.ts b/src/tokens/comparison/numerical.ts new file mode 100644 index 0000000..efa272e --- /dev/null +++ b/src/tokens/comparison/numerical.ts @@ -0,0 +1,51 @@ +import { ParserOptions } from '../../types/options'; +import { default as ComparisonToken, IComparisonToken } from './base'; +import toNumber from '../../helpers/to-number'; + +const isRange = /^((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))-((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))$/; + +export default class LessThanToken extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'numerical' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.arguments[0].evaluate(options, meta); + v1 = toNumber(v1); + + if (v1 == null) { + return false; + } + + if (this.arguments.length == 1) { + return true; + } + + let v2 = await this.arguments[1].evaluate(options, meta); + + if (v2 == null || v2 === '') { + return true; + } + + if (typeof v2 != 'string') { + return v1 === v2; + } + + const range = isRange.exec(v2); + if (!range) { + return false; + } + + const r1 = Number(range[1]); + const r2 = Number(range[2]); + + if (r1 > r2) { + return r2 <= v1 && v1 <= r1; + } + + return r1 <= v1 && v1 <= v2; + } +} \ No newline at end of file diff --git a/src/tokens/comparison/regex.ts b/src/tokens/comparison/regex.ts new file mode 100644 index 0000000..7dac271 --- /dev/null +++ b/src/tokens/comparison/regex.ts @@ -0,0 +1,37 @@ +import { ParserOptions } from '../../types/options'; +import { default as ComparisonToken, IComparisonToken } from './base'; +import toText from '../../helpers/to-text'; + +export default class LessThanToken extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'regex' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.arguments[0].evaluate(options, meta); + let v2 = await this.arguments[1].evaluate(options, meta); + + if (v1 == null || typeof v2 !== 'string') { + return false; + } + + v1 = toText(v1); + if (v1 == null) { + return false; + } + + v2 = toText(v2); + if (v2 == null) { + return false; + } + + const parts = /^\/(.*)\/([a-z]*)$/i.exec(v2); + if (parts) { + return (new RegExp(parts[1], parts[2])).test(v1); + } + return (new RegExp(v2)).test(v1); + } +} \ No newline at end of file diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts new file mode 100644 index 0000000..a00dd01 --- /dev/null +++ b/src/tokens/comparison/wildcard.ts @@ -0,0 +1,154 @@ +import { ParserOptions } from '../../types/options'; + +import split from '../../helpers/unicode-safe-split'; +import toText from '../../helpers/to-text'; + +import { default as ComparisonToken, IComparisonToken } from './base'; + +const toRegExp = (subject: string, caseSensitive: boolean = false) : RegExp => { + let wc = split(subject); + let pattern = ''; + let anchorStart = true; + let anchorEnd = true; + let idx = 0; + let len = wc.length; + while (idx < len) { + + const atStart = idx === 0; + + let hasTokens = false; + let zeroOrMore = false; + let anyOneChar = 0; + + let char = wc[idx]; + while (char === '?' || char === '*') { + hasTokens = true; + if (wc[idx] === '?') { + anyOneChar += 1; + } else { + zeroOrMore = true; + } + idx += 1; + char = wc[idx]; + } + + if (hasTokens) { + pattern += '.'.repeat(anyOneChar); + if (zeroOrMore) { + const atEnd = idx === len; + + if (atStart) { + anchorStart = false; + } + if (atEnd) { + anchorEnd = false; + } + + if (!atStart && !atEnd) { + if (!anyOneChar) { + pattern += '.*'; + } else { + pattern += '+'; + } + } + } + continue; + } + + // Char needs to be escaped + if ( + char === '^' || + char === '.' || + char === '-' || + char === '+' || + char === '\\' || + char === '/' || + char === '|' || + char === '(' || + char === ')' || + char === '[' || + char === ']' || + char === '{' || + char === '}' || + char === '$' + ) { + pattern += `\\${char}`; + idx += 1; + continue; + } + + pattern += char; + idx += 1; + } + + if (anchorStart) { + pattern = '^' + pattern; + } + if (anchorEnd) { + pattern += '$'; + } + return new RegExp( + pattern, + 'u' + (!caseSensitive ? 'i' : '') + ) +}; + +interface IWildcardToken extends IComparisonToken { + caseSensitive: boolean; +} + +export default class WildcardToken extends ComparisonToken { + readonly caseSensitive: boolean + + constructor(token: IWildcardToken) { + super({ + ...token, + value: 'wildcard' + }); + this.caseSensitive = token.caseSensitive; + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.arguments[0].evaluate(options, meta); + v1 = toText(v1); + if (v1 == null) { + return false; + } + + let v2 = await this.arguments[1].evaluate(options, meta); + v2 = toText(v2); + if (v2 == null) { + return false; + } + + v2 = toRegExp(v2); + if (v2 == null) { + return false; + } + + return v2.test(v1); + } + + async handleInverse(options: ParserOptions, meta?: any): Promise { + let v1 = await this.arguments[0].evaluate(options, meta); + v1 = toText(v1); + if (v1 == null) { + return false; + } + + let v2 = await this.arguments[1].evaluate(options, meta); + v2 = toRegExp(v2); + if (v2 == null) { + return false; + } + + return !v2.test(v1); + } + + toToken() : object { + return { + ...(super.toToken()), + caseSensitive: this.caseSensitive + }; + } +} \ No newline at end of file diff --git a/src/tokens/if.js b/src/tokens/if.js deleted file mode 100644 index f659fa5..0000000 --- a/src/tokens/if.js +++ /dev/null @@ -1,159 +0,0 @@ -const types = require('../helpers/token-types.js'); -const { removeWhitespace } = require('../helpers/misc.js'); - -const { ExpressionSyntaxError } = require('../errors.js'); - -const BaseToken = require('./base.js'); -const comparisonHandler = require('./comparison.js'); -const logicOperatorHandler = require('./logic-operator.js'); -const argumentsHandler = require('./arguments.js'); - -class IfToken extends BaseToken { - constructor(options) { - super({ - type: types.IF, - ...options - }); - this.condition = options.condition; - this.arguments = options.arguments; - } - - async evaluate(options = {}) { - let result = await this.condition.evaluate(options); - - const args = []; - if (this.arguments && this.arguments.length) { - for (let idx = 0; idx < this.arguments.length; idx += 1) { - let accumulator = ''; - const parts = this.arguments[idx]; - for (let partsIdx = 0; partsIdx < parts.length; partsIdx += 1) { - let res = await parts[partsIdx].evaluate(options); - if (res != null) { - accumulator += res; - } - } - args.push(accumulator); - } - } - - // Only validating: validate both arguments - if (options.onlyValidate) { - const args = []; - if (this.arguments && this.arguments.length) { - for (let idx = 0; idx < this.arguments.length; idx += 1) { - let accumulator = ''; - const parts = this.arguments[idx]; - for (let partsIdx = 0; partsIdx < parts.length; partsIdx += 1) { - let res = await parts[partsIdx].evaluate(options); - if (res != null) { - accumulator += res; - } - } - args.push(accumulator); - } - } - return ''; - } - - // No arguments - if (!this.arguments || !this.arguments.length) { - return ''; - } - - // Evaluate conditional argument - if (result) { - let accumulator = ''; - const parts = this.arguments[0]; - for (let partsIdx = 0; partsIdx < parts.length; partsIdx += 1) { - let res = await parts[partsIdx].evaluate(options); - if (res != null) { - accumulator += res; - } - } - return accumulator; - - } else if (this.arguments[1] == null) { - return ''; - - } else { - let accumulator = ''; - const parts = this.arguments[1]; - for (let partsIdx = 0; partsIdx < parts.length; partsIdx += 1) { - let res = await parts[partsIdx].evaluate(options); - if (res != null) { - accumulator += res; - } - } - return accumulator; - } - } -} - -// tokenizeIf(); -module.exports.tokenize = (output, tokens) => { - - // not an $if[ token - if ( - !tokens.length || - tokens.length < 2 || - tokens[0].value !== '$' || - tokens[1].value !== 'if' - ) { - return false; - } - - if (!tokens[2] || tokens[2].value !== '[') { - throw new ExpressionSyntaxError('$if requires atleast 2 arguments', tokens[1].position); - } - - const position = tokens[0].position; - const args = []; - - // remove opening tokens - tokens.splice(0, 2); - - // Save opening bracket token - const openToken = tokens.shift(); - - // Attempt to consume logic condition - let condition = logicOperatorHandler.tokenize(tokens); - if (!condition) { - - // Attempt to consume comparison condition - condition = comparisonHandler.tokenize(tokens); - if (!condition) { - throw new ExpressionSyntaxError('$if requires the first argument to be a conditional', openToken.position + 1); - } - } - - // Comsume delimiter(,) following condition - if (!tokens.length) { - throw new ExpressionSyntaxError('unexpected end of expression'); - } - if (tokens[0].value !== ',') { - throw new ExpressionSyntaxError('expected comma delimiter after condition', tokens[0].position); - } - tokens.shift(); - removeWhitespace(tokens); - - // Re-add opening bracket token so tokenizeArguments() can be used to finish parsing the $if[] - tokens.unshift(openToken); - argumentsHandler.tokenize(args, tokens); - - // check result - if (args.length < 1) { - throw new ExpressionSyntaxError('$if requires at least 2 arguments', openToken.position); - } - if (args.length > 2) { - throw new ExpressionSyntaxError('$if requires at most 3 arguments', args[3].position); - } - - output.push(new IfToken({ - position, - condition, - arguments: args - })); - return true; -}; - -module.exports.IfToken = IfToken; \ No newline at end of file diff --git a/src/tokens/index.js b/src/tokens/index.js deleted file mode 100644 index cf5399f..0000000 --- a/src/tokens/index.js +++ /dev/null @@ -1,52 +0,0 @@ - -const types = require('../helpers/token-types.js'); - -const { tokenize } = require('../helpers/split.js'); - -// const { tokenizeEscape, tokenizeQuote, TextToken } = require('./text.js'); -const { tokenizeEscape, TextToken } = require('./text.js'); -const ifHandler = require('./if.js'); -const variableHandler = require('./variable.js'); - -// tokenize(expression) -module.exports = expression => { - let tokens = tokenize(expression); - const result = []; - - while (tokens.length) { - - // Attempt to consume token as escape sequence - if (tokenizeEscape(result, tokens)) { - continue; - } - - /* - - // Attempt to consume token as quoted text - if (tokenizeQuote(result, tokens)) { - continue; - } - */ - - // Attempt to consume token as $if - if (ifHandler.tokenize(result, tokens)) { - continue; - } - - // Attempt to consume token as a variable - if (variableHandler.tokenize(result, tokens)) { - continue; - } - - // Assume token is literal text - let token = tokens.shift(); - - if (result.length && result[result.length - 1].type === types.TEXT) { - result[result.length - 1].value += token.value; - - } else { - result.push(new TextToken(token)); - } - } - return result; -}; \ No newline at end of file diff --git a/src/tokens/logic-operator.js b/src/tokens/logic-operator.js deleted file mode 100644 index fc8772d..0000000 --- a/src/tokens/logic-operator.js +++ /dev/null @@ -1,104 +0,0 @@ -const types = require('../helpers/token-types.js'); - -const { ExpressionSyntaxError } = require('../errors.js'); -const { removeWhitespace } = require('../helpers/misc.js'); - -const BaseToken = require('./base.js'); -const comparisonHandler = require('./comparison.js'); - -const operators = require('../operators/logical.js'); - -class LogicToken extends BaseToken { - constructor(options) { - super({ - ...options, - type: types.LOGICAL - }); - this.arguments = options.arguments; - } - - async evaluate(options = {}) { - let operator = operators.get(this.value); - if (!operator) { - return false; - } - - let args = []; - for (let idx = 0; idx < this.arguments.length; idx += 1) { - let arg = await this.arguments[idx].evaluate(options); - args.push(arg); - } - - if (options.onlyValidate) { - return false; - } - return operator(...args); - } -} - -// tokenizeLogicOperator() -const tokenize = tokens => { - // Not a logical operator - if ( - tokens.length < 4 || - tokens[0].value !== '$' || - !operators.has('$' + tokens[1].value) || - tokens[2].value !== '[' - ) { - return; - } - - - // setup result token - const result = { - position: tokens[0].position, - value: '$' + tokens[1].value, - arguments: [] - } - - // Remove opening tokens: $ operator [ - tokens.splice(0, 3); - - while (tokens.length) { - - // Trim leading whitespace - removeWhitespace(tokens); - if (!tokens.length) { - break; - } - - // store start position - let position = tokens[0].position; - - // Consume condition and trailing whitespace - let token = tokenize(tokens); - if (token == null) { - token = comparisonHandler.tokenize(tokens); - if (token == null) { - throw new ExpressionSyntaxError('condition expected', position); - } - } - result.arguments.push(token); - removeWhitespace(tokens); - if (!tokens.length) { - break; - } - - // Argument delimiter - if (tokens[0].value === ',') { - tokens.shift(); - continue; - } - - // End of Logic Block - if (tokens[0].value === ']') { - tokens.shift(); - removeWhitespace(tokens); - return new LogicToken(result); - } - } - - throw new ExpressionSyntaxError('unexpected end of expression'); -}; -module.exports.tokenize = tokenize; -module.exports.LogicToken = LogicToken; \ No newline at end of file diff --git a/src/tokens/logical/base.ts b/src/tokens/logical/base.ts new file mode 100644 index 0000000..bf92f84 --- /dev/null +++ b/src/tokens/logical/base.ts @@ -0,0 +1,14 @@ +import { TokenType } from '../../types/token-types'; + +import { default as OperatorToken, IOperatorToken} from '../operator'; + +export interface ILogicalToken extends IOperatorToken { } + +export default class LogicalToken extends OperatorToken { + constructor(token: ILogicalToken) { + super({ + type: TokenType.LOGICAL, + ...token + }); + } +} \ No newline at end of file diff --git a/src/tokens/logical/logical-and.ts b/src/tokens/logical/logical-and.ts new file mode 100644 index 0000000..553b8a6 --- /dev/null +++ b/src/tokens/logical/logical-and.ts @@ -0,0 +1,21 @@ +import { ParserOptions } from '../../types/options'; +import { default as LogicalToken, ILogicalToken } from './base'; + +export default class AndOperator extends LogicalToken { + constructor(token: ILogicalToken) { + super({ + ...token, + value: 'and' + }); + } + + async evaluate(options: ParserOptions, meta?: any): Promise { + const left = await this.arguments[0].evaluate(options, meta); + if (left == null || left === false || left === '' || left === 0) { + return false; + } + + const right = await this.arguments[1].evaluate(options, meta); + return right != null && right != false && right !== '' && right !== 0; + } +} \ No newline at end of file diff --git a/src/tokens/logical/logical-not.ts b/src/tokens/logical/logical-not.ts new file mode 100644 index 0000000..4a0228b --- /dev/null +++ b/src/tokens/logical/logical-not.ts @@ -0,0 +1,16 @@ +import { ParserOptions } from '../../types/options'; +import { default as LogicalToken, ILogicalToken } from './base'; + +export default class NotOperator extends LogicalToken { + constructor(token: ILogicalToken) { + super({ + ...token, + value: 'not' + }); + } + + async evaluate(options: ParserOptions, meta?: any): Promise { + const value = await this.arguments[0].evaluate(options, meta); + return value != null && value !== false && value !== '' && value !== 0; + } +} \ No newline at end of file diff --git a/src/tokens/logical/logical-or.ts b/src/tokens/logical/logical-or.ts new file mode 100644 index 0000000..f60588a --- /dev/null +++ b/src/tokens/logical/logical-or.ts @@ -0,0 +1,21 @@ +import { ParserOptions } from '../../types/options'; +import { default as LogicalToken, ILogicalToken } from './base'; + +export default class OrOperator extends LogicalToken { + constructor(token: ILogicalToken) { + super({ + ...token, + value: 'or' + }); + } + + async evaluate(options: ParserOptions, meta?: any): Promise { + const left = await this.arguments[0].evaluate(options, meta); + if (left != null && left !== false && left !== '' && left !== 0) { + return true; + } + + const right = await this.arguments[1].evaluate(options, meta); + return right != null && right != false && right !== '' && right !== 0; + } +} \ No newline at end of file diff --git a/src/tokens/operator.ts b/src/tokens/operator.ts new file mode 100644 index 0000000..3d04fb4 --- /dev/null +++ b/src/tokens/operator.ts @@ -0,0 +1,28 @@ +import type TokenList from './token-list'; +import { ParserOptions } from '../types/options'; +import { default as Token, IToken } from './base'; + +export interface IOperatorToken extends IToken { + arguments: TokenList +} + +export default class OperatorToken extends Token { + + protected arguments : TokenList; + + constructor(token: IOperatorToken) { + super(token); + this.arguments = token.arguments; + } + + async evaluate(options: ParserOptions, meta?: any) : Promise { + return false; + } + + toToken() : object { + return { + ...(super.toToken()), + arguments: this.arguments.toToken() + } + } +} \ No newline at end of file diff --git a/src/tokens/text.js b/src/tokens/text.js deleted file mode 100644 index 383c350..0000000 --- a/src/tokens/text.js +++ /dev/null @@ -1,114 +0,0 @@ -const { ExpressionSyntaxError } = require('../errors.js'); -const types = require('../helpers/token-types.js'); - -const BaseToken = require('./base.js'); - -class TextToken extends BaseToken { - constructor(options) { - super({ - ...options, - type: types.TEXT - }); - } -} - -const tokenizeEscape = (output, tokens, escape) => { - if (!tokens.length || tokens[0].value !== '\\') { - return false; - } - - if (escape == null) { - escape = '"$[\\rnt'; - } - - // get escape denoter character(\) - let token = tokens.shift(); - - // Denoter followed by non-escapable character - Treat as plain text - if ( - tokens[0] == null || - escape.indexOf(tokens[0].value[0]) === -1 - ) { - if (output[1] != null && output[output.length - 1].type === types.TEXT) { - output[output.length - 1].value += token.value; - } else { - output.push(new TextToken(token)); - } - return true; - } - - // Get escaped token - token = tokens.shift(); - - // Escaped token contains more than one character - // split escaped character from remaining token text - if (token.length > 1) { - tokens.unshift({ - position: token.position, - value: token.value.slice(1) - }); - token.value = token.value[0]; - } - - // If token is special-character-sequence - // replace sequence with represented value - if (token.value === 'n') { - token.value = '\n'; - } else if (token.value === 'r') { - token.value = '\r'; - } else if (token.value === 't') { - token.value = '\t'; - } - - // If the last token of the output is text, append the token value to the text - if (output.length && output[output.length - 1].type === types.TEXT) { - output[output.length - 1].value += token.value; - - // Otherwise add a new text token to the output - } else { - output.push(new TextToken(token)); - } - return true; -}; - -const tokenizeQuote = (output, tokens) => { - if (!tokens.length || tokens[0].value !== '"') { - return false; - } - - const openToken = tokens.shift(); - - let text = []; - while (tokens.length) { - if (tokens[0].value === '"') { - tokens.shift(); - text = text.map(item => item.value).join(''); - - if (output.length && output[output.length - 1].type === types.TEXT) { - output[output.length - 1].value += text; - - } else { - output.push(new TextToken({ - position: openToken.position + 1, - value: text - })); - } - return true; - } - - if (tokenizeEscape(text, tokens, '\\"nrt')) { - continue; - } - - text.push(tokens.shift()); - } - - // End quote wasn't encountered in the loop - throw new ExpressionSyntaxError('end quote missing', openToken.position); -}; - -module.exports = { - tokenizeEscape, - tokenizeQuote, - TextToken -}; \ No newline at end of file diff --git a/src/tokens/text.ts b/src/tokens/text.ts new file mode 100644 index 0000000..9923fb6 --- /dev/null +++ b/src/tokens/text.ts @@ -0,0 +1,7 @@ +import { default as Token, IToken } from './base'; + +export default class TextToken extends Token { + constructor(token: IToken) { + super(token); + } +} \ No newline at end of file diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts new file mode 100644 index 0000000..3312ebe --- /dev/null +++ b/src/tokens/token-list.ts @@ -0,0 +1,64 @@ +import { TokenType } from '../types/token-types'; + +import toText from '../helpers/to-text'; + +import { default as Token, IToken } from './base'; + +export interface ITokenList extends IToken { + value: Token[]; +} + +export default class TokenList extends Token { + protected value : Token[]; + + constructor(token: ITokenList) { + super({ + ...token, + type: TokenType.TOKENLIST + }); + } + + async evaluate(options: any, meta?: any): Promise { + const parts = this.value; + + let res : any; + for (let idx = 0; idx < parts.length; idx += 1) { + let value = await parts[idx].evaluate(options, meta); + + if (value === undefined) { + continue; + } + + if (res == null) { + res = value; + continue; + } + + let strValue = toText(value); + if (strValue == null) { + continue; + } + + if (typeof res !== 'string') { + let strRes = toText(res); + if (strRes == null) { + res = value; + } else { + res = strRes + strValue; + } + continue; + } + + res += strValue; + } + + return res; + } + + toToken() : object { + return { + ...(super.toToken()), + parts: this.value.map(value => value.toToken()) + }; + } +} \ No newline at end of file diff --git a/src/tokens/variable.js b/src/tokens/variable.js deleted file mode 100644 index e3d50e8..0000000 --- a/src/tokens/variable.js +++ /dev/null @@ -1,103 +0,0 @@ -const { ExpressionVariableError, ExpressionArgumentsError } = require('../errors.js'); - -const types = require('../helpers/token-types.js'); - -const BaseToken = require('./base.js'); -const argumentsHandler = require('./arguments.js'); - -const nameCheck = /^([a-z][a-z\d]+)([\s\S]*)$/i; - -class VariableToken extends BaseToken { - constructor(options) { - super({ - ...options, - type: types.VARIABLE - }); - this.arguments = options.arguments; - } - - async evaluate(options = {}) { - if (!options.handlers || !options.handlers.has(this.value)) { - throw new ExpressionVariableError(`unknown variable`, this.position, this.value); - } - - const variable = options.handlers.get(this.value); - - if (variable.triggers) { - let trigger = variable.triggers[options.trigger.type], - display = options.trigger.type ? options.trigger.type.toLowerCase() : "unknown trigger"; - - if (trigger == null || trigger === false) { - throw new ExpressionVariableError(`$${this.value} does not support being triggered by: ${display}`, this.position, this.value); - } - - if (Array.isArray(trigger)) { - if (!trigger.some(id => id === options.trigger.id)) { - throw new ExpressionVariableError(`$${this.value} does not support this specific trigger type: ${display}`, this.position, this.value); - } - } - } - - let args = []; - if (this.arguments && this.arguments.length) { - for (let idx = 0; idx < this.arguments.length; idx += 1) { - const parts = this.arguments[idx]; - let accumulator = ''; - for (let partIdx = 0; partIdx < parts.length; partIdx += 1) { - let part = await parts[partIdx].evaluate(options); - if (part != null) { - accumulator += part; - } - } - args.push(accumulator); - } - } - if (options.onlyValidate) { - return ''; - } - - try { - if (variable.argsCheck) { - await variable.argsCheck(...args); - } - } catch (err) { - throw new ExpressionArgumentsError(err.message, err.position, err.index); - } - const result = await variable.evaluator(options.metadata || {}, ...args); - return result == null ? '' : result; - } -} - -// tokenizeVariable() -module.exports.tokenize = (output, tokens) => { - - let nameMatch; - if ( - tokens.length < 2 || - tokens[0].value !== '$' || - !(nameMatch = nameCheck.exec(tokens[1].value)) - ) { - return false; - } - tokens.shift(); - - const token = tokens.shift(); - - // trailing character after variable name - if (nameMatch[2] !== '') { - tokens.unshift({ - value: nameMatch[2], - position: token.position + nameMatch[1].length - }); - token.value = nameMatch[1]; - } - - const args = []; - if (argumentsHandler.tokenize(args, tokens)) { - token.arguments = args; - } - - output.push(new VariableToken(token)) - return true; -}; -module.exports.VariableToken = VariableToken; \ No newline at end of file diff --git a/src/types/operator-types.ts b/src/types/operator-types.ts new file mode 100644 index 0000000..07f29fa --- /dev/null +++ b/src/types/operator-types.ts @@ -0,0 +1,5 @@ +enum operatorTypes { + COMPARISON, + LOGICAL +} +export default operatorTypes; \ No newline at end of file diff --git a/src/types/options.ts b/src/types/options.ts new file mode 100644 index 0000000..a8aa32d --- /dev/null +++ b/src/types/options.ts @@ -0,0 +1,13 @@ +interface Handler { + argsCheck?: (...args: any[]) => any; + evaluator: (...args: any[]) => any; +} + +type LookupHandler = (name: string) => Handler; + +export interface ParserOptions { + conditionalHandlers: Record + functionalHandlers: Record; + verifyOnly?: boolean; + skipArgumentsCheck?: boolean; +}; \ No newline at end of file diff --git a/src/types/token-types.ts b/src/types/token-types.ts new file mode 100644 index 0000000..09edb0e --- /dev/null +++ b/src/types/token-types.ts @@ -0,0 +1,10 @@ +export enum TokenType { + TOKENLIST, + TEXT, + ARGUMENT, + FUNCTIONAL, + CONDITIONAL, + LOGICAL, + COMPARISON, + UNKNOWN +} \ No newline at end of file From 9feb263d93d78833f8fb0d43095cc8871e61af3b Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 04:43:59 -0400 Subject: [PATCH 02/92] feat: implement left/right for operator tokens --- src/tokens/comparison/equal-loose.ts | 8 ++++++-- src/tokens/comparison/equal-strict.ts | 8 ++++++-- src/tokens/comparison/exists.ts | 2 +- src/tokens/comparison/greater-than-equal.ts | 8 ++++++-- src/tokens/comparison/greater-than.ts | 8 ++++++-- src/tokens/comparison/less-than-equal.ts | 8 ++++++-- src/tokens/comparison/less-than.ts | 8 ++++++-- src/tokens/comparison/numerical.ts | 6 +++--- src/tokens/comparison/regex.ts | 8 ++++++-- src/tokens/comparison/wildcard.ts | 16 ++++++++++++---- src/tokens/logical/base.ts | 4 +++- src/tokens/logical/logical-and.ts | 9 +++++++-- src/tokens/logical/logical-not.ts | 2 +- src/tokens/logical/logical-or.ts | 8 ++++++-- src/tokens/operator.ts | 19 ++++++++++++++----- 15 files changed, 89 insertions(+), 33 deletions(-) diff --git a/src/tokens/comparison/equal-loose.ts b/src/tokens/comparison/equal-loose.ts index 5d28134..b8293ec 100644 --- a/src/tokens/comparison/equal-loose.ts +++ b/src/tokens/comparison/equal-loose.ts @@ -12,8 +12,12 @@ export default class EqualLooseToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - const v1 = await this.arguments[0].evaluate(options, meta); - const v2 = await this.arguments[1].evaluate(options, meta); + const v1 = await this.left.evaluate(options, meta); + + let v2 : any; + if (this.right != null) { + v2 = await this.right.evaluate(options, meta); + } if ( v1 === v2 || diff --git a/src/tokens/comparison/equal-strict.ts b/src/tokens/comparison/equal-strict.ts index 758a4d4..9d6023e 100644 --- a/src/tokens/comparison/equal-strict.ts +++ b/src/tokens/comparison/equal-strict.ts @@ -10,8 +10,12 @@ export default class EqualStrictToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - const v1 = await this.arguments[0].evaluate(options, meta); - const v2 = await this.arguments[1].evaluate(options, meta); + const v1 = await this.left.evaluate(options, meta); + + let v2 : any; + if (this.right != null) { + v2 = await this.right.evaluate(options, meta); + } return ( v1 === v2 || diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts index c5d8d07..903f2a7 100644 --- a/src/tokens/comparison/exists.ts +++ b/src/tokens/comparison/exists.ts @@ -10,7 +10,7 @@ export default class ExistsToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.arguments[0].evaluate(options, meta); + let v1 = await this.left.evaluate(options, meta); return v1 != null && v1 !== ''; } } \ No newline at end of file diff --git a/src/tokens/comparison/greater-than-equal.ts b/src/tokens/comparison/greater-than-equal.ts index f7bc8b1..d4f00ec 100644 --- a/src/tokens/comparison/greater-than-equal.ts +++ b/src/tokens/comparison/greater-than-equal.ts @@ -11,8 +11,12 @@ export default class GreaterThanEqualToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.arguments[0].evaluate(options, meta); - let v2 = await this.arguments[1].evaluate(options, meta); + if (this.right == null) { + return false + } + + let v1 = await this.left.evaluate(options, meta); + let v2 = await this.right.evaluate(options, meta); v1 = toNumber(v1); if (v1 == null) { diff --git a/src/tokens/comparison/greater-than.ts b/src/tokens/comparison/greater-than.ts index d230fa1..289c2d2 100644 --- a/src/tokens/comparison/greater-than.ts +++ b/src/tokens/comparison/greater-than.ts @@ -11,8 +11,12 @@ export default class GreaterThanToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.arguments[0].evaluate(options, meta); - let v2 = await this.arguments[1].evaluate(options, meta); + if (this.right == null) { + return false + } + + let v1 = await this.left.evaluate(options, meta); + let v2 = await this.right.evaluate(options, meta); v1 = toNumber(v1); if (v1 == null) { diff --git a/src/tokens/comparison/less-than-equal.ts b/src/tokens/comparison/less-than-equal.ts index db7e4cf..0495353 100644 --- a/src/tokens/comparison/less-than-equal.ts +++ b/src/tokens/comparison/less-than-equal.ts @@ -11,8 +11,12 @@ export default class LessThanEqualToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.arguments[0].evaluate(options, meta); - let v2 = await this.arguments[1].evaluate(options, meta); + if (this.right == null) { + return false + } + + let v1 = await this.left.evaluate(options, meta); + let v2 = await this.right.evaluate(options, meta); v1 = toNumber(v1); if (v1 == null) { diff --git a/src/tokens/comparison/less-than.ts b/src/tokens/comparison/less-than.ts index a86464f..da46d12 100644 --- a/src/tokens/comparison/less-than.ts +++ b/src/tokens/comparison/less-than.ts @@ -11,8 +11,12 @@ export default class LessThanToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.arguments[0].evaluate(options, meta); - let v2 = await this.arguments[1].evaluate(options, meta); + if (this.right == null) { + return false + } + + let v1 = await this.left.evaluate(options, meta); + let v2 = await this.right.evaluate(options, meta); v1 = toNumber(v1); if (v1 == null) { diff --git a/src/tokens/comparison/numerical.ts b/src/tokens/comparison/numerical.ts index efa272e..6fef308 100644 --- a/src/tokens/comparison/numerical.ts +++ b/src/tokens/comparison/numerical.ts @@ -13,18 +13,18 @@ export default class LessThanToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.arguments[0].evaluate(options, meta); + let v1 = await this.left.evaluate(options, meta); v1 = toNumber(v1); if (v1 == null) { return false; } - if (this.arguments.length == 1) { + if (this.right == null) { return true; } - let v2 = await this.arguments[1].evaluate(options, meta); + let v2 = await this.right.evaluate(options, meta); if (v2 == null || v2 === '') { return true; diff --git a/src/tokens/comparison/regex.ts b/src/tokens/comparison/regex.ts index 7dac271..e68282f 100644 --- a/src/tokens/comparison/regex.ts +++ b/src/tokens/comparison/regex.ts @@ -11,8 +11,12 @@ export default class LessThanToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.arguments[0].evaluate(options, meta); - let v2 = await this.arguments[1].evaluate(options, meta); + if (this.right == null) { + return false + } + + let v1 = await this.left.evaluate(options, meta); + let v2 = await this.right.evaluate(options, meta); if (v1 == null || typeof v2 !== 'string') { return false; diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts index a00dd01..7eff2f2 100644 --- a/src/tokens/comparison/wildcard.ts +++ b/src/tokens/comparison/wildcard.ts @@ -109,13 +109,17 @@ export default class WildcardToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.arguments[0].evaluate(options, meta); + if (this.right == null) { + return false; + } + + let v1 = await this.left.evaluate(options, meta); v1 = toText(v1); if (v1 == null) { return false; } - let v2 = await this.arguments[1].evaluate(options, meta); + let v2 = await this.right.evaluate(options, meta); v2 = toText(v2); if (v2 == null) { return false; @@ -130,13 +134,17 @@ export default class WildcardToken extends ComparisonToken { } async handleInverse(options: ParserOptions, meta?: any): Promise { - let v1 = await this.arguments[0].evaluate(options, meta); + if (this.right == null) { + return false; + } + + let v1 = await this.left.evaluate(options, meta); v1 = toText(v1); if (v1 == null) { return false; } - let v2 = await this.arguments[1].evaluate(options, meta); + let v2 = await this.right.evaluate(options, meta); v2 = toRegExp(v2); if (v2 == null) { return false; diff --git a/src/tokens/logical/base.ts b/src/tokens/logical/base.ts index bf92f84..bb091e2 100644 --- a/src/tokens/logical/base.ts +++ b/src/tokens/logical/base.ts @@ -1,8 +1,10 @@ import { TokenType } from '../../types/token-types'; +import { default as Token, IToken } from '../base'; import { default as OperatorToken, IOperatorToken} from '../operator'; -export interface ILogicalToken extends IOperatorToken { } +export interface ILogicalToken extends IOperatorToken { +} export default class LogicalToken extends OperatorToken { constructor(token: ILogicalToken) { diff --git a/src/tokens/logical/logical-and.ts b/src/tokens/logical/logical-and.ts index 553b8a6..3e3eb14 100644 --- a/src/tokens/logical/logical-and.ts +++ b/src/tokens/logical/logical-and.ts @@ -10,12 +10,17 @@ export default class AndOperator extends LogicalToken { } async evaluate(options: ParserOptions, meta?: any): Promise { - const left = await this.arguments[0].evaluate(options, meta); + if (this.right == null) { + return false; + } + + const left = await this.left.evaluate(options, meta); if (left == null || left === false || left === '' || left === 0) { return false; } - const right = await this.arguments[1].evaluate(options, meta); + + const right = await this.right.evaluate(options, meta); return right != null && right != false && right !== '' && right !== 0; } } \ No newline at end of file diff --git a/src/tokens/logical/logical-not.ts b/src/tokens/logical/logical-not.ts index 4a0228b..c08ac34 100644 --- a/src/tokens/logical/logical-not.ts +++ b/src/tokens/logical/logical-not.ts @@ -10,7 +10,7 @@ export default class NotOperator extends LogicalToken { } async evaluate(options: ParserOptions, meta?: any): Promise { - const value = await this.arguments[0].evaluate(options, meta); + const value = await this.left.evaluate(options, meta); return value != null && value !== false && value !== '' && value !== 0; } } \ No newline at end of file diff --git a/src/tokens/logical/logical-or.ts b/src/tokens/logical/logical-or.ts index f60588a..d748590 100644 --- a/src/tokens/logical/logical-or.ts +++ b/src/tokens/logical/logical-or.ts @@ -10,12 +10,16 @@ export default class OrOperator extends LogicalToken { } async evaluate(options: ParserOptions, meta?: any): Promise { - const left = await this.arguments[0].evaluate(options, meta); + const left = await this.left.evaluate(options, meta); if (left != null && left !== false && left !== '' && left !== 0) { return true; } - const right = await this.arguments[1].evaluate(options, meta); + if (this.right == null) { + return false; + } + + const right = await this.right.evaluate(options, meta); return right != null && right != false && right !== '' && right !== 0; } } \ No newline at end of file diff --git a/src/tokens/operator.ts b/src/tokens/operator.ts index 3d04fb4..a98e25d 100644 --- a/src/tokens/operator.ts +++ b/src/tokens/operator.ts @@ -3,16 +3,18 @@ import { ParserOptions } from '../types/options'; import { default as Token, IToken } from './base'; export interface IOperatorToken extends IToken { - arguments: TokenList + left: Token, + right?: Token } export default class OperatorToken extends Token { - - protected arguments : TokenList; + protected left: Token; + protected right?: Token; constructor(token: IOperatorToken) { super(token); - this.arguments = token.arguments; + this.left = token.left; + this.right = token.right; } async evaluate(options: ParserOptions, meta?: any) : Promise { @@ -20,9 +22,16 @@ export default class OperatorToken extends Token { } toToken() : object { + const self : Record = { + left: this.left + }; + if (this.right != null) { + self.right = this.right; + } + return { ...(super.toToken()), - arguments: this.arguments.toToken() + ...self } } } \ No newline at end of file From 0f96837c711f82f7a21844921cc519c8ae62ef97 Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 04:50:07 -0400 Subject: [PATCH 03/92] rebase: rename inverted to invert --- src/tokens/comparison/base.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tokens/comparison/base.ts b/src/tokens/comparison/base.ts index bd1a505..5f947d4 100644 --- a/src/tokens/comparison/base.ts +++ b/src/tokens/comparison/base.ts @@ -4,11 +4,11 @@ import { TokenType } from '../../types/token-types'; import { default as OperatorToken, IOperatorToken } from '../operator'; export interface IComparisonToken extends IOperatorToken { - inverted?: boolean; + invert?: boolean; } export default class ComparisonToken extends OperatorToken { - protected inverted : boolean; + protected invert : boolean = false; constructor(token: IComparisonToken) { super({ @@ -16,7 +16,9 @@ export default class ComparisonToken extends OperatorToken { ...token }); - this.inverted = !!token.inverted; + if (token.invert) { + this.invert = true; + } } async handle(options: ParserOptions, meta?: any) : Promise { @@ -29,7 +31,7 @@ export default class ComparisonToken extends OperatorToken { } async evaluate(options: ParserOptions, meta?: any) : Promise { - if (this.inverted) { + if (this.invert) { return this.handleInverse(options, meta); } return this.handle(options, meta); @@ -38,7 +40,7 @@ export default class ComparisonToken extends OperatorToken { toToken() : object { return { ...(super.toToken()), - inverted: this.inverted + invert: this.invert } } } \ No newline at end of file From 74eb1a6ed596ab25c1d0021d2a8a5caf659e26a6 Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 04:50:29 -0400 Subject: [PATCH 04/92] improve: toToken logic for operators --- src/tokens/operator.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/tokens/operator.ts b/src/tokens/operator.ts index a98e25d..54356c8 100644 --- a/src/tokens/operator.ts +++ b/src/tokens/operator.ts @@ -22,16 +22,11 @@ export default class OperatorToken extends Token { } toToken() : object { - const self : Record = { - left: this.left - }; - if (this.right != null) { - self.right = this.right; - } - + const right : any = this.right; return { ...(super.toToken()), - ...self + left: this.left, + right } } } \ No newline at end of file From 6cf71c19fbf709749e48ddf2e59d5efae8e85846 Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 04:53:13 -0400 Subject: [PATCH 05/92] fix: tokenlist.toToken not tokenizing value --- src/tokens/token-list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index 3312ebe..a758aa0 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -58,7 +58,7 @@ export default class TokenList extends Token { toToken() : object { return { ...(super.toToken()), - parts: this.value.map(value => value.toToken()) + value: this.value.map(value => value.toToken()) }; } } \ No newline at end of file From f4151081656dd81f7f00f3395aa53d1cf95301ce Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 04:53:57 -0400 Subject: [PATCH 06/92] improve: remove unneeded import --- src/tokens/operator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tokens/operator.ts b/src/tokens/operator.ts index 54356c8..dfe7844 100644 --- a/src/tokens/operator.ts +++ b/src/tokens/operator.ts @@ -1,4 +1,3 @@ -import type TokenList from './token-list'; import { ParserOptions } from '../types/options'; import { default as Token, IToken } from './base'; From de0751cf040be88b51499dd0812184a6874753ee Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 06:04:56 -0400 Subject: [PATCH 07/92] feat: implement verifyOnly option --- src/tokens/comparison/equal-loose.ts | 12 +++- src/tokens/comparison/equal-strict.ts | 11 ++- src/tokens/comparison/exists.ts | 5 ++ src/tokens/comparison/greater-than-equal.ts | 7 +- src/tokens/comparison/greater-than.ts | 7 +- src/tokens/comparison/less-than-equal.ts | 7 +- src/tokens/comparison/less-than.ts | 7 +- src/tokens/comparison/numerical.ts | 11 ++- src/tokens/comparison/regex.ts | 7 +- src/tokens/comparison/wildcard.ts | 19 ++++- src/tokens/functional.ts | 78 +++++++++++++++++++++ src/tokens/if-statement.ts | 57 +++++++++++++++ src/tokens/logical/logical-and.ts | 9 ++- src/tokens/logical/logical-not.ts | 5 ++ src/tokens/logical/logical-or.ts | 14 ++-- src/tokens/operator.ts | 12 ++-- src/tokens/token-list.ts | 6 +- src/types/options.ts | 7 +- src/types/token-types.ts | 4 +- 19 files changed, 252 insertions(+), 33 deletions(-) create mode 100644 src/tokens/functional.ts create mode 100644 src/tokens/if-statement.ts diff --git a/src/tokens/comparison/equal-loose.ts b/src/tokens/comparison/equal-loose.ts index b8293ec..560c92a 100644 --- a/src/tokens/comparison/equal-loose.ts +++ b/src/tokens/comparison/equal-loose.ts @@ -12,11 +12,17 @@ export default class EqualLooseToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { + + if (this.right == null) { + // TODO - custom error + throw new Error('TODO'); + } + const v1 = await this.left.evaluate(options, meta); + const v2 = await this.right.evaluate(options, meta); - let v2 : any; - if (this.right != null) { - v2 = await this.right.evaluate(options, meta); + if (options.verifyOnly) { + return false; } if ( diff --git a/src/tokens/comparison/equal-strict.ts b/src/tokens/comparison/equal-strict.ts index 9d6023e..5942cc2 100644 --- a/src/tokens/comparison/equal-strict.ts +++ b/src/tokens/comparison/equal-strict.ts @@ -10,11 +10,16 @@ export default class EqualStrictToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { + if (this.right == null) { + // TODO - custom error + throw new Error('TODO'); + } + const v1 = await this.left.evaluate(options, meta); + const v2 = await this.right.evaluate(options, meta); - let v2 : any; - if (this.right != null) { - v2 = await this.right.evaluate(options, meta); + if (options.verifyOnly) { + return false; } return ( diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts index 903f2a7..5d8fe53 100644 --- a/src/tokens/comparison/exists.ts +++ b/src/tokens/comparison/exists.ts @@ -11,6 +11,11 @@ export default class ExistsToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { let v1 = await this.left.evaluate(options, meta); + + if (options.verifyOnly) { + return false; + } + return v1 != null && v1 !== ''; } } \ No newline at end of file diff --git a/src/tokens/comparison/greater-than-equal.ts b/src/tokens/comparison/greater-than-equal.ts index d4f00ec..759f561 100644 --- a/src/tokens/comparison/greater-than-equal.ts +++ b/src/tokens/comparison/greater-than-equal.ts @@ -12,12 +12,17 @@ export default class GreaterThanEqualToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { - return false + // TODO - custom error + throw new Error('TODO'); } let v1 = await this.left.evaluate(options, meta); let v2 = await this.right.evaluate(options, meta); + if (options.verifyOnly) { + return false; + } + v1 = toNumber(v1); if (v1 == null) { return false; diff --git a/src/tokens/comparison/greater-than.ts b/src/tokens/comparison/greater-than.ts index 289c2d2..00212e7 100644 --- a/src/tokens/comparison/greater-than.ts +++ b/src/tokens/comparison/greater-than.ts @@ -12,12 +12,17 @@ export default class GreaterThanToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { - return false + // TODO - custom error + throw new Error('TODO'); } let v1 = await this.left.evaluate(options, meta); let v2 = await this.right.evaluate(options, meta); + if (options.verifyOnly) { + return false; + } + v1 = toNumber(v1); if (v1 == null) { return false; diff --git a/src/tokens/comparison/less-than-equal.ts b/src/tokens/comparison/less-than-equal.ts index 0495353..274dfbd 100644 --- a/src/tokens/comparison/less-than-equal.ts +++ b/src/tokens/comparison/less-than-equal.ts @@ -12,12 +12,17 @@ export default class LessThanEqualToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { - return false + // TODO - custom error + throw new Error('TODO'); } let v1 = await this.left.evaluate(options, meta); let v2 = await this.right.evaluate(options, meta); + if (options.verifyOnly) { + return false; + } + v1 = toNumber(v1); if (v1 == null) { return false; diff --git a/src/tokens/comparison/less-than.ts b/src/tokens/comparison/less-than.ts index da46d12..731aeb6 100644 --- a/src/tokens/comparison/less-than.ts +++ b/src/tokens/comparison/less-than.ts @@ -12,12 +12,17 @@ export default class LessThanToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { - return false + // TODO - custom error + throw new Error('TODO'); } let v1 = await this.left.evaluate(options, meta); let v2 = await this.right.evaluate(options, meta); + if (options.verifyOnly) { + return false; + } + v1 = toNumber(v1); if (v1 == null) { return false; diff --git a/src/tokens/comparison/numerical.ts b/src/tokens/comparison/numerical.ts index 6fef308..2f6fadb 100644 --- a/src/tokens/comparison/numerical.ts +++ b/src/tokens/comparison/numerical.ts @@ -14,18 +14,23 @@ export default class LessThanToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { let v1 = await this.left.evaluate(options, meta); - v1 = toNumber(v1); - if (v1 == null) { + if (options.verifyOnly) { + if (this.right != null) { + await this.right.evaluate(options, meta); + } return false; } + v1 = toNumber(v1); + if (v1 == null) { + return false; + } if (this.right == null) { return true; } let v2 = await this.right.evaluate(options, meta); - if (v2 == null || v2 === '') { return true; } diff --git a/src/tokens/comparison/regex.ts b/src/tokens/comparison/regex.ts index e68282f..164f9ff 100644 --- a/src/tokens/comparison/regex.ts +++ b/src/tokens/comparison/regex.ts @@ -12,12 +12,17 @@ export default class LessThanToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { - return false + // TODO - custom error + throw new Error('TODO'); } let v1 = await this.left.evaluate(options, meta); let v2 = await this.right.evaluate(options, meta); + if (options.verifyOnly) { + return false; + } + if (v1 == null || typeof v2 !== 'string') { return false; } diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts index 7eff2f2..532a220 100644 --- a/src/tokens/comparison/wildcard.ts +++ b/src/tokens/comparison/wildcard.ts @@ -110,10 +110,17 @@ export default class WildcardToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { - return false; + // TODO - custom error + throw new Error('TODO'); } let v1 = await this.left.evaluate(options, meta); + + if (options.verifyOnly) { + await this.right.evaluate(options, meta); + return false; + } + v1 = toText(v1); if (v1 == null) { return false; @@ -134,11 +141,19 @@ export default class WildcardToken extends ComparisonToken { } async handleInverse(options: ParserOptions, meta?: any): Promise { + if (this.right == null) { - return false; + // TODO - custom error + throw new Error('TODO'); } let v1 = await this.left.evaluate(options, meta); + + if (options.verifyOnly) { + await this.right.evaluate(options, meta); + return false; + } + v1 = toText(v1); if (v1 == null) { return false; diff --git a/src/tokens/functional.ts b/src/tokens/functional.ts new file mode 100644 index 0000000..bdc9abd --- /dev/null +++ b/src/tokens/functional.ts @@ -0,0 +1,78 @@ +import { ParserOptions } from '../types/options'; +import { TokenType } from '../types/token-types'; +import { default as Token, IToken } from './base'; +import type { default as TokenList } from './token-list'; + +export interface IFunctionalToken extends IToken { + prefix: string; + arguments: TokenList +} + +export default class FunctionalToken extends Token { + protected prefix: string; + protected value: string; + protected arguments: TokenList; + + constructor(token: IFunctionalToken) { + super({ + ...token, + type: TokenType.FUNCTIONAL + }); + this.prefix = token.prefix; + this.arguments = token.arguments; + } + + async evaluate(options: ParserOptions, meta: any = {}) : Promise { + const lookupHandler = options.functionalHandlers[this.prefix]; + + if (lookupHandler == null) { + // TODO: custom errors + throw new Error('TODO'); + } + const handler = lookupHandler(meta, this.value); + + if (handler == null) { + // TODO: custom errors + throw new Error('TODO'); + } + + let args : any[] = []; + if (this.arguments != null) { + const argList = this.arguments.value; + for (let idx = 0; idx < argList.length; idx += 1) { + const arg = await argList[idx].evaluate(options, meta); + args.push(arg); + } + } + + if (options.verifyOnly) { + return; + } + + if (!options.skipArgumentsCheck && handler.argsCheck != null) { + try { + await handler.argsCheck(meta, args); + } catch (err) { + // TODO - Custom errors + throw err; + } + } + + try { + const res = handler.evaluator(meta, ...args); + return res; + + } catch (err) { + // TODO: custom errors + throw err; + } + } + + toToken() : object { + return { + ...(super.toToken()), + prefix: this.prefix, + arguments: this.arguments.toToken() + } + } +} \ No newline at end of file diff --git a/src/tokens/if-statement.ts b/src/tokens/if-statement.ts new file mode 100644 index 0000000..3ebf835 --- /dev/null +++ b/src/tokens/if-statement.ts @@ -0,0 +1,57 @@ +import { ParserOptions } from '../types/options'; +import { TokenType } from '../types/token-types'; +import { default as Token, IToken } from './base'; +import OperatorToken from './operator'; + +export interface IIfStatementToken extends IToken { + condition: OperatorToken; + whenTrue: Token, + whenFalse?: Token +} + +export default class IfStatementToken extends Token { + protected condition: OperatorToken; + protected whenTrue: Token; + protected whenFalse?: Token; + + constructor(token: IIfStatementToken) { + super({ + ...token, + type: TokenType.IFSTATEMENT, + value: 'if' + }); + this.condition = token.condition; + this.whenTrue = token.whenTrue; + this.whenFalse = token.whenFalse; + } + + async evaluate(options: ParserOptions, meta?: any) : Promise { + const res = await this.condition.evaluate(options, meta); + + if (options.verifyOnly) { + await this.whenTrue.evaluate(options, meta); + if (this.whenFalse != null) { + await this.whenFalse.evaluate(options, meta); + } + return; + } + + if (res != null && res !== false) { + return this.whenTrue.evaluate(options, meta); + } + + if (this.whenFalse != null) { + return this.whenFalse.evaluate(options, meta); + } + } + + toToken() : object { + const whenFalse : any = this.whenFalse; + return { + ...(super.toToken()), + condition: this.condition.toToken(), + whenTrue: this.whenTrue.toToken(), + whenFalse + } + } +} \ No newline at end of file diff --git a/src/tokens/logical/logical-and.ts b/src/tokens/logical/logical-and.ts index 3e3eb14..1ed32e1 100644 --- a/src/tokens/logical/logical-and.ts +++ b/src/tokens/logical/logical-and.ts @@ -11,10 +11,15 @@ export default class AndOperator extends LogicalToken { async evaluate(options: ParserOptions, meta?: any): Promise { if (this.right == null) { - return false; + // TODO - custom errors + throw new Error('TODO'); } - const left = await this.left.evaluate(options, meta); + let left = await this.left.evaluate(options, meta); + if (options.verifyOnly) { + await this.right.evaluate(options, meta); + return false; + } if (left == null || left === false || left === '' || left === 0) { return false; } diff --git a/src/tokens/logical/logical-not.ts b/src/tokens/logical/logical-not.ts index c08ac34..d804602 100644 --- a/src/tokens/logical/logical-not.ts +++ b/src/tokens/logical/logical-not.ts @@ -11,6 +11,11 @@ export default class NotOperator extends LogicalToken { async evaluate(options: ParserOptions, meta?: any): Promise { const value = await this.left.evaluate(options, meta); + + if (options.verifyOnly) { + return false; + } + return value != null && value !== false && value !== '' && value !== 0; } } \ No newline at end of file diff --git a/src/tokens/logical/logical-or.ts b/src/tokens/logical/logical-or.ts index d748590..5a87605 100644 --- a/src/tokens/logical/logical-or.ts +++ b/src/tokens/logical/logical-or.ts @@ -10,15 +10,21 @@ export default class OrOperator extends LogicalToken { } async evaluate(options: ParserOptions, meta?: any): Promise { - const left = await this.left.evaluate(options, meta); - if (left != null && left !== false && left !== '' && left !== 0) { - return true; + if (this.right == null) { + // TODO - custom errors + throw new Error('TODO'); } - if (this.right == null) { + const left = await this.left.evaluate(options, meta); + if (options.verifyOnly) { + await this.right.evaluate(options, meta); return false; } + if (left != null && left !== false && left !== '' && left !== 0) { + return true; + } + const right = await this.right.evaluate(options, meta); return right != null && right != false && right !== '' && right !== 0; } diff --git a/src/tokens/operator.ts b/src/tokens/operator.ts index dfe7844..f658391 100644 --- a/src/tokens/operator.ts +++ b/src/tokens/operator.ts @@ -21,11 +21,15 @@ export default class OperatorToken extends Token { } toToken() : object { - const right : any = this.right; - return { + + let result : Record = { ...(super.toToken()), - left: this.left, - right + left: this.left.toToken() + } + + if (this.right != null) { + result.right = this.right.toToken(); } + return result; } } \ No newline at end of file diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index a758aa0..61432ae 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -9,7 +9,7 @@ export interface ITokenList extends IToken { } export default class TokenList extends Token { - protected value : Token[]; + public value : Token[]; constructor(token: ITokenList) { super({ @@ -25,6 +25,10 @@ export default class TokenList extends Token { for (let idx = 0; idx < parts.length; idx += 1) { let value = await parts[idx].evaluate(options, meta); + if (options.verifyOnly) { + continue; + } + if (value === undefined) { continue; } diff --git a/src/types/options.ts b/src/types/options.ts index a8aa32d..a54601b 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -1,12 +1,11 @@ interface Handler { - argsCheck?: (...args: any[]) => any; - evaluator: (...args: any[]) => any; + argsCheck?: (meta: any, ...args: any[]) => any; + evaluator: (meta: any, ...args: any[]) => any; } -type LookupHandler = (name: string) => Handler; +type LookupHandler = (meta: any, name: string) => Handler; export interface ParserOptions { - conditionalHandlers: Record functionalHandlers: Record; verifyOnly?: boolean; skipArgumentsCheck?: boolean; diff --git a/src/types/token-types.ts b/src/types/token-types.ts index 09edb0e..fab3ec5 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -1,9 +1,9 @@ export enum TokenType { TOKENLIST, + IFSTATEMENT, + FUNCTIONAL, TEXT, ARGUMENT, - FUNCTIONAL, - CONDITIONAL, LOGICAL, COMPARISON, UNKNOWN From 34ff430eaaf80a8e75ba7e49deb512c3be81c933 Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 06:50:19 -0400 Subject: [PATCH 08/92] improve: export parseroptions and TokenType as defaults --- src/tokenize/tokenize.ts | 6 ++++++ src/tokens/base.ts | 4 +--- src/tokens/comparison/base.ts | 4 ++-- src/tokens/comparison/equal-loose.ts | 3 ++- src/tokens/comparison/equal-strict.ts | 2 +- src/tokens/comparison/exists.ts | 2 +- src/tokens/comparison/greater-than-equal.ts | 2 +- src/tokens/comparison/greater-than.ts | 2 +- src/tokens/comparison/less-than-equal.ts | 2 +- src/tokens/comparison/less-than.ts | 2 +- src/tokens/comparison/numerical.ts | 2 +- src/tokens/comparison/regex.ts | 2 +- src/tokens/comparison/wildcard.ts | 2 +- src/tokens/functional.ts | 4 ++-- src/tokens/if-statement.ts | 5 +++-- src/tokens/logical/base.ts | 3 +-- src/tokens/logical/logical-and.ts | 2 +- src/tokens/logical/logical-not.ts | 2 +- src/tokens/logical/logical-or.ts | 2 +- src/tokens/operator.ts | 2 +- src/tokens/token-list.ts | 5 +++-- src/types/options.ts | 4 +++- src/types/token-types.ts | 6 ++++-- 23 files changed, 40 insertions(+), 30 deletions(-) create mode 100644 src/tokenize/tokenize.ts diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts new file mode 100644 index 0000000..00d751f --- /dev/null +++ b/src/tokenize/tokenize.ts @@ -0,0 +1,6 @@ +import ParserOptions from '../types/options'; + +export default (options: ParserOptions, input: string, meta: any = {}) => { + + +} \ No newline at end of file diff --git a/src/tokens/base.ts b/src/tokens/base.ts index aaaa93f..e3108e4 100644 --- a/src/tokens/base.ts +++ b/src/tokens/base.ts @@ -1,6 +1,4 @@ -import { TokenType } from '../types/token-types'; - -import toText from '../helpers/to-text'; +import TokenType from '../types/token-types'; export interface IToken { type?: TokenType; diff --git a/src/tokens/comparison/base.ts b/src/tokens/comparison/base.ts index 5f947d4..6477ba2 100644 --- a/src/tokens/comparison/base.ts +++ b/src/tokens/comparison/base.ts @@ -1,5 +1,5 @@ -import { ParserOptions } from '../../types/options'; -import { TokenType } from '../../types/token-types'; +import ParserOptions from '../../types/options'; +import TokenType from '../../types/token-types'; import { default as OperatorToken, IOperatorToken } from '../operator'; diff --git a/src/tokens/comparison/equal-loose.ts b/src/tokens/comparison/equal-loose.ts index 560c92a..ed448eb 100644 --- a/src/tokens/comparison/equal-loose.ts +++ b/src/tokens/comparison/equal-loose.ts @@ -1,4 +1,5 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; + import { default as ComparisonToken, IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; import isPrimitive from '../../helpers/is-primitive'; diff --git a/src/tokens/comparison/equal-strict.ts b/src/tokens/comparison/equal-strict.ts index 5942cc2..08f30a2 100644 --- a/src/tokens/comparison/equal-strict.ts +++ b/src/tokens/comparison/equal-strict.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as ComparisonToken, IComparisonToken } from './base'; export default class EqualStrictToken extends ComparisonToken { diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts index 5d8fe53..1f05252 100644 --- a/src/tokens/comparison/exists.ts +++ b/src/tokens/comparison/exists.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as ComparisonToken, IComparisonToken } from './base'; export default class ExistsToken extends ComparisonToken { diff --git a/src/tokens/comparison/greater-than-equal.ts b/src/tokens/comparison/greater-than-equal.ts index 759f561..f294c7b 100644 --- a/src/tokens/comparison/greater-than-equal.ts +++ b/src/tokens/comparison/greater-than-equal.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as ComparisonToken, IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; diff --git a/src/tokens/comparison/greater-than.ts b/src/tokens/comparison/greater-than.ts index 00212e7..2a775ed 100644 --- a/src/tokens/comparison/greater-than.ts +++ b/src/tokens/comparison/greater-than.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as ComparisonToken, IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; diff --git a/src/tokens/comparison/less-than-equal.ts b/src/tokens/comparison/less-than-equal.ts index 274dfbd..5f147c6 100644 --- a/src/tokens/comparison/less-than-equal.ts +++ b/src/tokens/comparison/less-than-equal.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as ComparisonToken, IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; diff --git a/src/tokens/comparison/less-than.ts b/src/tokens/comparison/less-than.ts index 731aeb6..d1bd763 100644 --- a/src/tokens/comparison/less-than.ts +++ b/src/tokens/comparison/less-than.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as ComparisonToken, IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; diff --git a/src/tokens/comparison/numerical.ts b/src/tokens/comparison/numerical.ts index 2f6fadb..8dbc328 100644 --- a/src/tokens/comparison/numerical.ts +++ b/src/tokens/comparison/numerical.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as ComparisonToken, IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; diff --git a/src/tokens/comparison/regex.ts b/src/tokens/comparison/regex.ts index 164f9ff..3db0aeb 100644 --- a/src/tokens/comparison/regex.ts +++ b/src/tokens/comparison/regex.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as ComparisonToken, IComparisonToken } from './base'; import toText from '../../helpers/to-text'; diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts index 532a220..9dda219 100644 --- a/src/tokens/comparison/wildcard.ts +++ b/src/tokens/comparison/wildcard.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import split from '../../helpers/unicode-safe-split'; import toText from '../../helpers/to-text'; diff --git a/src/tokens/functional.ts b/src/tokens/functional.ts index bdc9abd..10f1508 100644 --- a/src/tokens/functional.ts +++ b/src/tokens/functional.ts @@ -1,5 +1,5 @@ -import { ParserOptions } from '../types/options'; -import { TokenType } from '../types/token-types'; +import TokenType from '../types/token-types'; +import ParserOptions from '../types/options'; import { default as Token, IToken } from './base'; import type { default as TokenList } from './token-list'; diff --git a/src/tokens/if-statement.ts b/src/tokens/if-statement.ts index 3ebf835..d9b5a0e 100644 --- a/src/tokens/if-statement.ts +++ b/src/tokens/if-statement.ts @@ -1,5 +1,6 @@ -import { ParserOptions } from '../types/options'; -import { TokenType } from '../types/token-types'; +import TokenType from '../types/token-types'; +import ParserOptions from '../types/options'; + import { default as Token, IToken } from './base'; import OperatorToken from './operator'; diff --git a/src/tokens/logical/base.ts b/src/tokens/logical/base.ts index bb091e2..930de4c 100644 --- a/src/tokens/logical/base.ts +++ b/src/tokens/logical/base.ts @@ -1,5 +1,4 @@ -import { TokenType } from '../../types/token-types'; -import { default as Token, IToken } from '../base'; +import TokenType from '../../types/token-types'; import { default as OperatorToken, IOperatorToken} from '../operator'; diff --git a/src/tokens/logical/logical-and.ts b/src/tokens/logical/logical-and.ts index 1ed32e1..659dbcc 100644 --- a/src/tokens/logical/logical-and.ts +++ b/src/tokens/logical/logical-and.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as LogicalToken, ILogicalToken } from './base'; export default class AndOperator extends LogicalToken { diff --git a/src/tokens/logical/logical-not.ts b/src/tokens/logical/logical-not.ts index d804602..5ea7a68 100644 --- a/src/tokens/logical/logical-not.ts +++ b/src/tokens/logical/logical-not.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as LogicalToken, ILogicalToken } from './base'; export default class NotOperator extends LogicalToken { diff --git a/src/tokens/logical/logical-or.ts b/src/tokens/logical/logical-or.ts index 5a87605..88ca5e6 100644 --- a/src/tokens/logical/logical-or.ts +++ b/src/tokens/logical/logical-or.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../../types/options'; +import ParserOptions from '../../types/options'; import { default as LogicalToken, ILogicalToken } from './base'; export default class OrOperator extends LogicalToken { diff --git a/src/tokens/operator.ts b/src/tokens/operator.ts index f658391..7fef411 100644 --- a/src/tokens/operator.ts +++ b/src/tokens/operator.ts @@ -1,4 +1,4 @@ -import { ParserOptions } from '../types/options'; +import ParserOptions from '../types/options'; import { default as Token, IToken } from './base'; export interface IOperatorToken extends IToken { diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index 61432ae..9409498 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -1,4 +1,5 @@ -import { TokenType } from '../types/token-types'; +import TokenType from '../types/token-types'; +import ParserOptions from '../types/options'; import toText from '../helpers/to-text'; @@ -18,7 +19,7 @@ export default class TokenList extends Token { }); } - async evaluate(options: any, meta?: any): Promise { + async evaluate(options: ParserOptions, meta?: any): Promise { const parts = this.value; let res : any; diff --git a/src/types/options.ts b/src/types/options.ts index a54601b..fd5f60e 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -5,8 +5,10 @@ interface Handler { type LookupHandler = (meta: any, name: string) => Handler; -export interface ParserOptions { +export default interface ParserOptions { functionalHandlers: Record; verifyOnly?: boolean; skipArgumentsCheck?: boolean; + + eol?: "error" | "ignore" | "consume" | "space" }; \ No newline at end of file diff --git a/src/types/token-types.ts b/src/types/token-types.ts index fab3ec5..216f086 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -1,4 +1,4 @@ -export enum TokenType { +enum TokenType { TOKENLIST, IFSTATEMENT, FUNCTIONAL, @@ -7,4 +7,6 @@ export enum TokenType { LOGICAL, COMPARISON, UNKNOWN -} \ No newline at end of file +}; + +export default TokenType; \ No newline at end of file From a162ed54a411ad3f913850e58907f70940abc332 Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 08:46:44 -0400 Subject: [PATCH 09/92] rebase: begin implementing tokenizer --- ...l-tokenizer.ts => get-potential-tokens.ts} | 8 +-- src/tokenize/text-escape-block.ts | 69 ++++++++++++++++++ src/tokenize/text-escape-single.ts | 33 +++++++++ src/tokenize/text-quoted.ts | 71 +++++++++++++++++++ src/tokenize/text-special.ts | 46 ++++++++++++ src/tokenize/tokenize.ts | 57 ++++++++++++++- src/tokens/base.ts | 6 +- src/tokens/text.ts | 1 + src/types/options.ts | 3 +- 9 files changed, 285 insertions(+), 9 deletions(-) rename src/helpers/{potential-tokenizer.ts => get-potential-tokens.ts} (95%) create mode 100644 src/tokenize/text-escape-block.ts create mode 100644 src/tokenize/text-escape-single.ts create mode 100644 src/tokenize/text-quoted.ts create mode 100644 src/tokenize/text-special.ts diff --git a/src/helpers/potential-tokenizer.ts b/src/helpers/get-potential-tokens.ts similarity index 95% rename from src/helpers/potential-tokenizer.ts rename to src/helpers/get-potential-tokens.ts index e765a12..6fda652 100644 --- a/src/helpers/potential-tokenizer.ts +++ b/src/helpers/get-potential-tokens.ts @@ -1,4 +1,4 @@ -import type { IBaseToken } from '../tokens/base'; +import type { IToken } from '../tokens/base'; import { HIGH_SURROGATE_START, @@ -18,14 +18,14 @@ import { } from './unicode-safe-split'; /** Split input string into array of potential tokens */ -export default (subject: string) : IBaseToken[] => { +export default (subject: string) : IToken[] => { if (typeof subject !== 'string') { throw new Error('string cannot be undefined or null') } - const result : IBaseToken[] = []; - let token : IBaseToken | null = null; + const result : IToken[] = []; + let token : IToken | null = null; let idx = 0; let inc = 0; diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts new file mode 100644 index 0000000..43302be --- /dev/null +++ b/src/tokenize/text-escape-block.ts @@ -0,0 +1,69 @@ +import ParserOptions from '../types/options'; +import type { TokenizeState } from './tokenize'; + +import TextToken from '../tokens/text'; +import TokenType from '../types/token-types'; +import Token from '../tokens/base'; + +export default ( + options: ParserOptions, + meta: any, + state: TokenizeState, +) : boolean => { + let { tokens, cursor, output } = state; + + if ( + cursor < (tokens.length - 2) || + `${tokens[cursor].value}${tokens[cursor + 1].value}` !== '``' + ) { + return false; + } + cursor += 2; + + const escTokens : Token[] = []; + while ( + cursor < (tokens.length - 2) && + `${tokens[cursor].value}${tokens[cursor + 1].value}` !== '``' + ) { + + /* TODO: Uncomment once tokenize* is implemented + const mockState = { + ...state, + cursor, + output: escTokens + }; + if ( + tokenizeFunctionIf(options, meta, mockState) || + tokenizeFunction(options, meta, mockState) + ) { + cursor = mockState.cursor; + continue; + } + */ + + if ( + escTokens.length === 0 || + escTokens[escTokens.length - 1].type != TokenType.TEXT + ) { + escTokens.push(new TextToken(tokens[cursor])); + + } else { + escTokens[escTokens.length - 1].value += tokens[cursor].value; + } + + cursor += 1; + } + + if ( + cursor > (tokens.length - 2) || + `${tokens[cursor].value}${tokens[cursor + 1].value}` !== '``' + ) { + // TODO - custom error + throw new Error('TODO'); + } + + output.push(...escTokens); + state.cursor = cursor + 2; + + return true; +} \ No newline at end of file diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts new file mode 100644 index 0000000..97e9875 --- /dev/null +++ b/src/tokenize/text-escape-single.ts @@ -0,0 +1,33 @@ +import type { TokenizeState } from './tokenize'; + +import TextToken from '../tokens/text'; +import TokenType from '../types/token-types'; + +export default ( + state: TokenizeState, + characters?: string[] +) : boolean => { + let { tokens, cursor, output } = state; + + if (characters == null) { + characters = ['\\', '$', '"', '`'] + } + + if ( + tokens[cursor].value !== '\\' || + (cursor += 1) === tokens.length || + characters?.findIndex(tokens[cursor].value) === -1 + ) { + return false; + } + + if (output.length > 0 && output[output.length -1].type === TokenType.TEXT) { + output[output.length - 1].value += tokens[cursor].value; + state.cursor = cursor + 1; + } else { + output.push(new TextToken(tokens[cursor])); + state.cursor = cursor + 1; + } + + return true; +} \ No newline at end of file diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts new file mode 100644 index 0000000..495cab1 --- /dev/null +++ b/src/tokenize/text-quoted.ts @@ -0,0 +1,71 @@ +import ParserOptions from '../types/options'; +import type { TokenizeState } from './tokenize'; + +import TextToken from '../tokens/text'; +import TokenType from '../types/token-types'; +import Token from '../tokens/base'; + +import tokenizeEscapeSingle from './text-escape-single'; +import tokenizeTextSpecial from './text-special'; + +export default ( + options: ParserOptions, + meta: any, + state: TokenizeState +) : boolean => { + let { tokens, cursor, output } = state; + + if ( + cursor < (tokens.length - 2) || + tokens[cursor].value !== '`' || + tokens[cursor + 1].value !== '`' + ) { + return false; + } + cursor += 2; + + const quoteTokens : Token[] = []; + while (cursor < (tokens.length - 1) && tokens[cursor].value !== '"') { + + const mockState = { + ...state, + cursor, + output: quoteTokens + }; + if ( + tokenizeEscapeSingle(mockState, ['\\', '"']) || + tokenizeTextSpecial(options, mockState) /* || + tokenizeFunctionIf(options, meta, mockState) || + tokenizeFunction(options, meta, mockState) + */ + ) { + cursor = mockState.cursor; + continue; + } + + if ( + quoteTokens.length === 0 || + quoteTokens[quoteTokens.length - 1].type != TokenType.TEXT + ) { + quoteTokens.push(new TextToken(tokens[cursor])); + + } else { + quoteTokens[quoteTokens.length - 1].value += tokens[cursor].value; + } + + cursor += 1; + } + + if ( + cursor > (tokens.length - 1) || + tokens[cursor + 1].value !== '"' + ) { + // TODO - custom error + throw new Error('TODO'); + } + + output.push(...quoteTokens); + state.cursor = cursor + 2; + + return true; +} \ No newline at end of file diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts new file mode 100644 index 0000000..d8973ea --- /dev/null +++ b/src/tokenize/text-special.ts @@ -0,0 +1,46 @@ +import ParserOptions from '../types/options'; +import type { TokenizeState } from './tokenize'; + +import TextToken from '../tokens/text'; +import TokenType from '../types/token-types'; + +import has from '../helpers/has'; + +export default ( + options: ParserOptions, + state: TokenizeState +) : boolean => { + if (!options.specialSequences) { + return false; + } + + let { tokens, cursor, output } = state; + + const characters = { + 'n': '\n', + 'r': '\r', + 't': '\t' + } + + if ( + tokens[cursor].value !== '\\' || + (cursor += 1) === tokens.length || + !has(characters, tokens[cursor].value) + ) { + return false; + } + + const value = characters[tokens[cursor].value]; + if (output.length > 0 && output[output.length -1].type === TokenType.TEXT) { + output[output.length - 1].value += value; + + } else { + output.push(new TextToken({ + ...(tokens[cursor]), + value + })); + } + + state.cursor = cursor + 1; + return true; +} \ No newline at end of file diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index 00d751f..8383d5b 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -1,6 +1,61 @@ import ParserOptions from '../types/options'; +import getPotentialTokens from '../helpers/get-potential-tokens'; +import { default as Token, IToken } from '../tokens/base'; +import TokenList, { ITokenList } from '../tokens/token-list'; -export default (options: ParserOptions, input: string, meta: any = {}) => { +import tokenizeTextEscapeSingle from './text-escape-single'; +import tokenizeTextEscapeBlock from './text-escape-block'; +import tokenizeTextQuoted from './text-quoted'; +import tokenizeTextSpecial from './text-special'; +import TokenType from '../types/token-types'; +import TextToken from '../tokens/text'; +export interface TokenizeState { + tokens: IToken[]; + cursor: number; + output: Token[]; +} +export default (subject: string, options: ParserOptions, meta: any = {}) : TokenList => { + const tokens = getPotentialTokens(subject); + + const state : TokenizeState = { + tokens, + cursor: 0, + output: [] + } + + while (state.cursor < tokens.length) { + if ( + tokenizeTextEscapeSingle(state) || + tokenizeTextEscapeBlock(options, meta, state) || + tokenizeTextQuoted(options, meta, state) || + tokenizeTextSpecial(options, state) /* || + tokenizeFunctionIf(options, meta, state) || + tokenizeFunction(options, meta, state) + */ + ) { + continue; + } + + const { tokens, output, cursor } = state; + + if (output.length === 0 || output[output.length - 1].type !== TokenType.TEXT) { + output.push(new TextToken(tokens[cursor])); + + } else { + output[output.length - 1].value += tokens[cursor].value; + } + + state.cursor += 1; + } + + if (state.cursor < tokens.length) { + // TODO - custom error; + throw new Error('TODO'); + } + + return new TokenList({ + value: state.output + }); } \ No newline at end of file diff --git a/src/tokens/base.ts b/src/tokens/base.ts index e3108e4..92b8068 100644 --- a/src/tokens/base.ts +++ b/src/tokens/base.ts @@ -8,9 +8,9 @@ export interface IToken { export default class Token { - protected type: TokenType; - protected position : number; - protected value : any; + public type: TokenType; + public position : number; + public value : any; constructor(token: IToken) { this.type = token.type == null ? TokenType.UNKNOWN : token.type; diff --git a/src/tokens/text.ts b/src/tokens/text.ts index 9923fb6..dcc8da9 100644 --- a/src/tokens/text.ts +++ b/src/tokens/text.ts @@ -1,6 +1,7 @@ import { default as Token, IToken } from './base'; export default class TextToken extends Token { + public value: string; constructor(token: IToken) { super(token); } diff --git a/src/types/options.ts b/src/types/options.ts index fd5f60e..a7deafe 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -10,5 +10,6 @@ export default interface ParserOptions { verifyOnly?: boolean; skipArgumentsCheck?: boolean; - eol?: "error" | "ignore" | "consume" | "space" + eol?: "error" | "ignore" | "consume" | "space"; + specialSequences?: boolean; }; \ No newline at end of file From eabdd39b0bef3274382bd52f281ded70dabfddda Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 09:58:30 -0400 Subject: [PATCH 10/92] feat: being functional implementation --- src/tokenize/function-if.ts | 0 src/tokenize/function.ts | 90 ++++++++++++++++++++++++++++++ src/tokenize/text-escape-block.ts | 32 +++++------ src/tokenize/text-escape-single.ts | 19 ++++--- src/tokenize/text-quoted.ts | 9 ++- src/tokenize/text-special.ts | 9 +-- src/tokenize/tokenize.ts | 16 ++++-- src/tokens/functional.ts | 9 ++- 8 files changed, 146 insertions(+), 38 deletions(-) create mode 100644 src/tokenize/function-if.ts create mode 100644 src/tokenize/function.ts diff --git a/src/tokenize/function-if.ts b/src/tokenize/function-if.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts new file mode 100644 index 0000000..3c8177e --- /dev/null +++ b/src/tokenize/function.ts @@ -0,0 +1,90 @@ +import ParserOptions from '../types/options'; + +import has from '../helpers/has'; + +import type { TokenizeState } from './tokenize'; + +import TokenList from '../tokens/token-list'; +import FunctionalToken from '../tokens/functional'; + +// import tokenizeArguments from './arguments; + +const nameCheck = /^([a-z][a-z\d]{2,})$/i; + +export default ( + options: ParserOptions, + meta: any, + state: TokenizeState +) : boolean => { + + let { tokens, cursor, output } = state; + + if ( + cursor + 4 >= tokens.length || + tokens[cursor].value !== '$' + ) { + return false; + } + + const varPosition = tokens[cursor].position; + + cursor += 1; + + let prefix = '$'; + if (has(options.functionalHandlers, '$' + tokens[cursor].value[0])) { + + let {position, value} = tokens[cursor].value; + prefix += value[0]; + + if (value.length > 1) { + tokens = tokens.slice(); + tokens[cursor] = { + position, + value: value[0] + }; + + tokens.splice(cursor + 1, 0, { + position: position += 1, + value: (value).slice(1) + }); + cursor += 1; + } + cursor += 1; + } + + if (!nameCheck.test(tokens[cursor].value)) { + return false; + } + + const varName = tokens[cursor].value; + cursor += 1; + + const varArguments : TokenList[] = []; + const mockState = { + tokens, + cursor, + output: varArguments + } + + /* TODO - Uncomment once argument tokenizer is implemented + if (tokenizeArguments(options, meta, mockState)) { + tokens = mockState.tokens; + cursor = mockState.cursor; + } + */ + + output.push(new FunctionalToken({ + position: varPosition, + prefix, + value: varName, + arguments: new TokenList({ + position: cursor, + value: varArguments + }) + })); + + state.tokens = tokens; + state.cursor = cursor; + + return true; +}; \ No newline at end of file diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index 43302be..f2dc182 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -5,6 +5,9 @@ import TextToken from '../tokens/text'; import TokenType from '../types/token-types'; import Token from '../tokens/base'; +// import tokenizeFunctionIf from './function-if; +import tokenizeFunction from './function'; + export default ( options: ParserOptions, meta: any, @@ -12,34 +15,34 @@ export default ( ) : boolean => { let { tokens, cursor, output } = state; - if ( - cursor < (tokens.length - 2) || - `${tokens[cursor].value}${tokens[cursor + 1].value}` !== '``' - ) { + if (tokens[cursor].value !== '``') { return false; } - cursor += 2; + cursor += 1; + + if (cursor < (tokens.length - 1)) { + // TODO - custom error + throw new Error('TODO'); + } const escTokens : Token[] = []; - while ( - cursor < (tokens.length - 2) && - `${tokens[cursor].value}${tokens[cursor + 1].value}` !== '``' - ) { + while (cursor < tokens.length && tokens[cursor].value !== '``') { - /* TODO: Uncomment once tokenize* is implemented const mockState = { ...state, cursor, output: escTokens }; + if ( + /* TODO: Uncomment once tokenize* is implemented tokenizeFunctionIf(options, meta, mockState) || + */ tokenizeFunction(options, meta, mockState) ) { cursor = mockState.cursor; continue; } - */ if ( escTokens.length === 0 || @@ -54,16 +57,13 @@ export default ( cursor += 1; } - if ( - cursor > (tokens.length - 2) || - `${tokens[cursor].value}${tokens[cursor + 1].value}` !== '``' - ) { + if (tokens[cursor].value !== '``') { // TODO - custom error throw new Error('TODO'); } output.push(...escTokens); - state.cursor = cursor + 2; + state.cursor = cursor + 1; return true; } \ No newline at end of file diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index 97e9875..d26649b 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -13,21 +13,26 @@ export default ( characters = ['\\', '$', '"', '`'] } + const value = tokens[cursor].value; + if ( - tokens[cursor].value !== '\\' || - (cursor += 1) === tokens.length || - characters?.findIndex(tokens[cursor].value) === -1 + value[0] !== '\\' || + (value[1] == null || value[1] === '') || + characters.findIndex(value[1]) !== -1 ) { return false; } if (output.length > 0 && output[output.length -1].type === TokenType.TEXT) { - output[output.length - 1].value += tokens[cursor].value; - state.cursor = cursor + 1; + output[output.length - 1].value += value[1]; + } else { - output.push(new TextToken(tokens[cursor])); - state.cursor = cursor + 1; + output.push(new TextToken({ + ...(tokens[cursor]), + value: value[1] + })); } + state.cursor = cursor + 1; return true; } \ No newline at end of file diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 495cab1..91c2e29 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -7,6 +7,8 @@ import Token from '../tokens/base'; import tokenizeEscapeSingle from './text-escape-single'; import tokenizeTextSpecial from './text-special'; +// import tokenizeFunctionIf from './function-if; +import tokenizeFunction from './function'; export default ( options: ParserOptions, @@ -34,10 +36,13 @@ export default ( }; if ( tokenizeEscapeSingle(mockState, ['\\', '"']) || - tokenizeTextSpecial(options, mockState) /* || + tokenizeTextSpecial(options, mockState) || + + /* TODO - uncomment once implemented tokenizeFunctionIf(options, meta, mockState) || - tokenizeFunction(options, meta, mockState) */ + + tokenizeFunction(options, meta, mockState) ) { cursor = mockState.cursor; continue; diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index d8973ea..ee4271b 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -22,15 +22,16 @@ export default ( 't': '\t' } + let value = tokens[cursor].value; if ( - tokens[cursor].value !== '\\' || - (cursor += 1) === tokens.length || - !has(characters, tokens[cursor].value) + value[0] !== '\\' || + (value[1] == null || value[1] === '') || + !has(characters, value[1]) ) { return false; } - const value = characters[tokens[cursor].value]; + value = characters[value[1]]; if (output.length > 0 && output[output.length -1].type === TokenType.TEXT) { output[output.length - 1].value += value; diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index 8383d5b..bc5c413 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -1,14 +1,20 @@ import ParserOptions from '../types/options'; +import TokenType from '../types/token-types'; + import getPotentialTokens from '../helpers/get-potential-tokens'; + import { default as Token, IToken } from '../tokens/base'; import TokenList, { ITokenList } from '../tokens/token-list'; +import TextToken from '../tokens/text'; import tokenizeTextEscapeSingle from './text-escape-single'; import tokenizeTextEscapeBlock from './text-escape-block'; import tokenizeTextQuoted from './text-quoted'; import tokenizeTextSpecial from './text-special'; -import TokenType from '../types/token-types'; -import TextToken from '../tokens/text'; +// import tokenizeFunctionIf from './function-if; +import tokenizeFunction from './function'; + + export interface TokenizeState { tokens: IToken[]; @@ -30,10 +36,12 @@ export default (subject: string, options: ParserOptions, meta: any = {}) : Token tokenizeTextEscapeSingle(state) || tokenizeTextEscapeBlock(options, meta, state) || tokenizeTextQuoted(options, meta, state) || - tokenizeTextSpecial(options, state) /* || + tokenizeTextSpecial(options, state) || + /* + TODO - Uncomment once implemented tokenizeFunctionIf(options, meta, state) || - tokenizeFunction(options, meta, state) */ + tokenizeFunction(options, meta, state) ) { continue; } diff --git a/src/tokens/functional.ts b/src/tokens/functional.ts index 10f1508..b65b9f1 100644 --- a/src/tokens/functional.ts +++ b/src/tokens/functional.ts @@ -1,7 +1,7 @@ import TokenType from '../types/token-types'; import ParserOptions from '../types/options'; -import { default as Token, IToken } from './base'; -import type { default as TokenList } from './token-list'; +import Token, { IToken } from './base'; +import type TokenList from './token-list'; export interface IFunctionalToken extends IToken { prefix: string; @@ -9,9 +9,8 @@ export interface IFunctionalToken extends IToken { } export default class FunctionalToken extends Token { - protected prefix: string; - protected value: string; - protected arguments: TokenList; + public prefix: string; + public arguments: TokenList; constructor(token: IFunctionalToken) { super({ From f5d55c4706f0f422a8f8b20aefcac1701e9e1308 Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 13 Sep 2022 10:07:35 -0400 Subject: [PATCH 11/92] chore: adjust default import statements --- src/tokenize/tokenize.ts | 2 +- src/tokens/comparison/base.ts | 2 +- src/tokens/comparison/equal-loose.ts | 2 +- src/tokens/comparison/equal-strict.ts | 3 ++- src/tokens/comparison/exists.ts | 3 ++- src/tokens/comparison/greater-than-equal.ts | 4 +++- src/tokens/comparison/greater-than.ts | 2 +- src/tokens/comparison/less-than-equal.ts | 2 +- src/tokens/comparison/less-than.ts | 2 +- src/tokens/comparison/numerical.ts | 2 +- src/tokens/comparison/regex.ts | 2 +- src/tokens/comparison/wildcard.ts | 2 +- src/tokens/if-statement.ts | 2 +- src/tokens/operator.ts | 2 +- src/tokens/text.ts | 2 +- src/tokens/token-list.ts | 2 +- src/types/operator-types.ts | 5 ----- 17 files changed, 20 insertions(+), 21 deletions(-) delete mode 100644 src/types/operator-types.ts diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index bc5c413..864ca53 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -3,7 +3,7 @@ import TokenType from '../types/token-types'; import getPotentialTokens from '../helpers/get-potential-tokens'; -import { default as Token, IToken } from '../tokens/base'; +import Token, { IToken } from '../tokens/base'; import TokenList, { ITokenList } from '../tokens/token-list'; import TextToken from '../tokens/text'; diff --git a/src/tokens/comparison/base.ts b/src/tokens/comparison/base.ts index 6477ba2..3d458fd 100644 --- a/src/tokens/comparison/base.ts +++ b/src/tokens/comparison/base.ts @@ -1,7 +1,7 @@ import ParserOptions from '../../types/options'; import TokenType from '../../types/token-types'; -import { default as OperatorToken, IOperatorToken } from '../operator'; +import OperatorToken, { IOperatorToken } from '../operator'; export interface IComparisonToken extends IOperatorToken { invert?: boolean; diff --git a/src/tokens/comparison/equal-loose.ts b/src/tokens/comparison/equal-loose.ts index ed448eb..0d6ba59 100644 --- a/src/tokens/comparison/equal-loose.ts +++ b/src/tokens/comparison/equal-loose.ts @@ -1,6 +1,6 @@ import ParserOptions from '../../types/options'; -import { default as ComparisonToken, IComparisonToken } from './base'; +import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; import isPrimitive from '../../helpers/is-primitive'; diff --git a/src/tokens/comparison/equal-strict.ts b/src/tokens/comparison/equal-strict.ts index 08f30a2..403506d 100644 --- a/src/tokens/comparison/equal-strict.ts +++ b/src/tokens/comparison/equal-strict.ts @@ -1,5 +1,6 @@ import ParserOptions from '../../types/options'; -import { default as ComparisonToken, IComparisonToken } from './base'; + +import ComparisonToken, { IComparisonToken } from './base'; export default class EqualStrictToken extends ComparisonToken { constructor(token: IComparisonToken) { diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts index 1f05252..a71f9ad 100644 --- a/src/tokens/comparison/exists.ts +++ b/src/tokens/comparison/exists.ts @@ -1,5 +1,6 @@ import ParserOptions from '../../types/options'; -import { default as ComparisonToken, IComparisonToken } from './base'; + +import ComparisonToken, { IComparisonToken } from './base'; export default class ExistsToken extends ComparisonToken { constructor(token: IComparisonToken) { diff --git a/src/tokens/comparison/greater-than-equal.ts b/src/tokens/comparison/greater-than-equal.ts index f294c7b..0e1821b 100644 --- a/src/tokens/comparison/greater-than-equal.ts +++ b/src/tokens/comparison/greater-than-equal.ts @@ -1,5 +1,7 @@ import ParserOptions from '../../types/options'; -import { default as ComparisonToken, IComparisonToken } from './base'; + +import ComparisonToken, { IComparisonToken } from './base'; + import toNumber from '../../helpers/to-number'; export default class GreaterThanEqualToken extends ComparisonToken { diff --git a/src/tokens/comparison/greater-than.ts b/src/tokens/comparison/greater-than.ts index 2a775ed..3914276 100644 --- a/src/tokens/comparison/greater-than.ts +++ b/src/tokens/comparison/greater-than.ts @@ -1,5 +1,5 @@ import ParserOptions from '../../types/options'; -import { default as ComparisonToken, IComparisonToken } from './base'; +import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; export default class GreaterThanToken extends ComparisonToken { diff --git a/src/tokens/comparison/less-than-equal.ts b/src/tokens/comparison/less-than-equal.ts index 5f147c6..7de3c2a 100644 --- a/src/tokens/comparison/less-than-equal.ts +++ b/src/tokens/comparison/less-than-equal.ts @@ -1,5 +1,5 @@ import ParserOptions from '../../types/options'; -import { default as ComparisonToken, IComparisonToken } from './base'; +import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; export default class LessThanEqualToken extends ComparisonToken { diff --git a/src/tokens/comparison/less-than.ts b/src/tokens/comparison/less-than.ts index d1bd763..26c19c1 100644 --- a/src/tokens/comparison/less-than.ts +++ b/src/tokens/comparison/less-than.ts @@ -1,5 +1,5 @@ import ParserOptions from '../../types/options'; -import { default as ComparisonToken, IComparisonToken } from './base'; +import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; export default class LessThanToken extends ComparisonToken { diff --git a/src/tokens/comparison/numerical.ts b/src/tokens/comparison/numerical.ts index 8dbc328..bc91c4e 100644 --- a/src/tokens/comparison/numerical.ts +++ b/src/tokens/comparison/numerical.ts @@ -1,5 +1,5 @@ import ParserOptions from '../../types/options'; -import { default as ComparisonToken, IComparisonToken } from './base'; +import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; const isRange = /^((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))-((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))$/; diff --git a/src/tokens/comparison/regex.ts b/src/tokens/comparison/regex.ts index 3db0aeb..2e528eb 100644 --- a/src/tokens/comparison/regex.ts +++ b/src/tokens/comparison/regex.ts @@ -1,5 +1,5 @@ import ParserOptions from '../../types/options'; -import { default as ComparisonToken, IComparisonToken } from './base'; +import ComparisonToken, { IComparisonToken } from './base'; import toText from '../../helpers/to-text'; export default class LessThanToken extends ComparisonToken { diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts index 9dda219..3dcda05 100644 --- a/src/tokens/comparison/wildcard.ts +++ b/src/tokens/comparison/wildcard.ts @@ -3,7 +3,7 @@ import ParserOptions from '../../types/options'; import split from '../../helpers/unicode-safe-split'; import toText from '../../helpers/to-text'; -import { default as ComparisonToken, IComparisonToken } from './base'; +import ComparisonToken, { IComparisonToken } from './base'; const toRegExp = (subject: string, caseSensitive: boolean = false) : RegExp => { let wc = split(subject); diff --git a/src/tokens/if-statement.ts b/src/tokens/if-statement.ts index d9b5a0e..7c7526a 100644 --- a/src/tokens/if-statement.ts +++ b/src/tokens/if-statement.ts @@ -1,7 +1,7 @@ import TokenType from '../types/token-types'; import ParserOptions from '../types/options'; -import { default as Token, IToken } from './base'; +import Token, { IToken } from './base'; import OperatorToken from './operator'; export interface IIfStatementToken extends IToken { diff --git a/src/tokens/operator.ts b/src/tokens/operator.ts index 7fef411..8e8cbef 100644 --- a/src/tokens/operator.ts +++ b/src/tokens/operator.ts @@ -1,5 +1,5 @@ import ParserOptions from '../types/options'; -import { default as Token, IToken } from './base'; +import Token, { IToken } from './base'; export interface IOperatorToken extends IToken { left: Token, diff --git a/src/tokens/text.ts b/src/tokens/text.ts index dcc8da9..35ea02b 100644 --- a/src/tokens/text.ts +++ b/src/tokens/text.ts @@ -1,4 +1,4 @@ -import { default as Token, IToken } from './base'; +import Token, { IToken } from './base'; export default class TextToken extends Token { public value: string; diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index 9409498..a7972d2 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -3,7 +3,7 @@ import ParserOptions from '../types/options'; import toText from '../helpers/to-text'; -import { default as Token, IToken } from './base'; +import Token, { IToken } from './base'; export interface ITokenList extends IToken { value: Token[]; diff --git a/src/types/operator-types.ts b/src/types/operator-types.ts deleted file mode 100644 index 07f29fa..0000000 --- a/src/types/operator-types.ts +++ /dev/null @@ -1,5 +0,0 @@ -enum operatorTypes { - COMPARISON, - LOGICAL -} -export default operatorTypes; \ No newline at end of file From c9bbfdf258c3f97a1d5c2ca15d489bc7abe418dd Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 14 Sep 2022 02:10:25 -0400 Subject: [PATCH 12/92] dev: continuing --- src/helpers/remove-whitespace.ts | 9 --- src/tokenize/argument-list.ts | 74 +++++++++++++++++++++ src/tokenize/argument.ts | 12 ++++ src/tokenize/function.ts | 19 ++---- src/tokenize/text-escape-block.ts | 2 - src/tokenize/text-escape-single.ts | 1 - src/tokenize/text-quoted.ts | 4 +- src/tokenize/text-special.ts | 2 - src/tokenize/tokenize.ts | 7 +- src/tokens/comparison/equal-loose.ts | 1 - src/tokens/comparison/equal-strict.ts | 3 +- src/tokens/comparison/exists.ts | 1 - src/tokens/comparison/greater-than-equal.ts | 4 +- src/tokens/comparison/greater-than.ts | 2 +- src/tokens/comparison/less-than-equal.ts | 2 +- src/tokens/comparison/less-than.ts | 2 +- src/tokens/comparison/regex.ts | 2 +- src/tokens/comparison/wildcard.ts | 6 +- src/tokens/functional.ts | 9 ++- src/types/options.ts | 2 +- 20 files changed, 106 insertions(+), 58 deletions(-) delete mode 100644 src/helpers/remove-whitespace.ts create mode 100644 src/tokenize/argument-list.ts create mode 100644 src/tokenize/argument.ts diff --git a/src/helpers/remove-whitespace.ts b/src/helpers/remove-whitespace.ts deleted file mode 100644 index 03218f8..0000000 --- a/src/helpers/remove-whitespace.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { IBaseToken } from '../tokens/base'; - -export default (tokens: IBaseToken[]) : string => { - let result = ''; - while (tokens.length && tokens[0].value === ' ') { - result += (tokens.shift()).value; - } - return result; -}; \ No newline at end of file diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts new file mode 100644 index 0000000..fda8c4d --- /dev/null +++ b/src/tokenize/argument-list.ts @@ -0,0 +1,74 @@ +import ParserOptions from '../types/options'; +import { TokenizeState } from "./tokenize"; +import Token from '../tokens/base'; +import TokenList from '../tokens/token-list'; +import tokenizeArgument from './argument'; + +export default (options: ParserOptions, meta: any, state: TokenizeState) : boolean => { + + let { tokens, cursor } = state; + + if (tokens[cursor].value !== '[') { + return false; + } + cursor += 1; + + let args : TokenList[] = []; + + while ( + cursor < tokens.length && + tokens[cursor].value != ']' + ) { + + // consume leading whitespace + while (tokens[cursor].value === ' ') { + cursor += 1; + } + + let argParts : Token[] = []; + const mockState = { + tokens, + cursor, + output: argParts + }; + + tokenizeArgument(options, meta, mockState); + + const next = mockState.tokens[mockState.cursor].value; + if (next == null) { + // TODO - custom error - Syntax Error: unexpected end + throw new Error('TODO - Syntax Error: Unexpected end'); + } + + if ( + next !== ',' && + next !== ']' + ) { + // TODO - custom error - Syntax Error: Illegal token + throw new Error('TODO - Syntax Error: Illegal Token') + } + + args.push(new TokenList({ + position: cursor, + value: argParts + })); + tokens = mockState.tokens; + cursor = mockState.cursor; + + + if (next === ',') { + cursor += 1; + } + } + + if (tokens[cursor].value !== ']') { + // TODO - custom error - SyntaxError: Expected ']' + throw new Error('TODO - Syntax Error: Expected \']\''); + } + + state.tokens = tokens; + state.cursor = cursor + 1; + state.output.push(...args); + + return true; +}; \ No newline at end of file diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts new file mode 100644 index 0000000..2627529 --- /dev/null +++ b/src/tokenize/argument.ts @@ -0,0 +1,12 @@ +import Token from '../tokens/base'; +import ParserOptions from '../types/options'; +import { TokenizeState } from './tokenize'; + +export default (options: ParserOptions, meta: any, state: TokenizeState) : boolean => { + let { tokens, cursor, output } = state; + + let whitespace = ''; + let tokens : Token[] = [] + + return false; +} \ No newline at end of file diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index 3c8177e..ebb46f7 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -1,13 +1,9 @@ import ParserOptions from '../types/options'; - import has from '../helpers/has'; - import type { TokenizeState } from './tokenize'; - -import TokenList from '../tokens/token-list'; +import type Token from '../tokens/base'; import FunctionalToken from '../tokens/functional'; - -// import tokenizeArguments from './arguments; +import tokenizeArgumentList from './argument-list'; const nameCheck = /^([a-z][a-z\d]{2,})$/i; @@ -59,28 +55,23 @@ export default ( const varName = tokens[cursor].value; cursor += 1; - const varArguments : TokenList[] = []; + const varArguments : Token[] = []; const mockState = { tokens, cursor, output: varArguments } - /* TODO - Uncomment once argument tokenizer is implemented - if (tokenizeArguments(options, meta, mockState)) { + if (tokenizeArgumentList(options, meta, mockState)) { tokens = mockState.tokens; cursor = mockState.cursor; } - */ output.push(new FunctionalToken({ position: varPosition, prefix, value: varName, - arguments: new TokenList({ - position: cursor, - value: varArguments - }) + arguments: varArguments })); state.tokens = tokens; diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index f2dc182..e9f5f58 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -1,10 +1,8 @@ import ParserOptions from '../types/options'; import type { TokenizeState } from './tokenize'; - import TextToken from '../tokens/text'; import TokenType from '../types/token-types'; import Token from '../tokens/base'; - // import tokenizeFunctionIf from './function-if; import tokenizeFunction from './function'; diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index d26649b..4ab33f0 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -1,5 +1,4 @@ import type { TokenizeState } from './tokenize'; - import TextToken from '../tokens/text'; import TokenType from '../types/token-types'; diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 91c2e29..3f9ce86 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -1,10 +1,8 @@ import ParserOptions from '../types/options'; import type { TokenizeState } from './tokenize'; - import TextToken from '../tokens/text'; import TokenType from '../types/token-types'; import Token from '../tokens/base'; - import tokenizeEscapeSingle from './text-escape-single'; import tokenizeTextSpecial from './text-special'; // import tokenizeFunctionIf from './function-if; @@ -66,7 +64,7 @@ export default ( tokens[cursor + 1].value !== '"' ) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Syntax Error: expected closing quote'); } output.push(...quoteTokens); diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index ee4271b..b125f21 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -1,9 +1,7 @@ import ParserOptions from '../types/options'; import type { TokenizeState } from './tokenize'; - import TextToken from '../tokens/text'; import TokenType from '../types/token-types'; - import has from '../helpers/has'; export default ( diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index 864ca53..f7d5ce4 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -1,12 +1,9 @@ import ParserOptions from '../types/options'; import TokenType from '../types/token-types'; - import getPotentialTokens from '../helpers/get-potential-tokens'; - import Token, { IToken } from '../tokens/base'; import TokenList, { ITokenList } from '../tokens/token-list'; import TextToken from '../tokens/text'; - import tokenizeTextEscapeSingle from './text-escape-single'; import tokenizeTextEscapeBlock from './text-escape-block'; import tokenizeTextQuoted from './text-quoted'; @@ -14,8 +11,6 @@ import tokenizeTextSpecial from './text-special'; // import tokenizeFunctionIf from './function-if; import tokenizeFunction from './function'; - - export interface TokenizeState { tokens: IToken[]; cursor: number; @@ -60,7 +55,7 @@ export default (subject: string, options: ParserOptions, meta: any = {}) : Token if (state.cursor < tokens.length) { // TODO - custom error; - throw new Error('TODO'); + throw new Error('TODO - Syntax Error: Unexpected token'); } return new TokenList({ diff --git a/src/tokens/comparison/equal-loose.ts b/src/tokens/comparison/equal-loose.ts index 0d6ba59..f19839e 100644 --- a/src/tokens/comparison/equal-loose.ts +++ b/src/tokens/comparison/equal-loose.ts @@ -1,5 +1,4 @@ import ParserOptions from '../../types/options'; - import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; import isPrimitive from '../../helpers/is-primitive'; diff --git a/src/tokens/comparison/equal-strict.ts b/src/tokens/comparison/equal-strict.ts index 403506d..35419a4 100644 --- a/src/tokens/comparison/equal-strict.ts +++ b/src/tokens/comparison/equal-strict.ts @@ -1,5 +1,4 @@ import ParserOptions from '../../types/options'; - import ComparisonToken, { IComparisonToken } from './base'; export default class EqualStrictToken extends ComparisonToken { @@ -13,7 +12,7 @@ export default class EqualStrictToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Evaluation Error: Right hand argument missing'); } const v1 = await this.left.evaluate(options, meta); diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts index a71f9ad..9b8689f 100644 --- a/src/tokens/comparison/exists.ts +++ b/src/tokens/comparison/exists.ts @@ -1,5 +1,4 @@ import ParserOptions from '../../types/options'; - import ComparisonToken, { IComparisonToken } from './base'; export default class ExistsToken extends ComparisonToken { diff --git a/src/tokens/comparison/greater-than-equal.ts b/src/tokens/comparison/greater-than-equal.ts index 0e1821b..b1e24cc 100644 --- a/src/tokens/comparison/greater-than-equal.ts +++ b/src/tokens/comparison/greater-than-equal.ts @@ -1,7 +1,5 @@ import ParserOptions from '../../types/options'; - import ComparisonToken, { IComparisonToken } from './base'; - import toNumber from '../../helpers/to-number'; export default class GreaterThanEqualToken extends ComparisonToken { @@ -15,7 +13,7 @@ export default class GreaterThanEqualToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Evaluation Error: Right hand argument missing'); } let v1 = await this.left.evaluate(options, meta); diff --git a/src/tokens/comparison/greater-than.ts b/src/tokens/comparison/greater-than.ts index 3914276..94ee0fd 100644 --- a/src/tokens/comparison/greater-than.ts +++ b/src/tokens/comparison/greater-than.ts @@ -13,7 +13,7 @@ export default class GreaterThanToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Evaluation Error: Right hand argument missing'); } let v1 = await this.left.evaluate(options, meta); diff --git a/src/tokens/comparison/less-than-equal.ts b/src/tokens/comparison/less-than-equal.ts index 7de3c2a..d7541f4 100644 --- a/src/tokens/comparison/less-than-equal.ts +++ b/src/tokens/comparison/less-than-equal.ts @@ -13,7 +13,7 @@ export default class LessThanEqualToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Evaluation Error: Right hand argument missing'); } let v1 = await this.left.evaluate(options, meta); diff --git a/src/tokens/comparison/less-than.ts b/src/tokens/comparison/less-than.ts index 26c19c1..db82016 100644 --- a/src/tokens/comparison/less-than.ts +++ b/src/tokens/comparison/less-than.ts @@ -13,7 +13,7 @@ export default class LessThanToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Evaluation Error: Right hand argument missing'); } let v1 = await this.left.evaluate(options, meta); diff --git a/src/tokens/comparison/regex.ts b/src/tokens/comparison/regex.ts index 2e528eb..1fe491c 100644 --- a/src/tokens/comparison/regex.ts +++ b/src/tokens/comparison/regex.ts @@ -13,7 +13,7 @@ export default class LessThanToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Evaluation Error: Right hand argument missing'); } let v1 = await this.left.evaluate(options, meta); diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts index 3dcda05..27a2f7b 100644 --- a/src/tokens/comparison/wildcard.ts +++ b/src/tokens/comparison/wildcard.ts @@ -1,8 +1,6 @@ import ParserOptions from '../../types/options'; - import split from '../../helpers/unicode-safe-split'; import toText from '../../helpers/to-text'; - import ComparisonToken, { IComparisonToken } from './base'; const toRegExp = (subject: string, caseSensitive: boolean = false) : RegExp => { @@ -111,7 +109,7 @@ export default class WildcardToken extends ComparisonToken { async handle(options: ParserOptions, meta?: any): Promise { if (this.right == null) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Evaluation Error: Right hand argument missing'); } let v1 = await this.left.evaluate(options, meta); @@ -144,7 +142,7 @@ export default class WildcardToken extends ComparisonToken { if (this.right == null) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Evaluation Error: Right hand argument missing'); } let v1 = await this.left.evaluate(options, meta); diff --git a/src/tokens/functional.ts b/src/tokens/functional.ts index b65b9f1..2e26e8e 100644 --- a/src/tokens/functional.ts +++ b/src/tokens/functional.ts @@ -1,16 +1,15 @@ import TokenType from '../types/token-types'; import ParserOptions from '../types/options'; import Token, { IToken } from './base'; -import type TokenList from './token-list'; export interface IFunctionalToken extends IToken { prefix: string; - arguments: TokenList + arguments: Token[] } export default class FunctionalToken extends Token { public prefix: string; - public arguments: TokenList; + public arguments: Token[]; constructor(token: IFunctionalToken) { super({ @@ -37,7 +36,7 @@ export default class FunctionalToken extends Token { let args : any[] = []; if (this.arguments != null) { - const argList = this.arguments.value; + const argList = this.arguments; for (let idx = 0; idx < argList.length; idx += 1) { const arg = await argList[idx].evaluate(options, meta); args.push(arg); @@ -71,7 +70,7 @@ export default class FunctionalToken extends Token { return { ...(super.toToken()), prefix: this.prefix, - arguments: this.arguments.toToken() + arguments: this.arguments.map(value => value.toToken()) } } } \ No newline at end of file diff --git a/src/types/options.ts b/src/types/options.ts index a7deafe..5c6768b 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -10,6 +10,6 @@ export default interface ParserOptions { verifyOnly?: boolean; skipArgumentsCheck?: boolean; - eol?: "error" | "ignore" | "consume" | "space"; + eol?: 'error' | 'ignore' | 'consume' | 'space'; specialSequences?: boolean; }; \ No newline at end of file From 4174a791fbb745b0e4f1ee5f3b7ac583ca8c7fc2 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 14 Sep 2022 08:32:21 -0400 Subject: [PATCH 13/92] everything up to $if[] done --- .gitignore | 3 +- package-lock.json | 1613 ++++++++++++++++++++++++++- package.json | 8 +- src/helpers/get-potential-tokens.ts | 183 ++- src/helpers/unicode-safe-split.ts | 15 +- src/index.ts | 1 + src/tokenize/argument-list.ts | 70 +- src/tokenize/argument.ts | 144 ++- src/tokenize/function-if.ts | 9 + src/tokenize/function.ts | 39 +- src/tokenize/text-escape-block.ts | 32 +- src/tokenize/text-escape-single.ts | 16 +- src/tokenize/text-quoted.ts | 82 +- src/tokenize/text-special.ts | 23 +- src/tokenize/tokenize.ts | 81 +- src/tokens/functional.ts | 2 + src/tokens/token-list.ts | 5 +- src/types/options.ts | 2 +- src/types/token-types.ts | 1 + tsconfig.json | 20 + 20 files changed, 2080 insertions(+), 269 deletions(-) create mode 100644 src/index.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 40b878d..c18ed01 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +node_modules/ +lib/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0bc7488..d5606cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,1604 @@ { "name": "expressionish", "version": "0.0.1", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "expressionish", + "version": "0.0.1", + "license": "ISC", + "devDependencies": { + "@types/node": "^18.7.18", + "eslint": "^8.4.1", + "mocha": "^9.1.3", + "typescript": "^4.8.3" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.2.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", + "dev": true + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.2.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", + "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/espree": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "dev": true, + "dependencies": { + "acorn": "^8.6.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, "dependencies": { "@eslint/eslintrc": { "version": "1.0.5", @@ -38,6 +1634,12 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@types/node": { + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", + "dev": true + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -54,7 +1656,8 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "ajv": { "version": "6.12.6", @@ -1042,6 +2645,12 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "typescript": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index ad6533b..3e2705a 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,15 @@ "private": true, "license": "ISC", "type": "commonjs", - "main": "./src/evaluate.js", + "main": "./lib/index.js", "scripts": { "lint": "eslint --fix ./src/** ./test/**", "test": "mocha" }, "devDependencies": { + "@types/node": "^18.7.18", "eslint": "^8.4.1", - "mocha": "^9.1.3" + "mocha": "^9.1.3", + "typescript": "^4.8.3" } -} +} \ No newline at end of file diff --git a/src/helpers/get-potential-tokens.ts b/src/helpers/get-potential-tokens.ts index 6fda652..a4d322e 100644 --- a/src/helpers/get-potential-tokens.ts +++ b/src/helpers/get-potential-tokens.ts @@ -1,124 +1,101 @@ -import type { IToken } from '../tokens/base'; +import { type IToken } from '../tokens/base'; +import ParserOptions from '../types/options'; -import { - HIGH_SURROGATE_START, - HIGH_SURROGATE_END, - REGIONAL_INDICATOR_START, - REGIONAL_INDICATOR_END, - FITZPATRICK_MODIFIER_START, - FITZPATRICK_MODIFIER_END, - VARIATION_MODIFIER_START, - VARIATION_MODIFIER_END, - DIACRITICAL_MARKS_START, - DIACRITICAL_MARKS_END, - ZWJ, - GRAPHEMS, - betweenInclusive, - codePointFromSurrogatePair -} from './unicode-safe-split'; +import split from './unicode-safe-split'; /** Split input string into array of potential tokens */ -export default (subject: string) : IToken[] => { - - if (typeof subject !== 'string') { - throw new Error('string cannot be undefined or null') - } +export default (options: ParserOptions, subject: string) : IToken[] => { const result : IToken[] = []; - let token : IToken | null = null; - let idx = 0; - let inc = 0; - while (idx < subject.length) { - const idxInc = idx + inc; - const current = subject[idxInc]; - if ( - idxInc < (subject.length - 1) && - current && - betweenInclusive(current.charCodeAt(0), HIGH_SURROGATE_START, HIGH_SURROGATE_END) - ) { - const currPair = codePointFromSurrogatePair(current + subject[idxInc + 1]); - const nextPair = codePointFromSurrogatePair(subject.substring(idxInc + 2, idxInc + 5)); - if ( - betweenInclusive(currPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) && - betweenInclusive(nextPair, REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END) - ) { - inc += 4; - } else if (betweenInclusive(nextPair, FITZPATRICK_MODIFIER_START, FITZPATRICK_MODIFIER_END)) { - inc += 4; - } else { - inc += 2; - } - } else { - inc += 1; - } - if (GRAPHEMS.has((subject[idx + inc] + '').charCodeAt(0))) { - inc += 1; - } - if (betweenInclusive((subject[idx + inc] + '').charCodeAt(0), VARIATION_MODIFIER_START, VARIATION_MODIFIER_END)) { - inc += 1; - } - if (betweenInclusive((subject[idx + inc] + '').charCodeAt(0), DIACRITICAL_MARKS_START, DIACRITICAL_MARKS_END)) { - inc += 1; - } - if ((subject[idx + inc] + '').charCodeAt(0) === ZWJ) { - inc += 1; - continue; - } + let textToken : null | IToken = null; + split( + subject, + (subject: string, char: string, position: number) : number | void => { + + // EOL + if (char === '\n' || char === '\r') { + if (options.eol === 'error') { + throw new Error('TODO - SyntaxError: illegal character'); + } + + if (!options.eol || options.eol === 'remove') { + return; + } - const char = subject.substring(idx, idx + inc); + if (textToken) { + result.push(textToken); + textToken = null; + } - // emoji - if (inc > 1) { - if (token == null) { - token = { - position: idx, + let inc = 0; + if (options.eol === 'space') { + while ( + subject[position + inc + 1] === '\n' || + subject[position + inc + 1] === '\r' + ) { + inc += 1; + } + char = ' '; + } + + result.push({ + position, value: char - }; + }); + + return inc; } - token.value += char; - // possibily a multi token - } else if ( - (char === '&' && subject[idx + 1] === '&') || - (char === '|' && subject[idx + 1] === '|') || - (char === '`' && subject[idx + 1] === '`') || - (char === '\\' && subject[idx + 1] != null) - ) { - if (token !== null) { - result.push(token); - token = null; + // Block Escape, Single Escape, &&, || + const seq = subject.slice(position, position + 2); + if ( + seq === '``' || + (char === '\\' && subject[position + 1]) || + ( + (seq === '&&' || seq === '||') && + subject[position - 1] === ' ' && + subject[position + 2] === ' ' + ) + ) { + if (textToken !== null) { + result.push(textToken); + textToken = null; + } + result.push({ + position: position, + value: subject.slice(position, position + 2) + }); + return 1; } - result.push({ - position: idx, - value: `${char}${subject[idx + 1]}` - }); - inc += 1; - // possibly significant character - } else if (!/^[a-z\d]$/i.test(char)) { - if (token !== null) { - result.push(token); - token = null; + // Non potentially significant characters + if (char[1] || /^[a-z\d]$/i.test(char)) { + if (textToken == null) { + textToken = { + position, + value: char + }; + } else { + textToken.value += char; + } + return; + } + + // All others + if (textToken !== null) { + result.push(textToken); + textToken = null; } result.push({ - position: idx, + position: position, value: char }); - - // non significant non emoji character - } else if (token == null) { - token = {position: idx, value: char}; - } else { - token.value += char; } + ); - idx += inc; - inc = 0; + if (textToken) { + result.push(textToken); } - if (token != null) { - result.push(token); - } - return result; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/helpers/unicode-safe-split.ts b/src/helpers/unicode-safe-split.ts index 2799330..ba0a3f9 100644 --- a/src/helpers/unicode-safe-split.ts +++ b/src/helpers/unicode-safe-split.ts @@ -41,7 +41,10 @@ export const codePointFromSurrogatePair = (pair: string) : number => { }; /** Splits input text into an array of characters */ -export default (subject : string) : string[] => { +export default ( + subject : string, + callback?: (subject: string, char: string, position: number) => number | void +) : string[] => { if (typeof subject !== 'string') { throw new Error('string cannot be undefined or null') @@ -86,10 +89,16 @@ export default (subject : string) : string[] => { inc += 1; continue; } - result.push(subject.substring(idx, idx + inc)); + const char = subject.substring(idx, idx + inc); + if (callback) { + let cbres = callback(subject, char, idx); + if (cbres != null) { + inc += cbres; + } + } + result.push(char); idx += inc; inc = 0; } - return result; }; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..d4ff10c --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export { default as tokenize } from './tokenize/tokenize'; \ No newline at end of file diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index fda8c4d..abfa835 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -1,8 +1,8 @@ import ParserOptions from '../types/options'; import { TokenizeState } from "./tokenize"; import Token from '../tokens/base'; -import TokenList from '../tokens/token-list'; import tokenizeArgument from './argument'; +import TokenType from '../types/token-types'; export default (options: ParserOptions, meta: any, state: TokenizeState) : boolean => { @@ -13,54 +13,62 @@ export default (options: ParserOptions, meta: any, state: TokenizeState) : boole } cursor += 1; - let args : TokenList[] = []; + let args : Token[] = []; while ( cursor < tokens.length && tokens[cursor].value != ']' ) { - // consume leading whitespace while (tokens[cursor].value === ' ') { cursor += 1; } - let argParts : Token[] = []; - const mockState = { - tokens, - cursor, - output: argParts - }; - - tokenizeArgument(options, meta, mockState); - - const next = mockState.tokens[mockState.cursor].value; - if (next == null) { - // TODO - custom error - Syntax Error: unexpected end - throw new Error('TODO - Syntax Error: Unexpected end'); + if (tokens[cursor].value === ']') { + break; } - if ( - next !== ',' && - next !== ']' - ) { - // TODO - custom error - Syntax Error: Illegal token - throw new Error('TODO - Syntax Error: Illegal Token') + if (tokens[cursor].value === ',') { + args.push(new Token({ + position: cursor, + type: TokenType.EMPTY, + value: undefined + })); + cursor += 1; + continue; } - args.push(new TokenList({ - position: cursor, - value: argParts - })); - tokens = mockState.tokens; - cursor = mockState.cursor; + const mockState : TokenizeState = { + tokens, + cursor + }; + + if (tokenizeArgument(options, meta, mockState)) { + if (mockState.output) { + args.push(mockState.output); - if (next === ',') { - cursor += 1; + } else { + args.push(new Token({ + position: cursor, + type: TokenType.EMPTY, + value: undefined + })); + } + tokens = mockState.tokens; + cursor = mockState.cursor; + + } else { + // TODO - custom error - SyntaxError: Illegal character + throw new Error('TODO - SyntaxError: Illegal character'); } } + if (cursor >= tokens.length) { + // TODO - custom error - SyntaxError: Unexpected end + throw new Error('TODO - SyntaxError: Unexpected end'); + } + if (tokens[cursor].value !== ']') { // TODO - custom error - SyntaxError: Expected ']' throw new Error('TODO - Syntax Error: Expected \']\''); @@ -68,7 +76,7 @@ export default (options: ParserOptions, meta: any, state: TokenizeState) : boole state.tokens = tokens; state.cursor = cursor + 1; - state.output.push(...args); + state.output = args; return true; }; \ No newline at end of file diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts index 2627529..bef40a9 100644 --- a/src/tokenize/argument.ts +++ b/src/tokenize/argument.ts @@ -1,12 +1,148 @@ import Token from '../tokens/base'; +import TokenList from '../tokens/token-list'; import ParserOptions from '../types/options'; import { TokenizeState } from './tokenize'; +import TokenType from '../types/token-types'; -export default (options: ParserOptions, meta: any, state: TokenizeState) : boolean => { - let { tokens, cursor, output } = state; +import tokenizeTextEscapeSingle from './text-escape-single'; +import tokenizeTextEscapeBlock from './text-escape-block'; +import tokenizeTextQuoted from './text-quoted'; +import tokenizeTextSpecial from './text-special'; +import tokenizeFunctionIf from './function-if'; +import tokenizeFunction from './function'; +import TextToken from '../tokens/text'; - let whitespace = ''; - let tokens : Token[] = [] +export default ( + options: ParserOptions, + meta: any, + state: TokenizeState +) : boolean => { + + let { tokens, cursor } = state; + + const startCursor = cursor; + + + let whitespaceStart = 0, + whitespace = ''; + const result : Token[] = []; + while ( + cursor < tokens.length && + tokens[cursor].value !== ',' && + tokens[cursor].value !== ']' + ) { + const lastToken : Token | void = result[result.length - 1]; + + const mockState : TokenizeState = { + tokens, + cursor + } + + if ( + tokenizeTextEscapeSingle(mockState, ['"', '$', '\\', ',', ']',]) || + tokenizeTextEscapeBlock(options, meta, mockState) || + tokenizeTextQuoted(options, meta, mockState) || + tokenizeTextSpecial(options, mockState) || + tokenizeFunctionIf(options, meta, mockState) || + tokenizeFunction(options, meta, mockState) + ) { + if (mockState.output) { + let output : Token = mockState.output; + + if (lastToken == null) { + result.push(output); + + } else { + + const lastTokenIsText = lastToken != null && lastToken.type === TokenType.TEXT; + const mockTokenIsText = output.type === TokenType.TEXT; + + if (lastTokenIsText) { + lastToken.value += whitespace; + if (mockTokenIsText) { + lastToken.value += output.value; + + } else { + result.push(output); + } + + } else if (mockTokenIsText) { + output.value = `${whitespace}${output.value}`; + result.push(output); + + } else{ + if (whitespace !== '') { + + result.push(new TextToken({ + position: whitespaceStart, + value: whitespace + })); + + result.push(output); + } + } + } + } + whitespaceStart = 0; + whitespace = ''; + + tokens = mockState.tokens; + cursor = mockState.cursor; + + continue; + } + + let value = tokens[cursor].value; + if (value === ' ' || value === '\t' || value === '\n' || value === '\r' ) { + if (whitespaceStart === 0) { + whitespaceStart = cursor; + } + whitespace += value; + + cursor += 1; + + } else if (value !== ',' && value !== ']') { + if (!lastToken) { + result.push(new TextToken({ + position: cursor, + value + })); + + } else if (lastToken.type === TokenType.TEXT) { + lastToken.value += whitespace + value; + + } else { + result.push(new TextToken({ + position: cursor, + value: whitespace + value + })); + } + + whitespaceStart = 0; + whitespace = ''; + + cursor += 1; + } + } + + if (cursor >= tokens.length) { + // TODO - custom error - Syntax Error: Unexpected End + throw new Error('TODO - SyntaxError: Unexpected end'); + } + + const next = tokens[cursor + 1].value; + if (next !== ',' && next !== ']') { + // TODO - custom error - Syntax Error: Illegal character + throw new Error('TODO - SyntaxError: Illegal character'); + } + + + state.tokens = tokens; + state.cursor = cursor; + state.output = new TokenList({ + position: startCursor, + value: result + }) return false; } \ No newline at end of file diff --git a/src/tokenize/function-if.ts b/src/tokenize/function-if.ts index e69de29..a04ffa9 100644 --- a/src/tokenize/function-if.ts +++ b/src/tokenize/function-if.ts @@ -0,0 +1,9 @@ +import ParserOptions from '../types/options'; +import { TokenizeState } from './tokenize'; +export default ( + options: ParserOptions, + meta: any, + state: TokenizeState +) : boolean => { + return false; +} \ No newline at end of file diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index ebb46f7..887c61a 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -1,9 +1,9 @@ import ParserOptions from '../types/options'; import has from '../helpers/has'; import type { TokenizeState } from './tokenize'; -import type Token from '../tokens/base'; import FunctionalToken from '../tokens/functional'; import tokenizeArgumentList from './argument-list'; +import Token from '../tokens/base'; const nameCheck = /^([a-z][a-z\d]{2,})$/i; @@ -13,23 +13,24 @@ export default ( state: TokenizeState ) : boolean => { - let { tokens, cursor, output } = state; + let { tokens, cursor } = state; if ( - cursor + 4 >= tokens.length || - tokens[cursor].value !== '$' + tokens[cursor].value !== '$' || + cursor + 4 >= tokens.length ) { return false; } + const startCursor = cursor; - const varPosition = tokens[cursor].position; + let prefix = '$'; cursor += 1; - let prefix = '$'; if (has(options.functionalHandlers, '$' + tokens[cursor].value[0])) { - let {position, value} = tokens[cursor].value; + let { position, value } = tokens[cursor]; + prefix += value[0]; if (value.length > 1) { @@ -55,27 +56,21 @@ export default ( const varName = tokens[cursor].value; cursor += 1; - const varArguments : Token[] = []; - const mockState = { + const mockState : TokenizeState = { tokens, - cursor, - output: varArguments + cursor } - if (tokenizeArgumentList(options, meta, mockState)) { - tokens = mockState.tokens; - cursor = mockState.cursor; - } + tokenizeArgumentList(options, meta, mockState); - output.push(new FunctionalToken({ - position: varPosition, + state.tokens = mockState.tokens; + state.output = new FunctionalToken({ + position: startCursor, prefix, value: varName, - arguments: varArguments - })); - - state.tokens = tokens; - state.cursor = cursor; + arguments: (mockState.output || []) + }); + state.cursor = mockState.cursor; return true; }; \ No newline at end of file diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index e9f5f58..db9f9f8 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -3,45 +3,51 @@ import type { TokenizeState } from './tokenize'; import TextToken from '../tokens/text'; import TokenType from '../types/token-types'; import Token from '../tokens/base'; -// import tokenizeFunctionIf from './function-if; +import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; +import TokenList from '../tokens/token-list'; export default ( options: ParserOptions, meta: any, state: TokenizeState, ) : boolean => { - let { tokens, cursor, output } = state; + let { tokens, cursor } = state; if (tokens[cursor].value !== '``') { return false; } + + const startCursor = cursor; + cursor += 1; if (cursor < (tokens.length - 1)) { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Syntax Error: Unexpected end of statement'); } const escTokens : Token[] = []; while (cursor < tokens.length && tokens[cursor].value !== '``') { - const mockState = { - ...state, - cursor, - output: escTokens + const mockState : TokenizeState = { + tokens, + cursor }; if ( - /* TODO: Uncomment once tokenize* is implemented tokenizeFunctionIf(options, meta, mockState) || - */ tokenizeFunction(options, meta, mockState) ) { + if (mockState.output) { + escTokens.push(mockState.output); + } + tokens = mockState.tokens; cursor = mockState.cursor; continue; } + // Treat everything else as plain text if ( escTokens.length === 0 || escTokens[escTokens.length - 1].type != TokenType.TEXT @@ -57,11 +63,15 @@ export default ( if (tokens[cursor].value !== '``') { // TODO - custom error - throw new Error('TODO'); + throw new Error('TODO - Syntax Error: Unexpected end'); } - output.push(...escTokens); + state.tokens = tokens; state.cursor = cursor + 1; + state.output = new TokenList({ + position: startCursor, + value: escTokens + }); return true; } \ No newline at end of file diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index 4ab33f0..c853ab3 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -6,7 +6,7 @@ export default ( state: TokenizeState, characters?: string[] ) : boolean => { - let { tokens, cursor, output } = state; + let { tokens, cursor } = state; if (characters == null) { characters = ['\\', '$', '"', '`'] @@ -22,16 +22,10 @@ export default ( return false; } - if (output.length > 0 && output[output.length -1].type === TokenType.TEXT) { - output[output.length - 1].value += value[1]; - - } else { - output.push(new TextToken({ - ...(tokens[cursor]), - value: value[1] - })); - } - + state.output = new TextToken({ + position: cursor, + value: value[1] + }); state.cursor = cursor + 1; return true; } \ No newline at end of file diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 3f9ce86..8b71894 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -5,8 +5,9 @@ import TokenType from '../types/token-types'; import Token from '../tokens/base'; import tokenizeEscapeSingle from './text-escape-single'; import tokenizeTextSpecial from './text-special'; -// import tokenizeFunctionIf from './function-if; +import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; +import TokenList from '../tokens/token-list'; export default ( options: ParserOptions, @@ -15,60 +16,83 @@ export default ( ) : boolean => { let { tokens, cursor, output } = state; - if ( - cursor < (tokens.length - 2) || - tokens[cursor].value !== '`' || - tokens[cursor + 1].value !== '`' - ) { + if (tokens[cursor].value !== '"') { return false; } - cursor += 2; + + const startCursor = cursor; + + cursor += 1; + + if (cursor === tokens.length) { + // TODO - custom error - unexpected end + throw new Error('TODO - Syntax Error: unexpected end'); + } const quoteTokens : Token[] = []; - while (cursor < (tokens.length - 1) && tokens[cursor].value !== '"') { + while ( + cursor < tokens.length && + tokens[cursor].value !== '"' + ) { - const mockState = { - ...state, - cursor, - output: quoteTokens + let lastToken : Token = quoteTokens[quoteTokens.length - 1]; + + const mockState : TokenizeState = { + tokens, + cursor }; if ( tokenizeEscapeSingle(mockState, ['\\', '"']) || tokenizeTextSpecial(options, mockState) || - - /* TODO - uncomment once implemented tokenizeFunctionIf(options, meta, mockState) || - */ - tokenizeFunction(options, meta, mockState) ) { + + if (mockState.output != null) { + if ( + lastToken && + lastToken.type === TokenType.TEXT && + (mockState.output).type === TokenType.TEXT + ) { + lastToken.value += (mockState.output).value; + } else { + quoteTokens.push(mockState.output); + } + } + tokens = mockState.tokens; cursor = mockState.cursor; continue; } - if ( - quoteTokens.length === 0 || - quoteTokens[quoteTokens.length - 1].type != TokenType.TEXT - ) { - quoteTokens.push(new TextToken(tokens[cursor])); + // Treat everything else as text + if (lastToken && lastToken.type === TokenType.TEXT) { + lastToken.value += tokens[cursor].value; } else { - quoteTokens[quoteTokens.length - 1].value += tokens[cursor].value; + quoteTokens.push(new TextToken({ + position: cursor, + value: tokens[cursor].value + })); } - cursor += 1; } - if ( - cursor > (tokens.length - 1) || - tokens[cursor + 1].value !== '"' - ) { - // TODO - custom error + if (cursor >= tokens.length) { + // TODO - custom error - unexpected end + throw new Error('TODO - SyntaxError: unexpected end'); + } + + if (tokens[cursor].value !== '"') { + // TODO - custom error - expected closing token throw new Error('TODO - Syntax Error: expected closing quote'); } - output.push(...quoteTokens); + state.tokens = tokens; state.cursor = cursor + 2; + state.output = new TokenList({ + position: startCursor, + value: quoteTokens + }); return true; } \ No newline at end of file diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index b125f21..a7d7978 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -1,7 +1,6 @@ import ParserOptions from '../types/options'; import type { TokenizeState } from './tokenize'; import TextToken from '../tokens/text'; -import TokenType from '../types/token-types'; import has from '../helpers/has'; export default ( @@ -12,15 +11,15 @@ export default ( return false; } - let { tokens, cursor, output } = state; + let { tokens, cursor } = state; - const characters = { + const characters : Record = { 'n': '\n', 'r': '\r', 't': '\t' } - let value = tokens[cursor].value; + let value : string = tokens[cursor].value; if ( value[0] !== '\\' || (value[1] == null || value[1] === '') || @@ -29,17 +28,11 @@ export default ( return false; } - value = characters[value[1]]; - if (output.length > 0 && output[output.length -1].type === TokenType.TEXT) { - output[output.length - 1].value += value; + state.output = new TextToken({ + position: state.cursor, + value: characters[value[1]] + }); + state.cursor += 1; - } else { - output.push(new TextToken({ - ...(tokens[cursor]), - value - })); - } - - state.cursor = cursor + 1; return true; } \ No newline at end of file diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index f7d5ce4..b38f031 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -8,57 +8,76 @@ import tokenizeTextEscapeSingle from './text-escape-single'; import tokenizeTextEscapeBlock from './text-escape-block'; import tokenizeTextQuoted from './text-quoted'; import tokenizeTextSpecial from './text-special'; -// import tokenizeFunctionIf from './function-if; +import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; export interface TokenizeState { tokens: IToken[]; cursor: number; - output: Token[]; + output?: Token | Token[]; } export default (subject: string, options: ParserOptions, meta: any = {}) : TokenList => { - const tokens = getPotentialTokens(subject); - const state : TokenizeState = { - tokens, - cursor: 0, - output: [] - } + let tokens = getPotentialTokens(options, subject); + + const result : Array = []; + + let cursor = 0; + + // trim leading and trailing spaces + while (tokens.length && tokens[0].value === ' ') tokens.shift(); + while (tokens.length && tokens[tokens.length - 1].value === ' ') tokens.pop(); + + while (cursor < tokens.length) { + + let mockState : TokenizeState = { + tokens: tokens, + cursor: cursor + }; - while (state.cursor < tokens.length) { if ( - tokenizeTextEscapeSingle(state) || - tokenizeTextEscapeBlock(options, meta, state) || - tokenizeTextQuoted(options, meta, state) || - tokenizeTextSpecial(options, state) || - /* - TODO - Uncomment once implemented - tokenizeFunctionIf(options, meta, state) || - */ - tokenizeFunction(options, meta, state) + tokenizeTextEscapeSingle(mockState) || + tokenizeTextEscapeBlock(options, meta, mockState) || + tokenizeTextQuoted(options, meta, mockState) || + tokenizeTextSpecial(options, mockState) || + tokenizeFunctionIf(options, meta, mockState) || + tokenizeFunction(options, meta, mockState) ) { - continue; - } + let lastToken : Token = result[result.length - 1]; + if ( + lastToken != null && + lastToken.type === TokenType.TEXT && + mockState.output && + (mockState.output).type === TokenType.TEXT + ) { + lastToken.value += (mockState.output).value; - const { tokens, output, cursor } = state; + } else { + result.push(mockState.output); + } - if (output.length === 0 || output[output.length - 1].type !== TokenType.TEXT) { - output.push(new TextToken(tokens[cursor])); + tokens = mockState.tokens; + cursor = mockState.cursor; + continue; + } + // Assume anything else is plain text + const last : Token = result[result.length - 1]; + if ( + result.length && + last != null && + last.type === TokenType.TEXT + ) { + last.value += tokens[cursor].value; } else { - output[output.length - 1].value += tokens[cursor].value; + result.push(new TextToken(tokens[cursor])); } - state.cursor += 1; - } - - if (state.cursor < tokens.length) { - // TODO - custom error; - throw new Error('TODO - Syntax Error: Unexpected token'); + cursor += 1; } return new TokenList({ - value: state.output + value: result }); } \ No newline at end of file diff --git a/src/tokens/functional.ts b/src/tokens/functional.ts index 2e26e8e..728ef98 100644 --- a/src/tokens/functional.ts +++ b/src/tokens/functional.ts @@ -2,6 +2,8 @@ import TokenType from '../types/token-types'; import ParserOptions from '../types/options'; import Token, { IToken } from './base'; +import TokenList from './token-list'; + export interface IFunctionalToken extends IToken { prefix: string; arguments: Token[] diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index a7972d2..01d4ff4 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -47,9 +47,10 @@ export default class TokenList extends Token { if (typeof res !== 'string') { let strRes = toText(res); if (strRes == null) { - res = value; + res = strValue; + } else { - res = strRes + strValue; + res = strRes + strValue; } continue; } diff --git a/src/types/options.ts b/src/types/options.ts index 5c6768b..178022a 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -10,6 +10,6 @@ export default interface ParserOptions { verifyOnly?: boolean; skipArgumentsCheck?: boolean; - eol?: 'error' | 'ignore' | 'consume' | 'space'; + eol?: 'error' | 'remove' | 'space' | 'keep'; specialSequences?: boolean; }; \ No newline at end of file diff --git a/src/types/token-types.ts b/src/types/token-types.ts index 216f086..c21347b 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -6,6 +6,7 @@ enum TokenType { ARGUMENT, LOGICAL, COMPARISON, + EMPTY, UNKNOWN }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e830a0b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + + "module": "commonjs", + + + "lib": ["ES2020"], + "noImplicitAny": true, + + "target": "ES2020", + "esModuleInterop": true, + "newLine": "lf", + "removeComments": true, + "sourceMap": true, + "outDir": "./lib", + }, + "files": [ + "./src/index.ts" + ] +} \ No newline at end of file From 0fba69e780749b50bdccc7ad740572c531606c0d Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 14 Sep 2022 08:51:17 -0400 Subject: [PATCH 14/92] fix: textToken not setting type --- src/tokens/text.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tokens/text.ts b/src/tokens/text.ts index 35ea02b..7ff0a7e 100644 --- a/src/tokens/text.ts +++ b/src/tokens/text.ts @@ -1,8 +1,12 @@ +import TokenType from '../types/token-types'; import Token, { IToken } from './base'; export default class TextToken extends Token { public value: string; constructor(token: IToken) { - super(token); + super({ + ...token, + type: TokenType.TEXT + }); } } \ No newline at end of file From 2912c79c3408a3abff27e039dcb19040489bcfb6 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 14 Sep 2022 08:51:30 -0400 Subject: [PATCH 15/92] improv: cleanup --- src/tokenize/tokenize.ts | 7 ++----- src/tokens/functional.ts | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index b38f031..76b5c86 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -64,12 +64,9 @@ export default (subject: string, options: ParserOptions, meta: any = {}) : Token // Assume anything else is plain text const last : Token = result[result.length - 1]; - if ( - result.length && - last != null && - last.type === TokenType.TEXT - ) { + if (last != null && last.type === TokenType.TEXT) { last.value += tokens[cursor].value; + } else { result.push(new TextToken(tokens[cursor])); } diff --git a/src/tokens/functional.ts b/src/tokens/functional.ts index 728ef98..2e26e8e 100644 --- a/src/tokens/functional.ts +++ b/src/tokens/functional.ts @@ -2,8 +2,6 @@ import TokenType from '../types/token-types'; import ParserOptions from '../types/options'; import Token, { IToken } from './base'; -import TokenList from './token-list'; - export interface IFunctionalToken extends IToken { prefix: string; arguments: Token[] From 98c3ca7fe74b61775376224ebce86ee0da2f6024 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 14 Sep 2022 08:51:48 -0400 Subject: [PATCH 16/92] improve: ignore debug playground script --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c18ed01..af98353 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ -lib/ \ No newline at end of file +lib/ +/debug-playground.js \ No newline at end of file From 4426df3c15441f2b26edab3c09f72e71b5a19aff Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 03:11:33 -0400 Subject: [PATCH 17/92] improve: split special token characters --- src/helpers/get-potential-tokens.ts | 20 +++++++++++++++++++- src/tokenize/text-escape-single.ts | 13 ++++++------- src/tokenize/text-special.ts | 13 ++++++------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/helpers/get-potential-tokens.ts b/src/helpers/get-potential-tokens.ts index a4d322e..543fd20 100644 --- a/src/helpers/get-potential-tokens.ts +++ b/src/helpers/get-potential-tokens.ts @@ -47,11 +47,29 @@ export default (options: ParserOptions, subject: string) : IToken[] => { return inc; } + if ( + char === '\\' && + subject[position + 1] != null && + subject[position + 1] !== '' + ) { + if (textToken !== null) { + result.push(textToken); + textToken = null; + } + result.push({ + position: position, + value: '\\' + }, { + position: position + 1, + value: subject[position + 1] + }); + return 1; + } + // Block Escape, Single Escape, &&, || const seq = subject.slice(position, position + 2); if ( seq === '``' || - (char === '\\' && subject[position + 1]) || ( (seq === '&&' || seq === '||') && subject[position - 1] === ' ' && diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index c853ab3..8209884 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -12,20 +12,19 @@ export default ( characters = ['\\', '$', '"', '`'] } - const value = tokens[cursor].value; - if ( - value[0] !== '\\' || - (value[1] == null || value[1] === '') || - characters.findIndex(value[1]) !== -1 + tokens[cursor] == null || + tokens[cursor].value !== '\\' || + tokens[cursor + 1] == null || + !characters.includes(tokens[cursor + 1].value) ) { return false; } state.output = new TextToken({ position: cursor, - value: value[1] + value: tokens[cursor + 1].value }); - state.cursor = cursor + 1; + state.cursor += 2; return true; } \ No newline at end of file diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index a7d7978..4dbbf76 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -18,21 +18,20 @@ export default ( 'r': '\r', 't': '\t' } - - let value : string = tokens[cursor].value; if ( - value[0] !== '\\' || - (value[1] == null || value[1] === '') || - !has(characters, value[1]) + tokens[cursor] == null || + tokens[cursor].value !== '\\' || + tokens[cursor + 1] == null || + !has(characters, tokens[cursor + 1].value) ) { return false; } state.output = new TextToken({ position: state.cursor, - value: characters[value[1]] + value: tokens[cursor + 1].value }); - state.cursor += 1; + state.cursor += 2; return true; } \ No newline at end of file From 1035d7238abba82a63851f8ef5478a867cb796de Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 03:12:20 -0400 Subject: [PATCH 18/92] improve: error messages --- src/tokens/functional.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tokens/functional.ts b/src/tokens/functional.ts index 2e26e8e..2caeb22 100644 --- a/src/tokens/functional.ts +++ b/src/tokens/functional.ts @@ -25,13 +25,13 @@ export default class FunctionalToken extends Token { if (lookupHandler == null) { // TODO: custom errors - throw new Error('TODO'); + throw new Error(`TODO - no lookup handler for ${this.prefix}`); } - const handler = lookupHandler(meta, this.value); + const handler = lookupHandler(this.value, meta); if (handler == null) { // TODO: custom errors - throw new Error('TODO'); + throw new Error(`TODO - No handler for ${this.prefix}${this.value}`); } let args : any[] = []; From fa303839d643585a79edbb89bacad6031904d0d2 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 03:12:50 -0400 Subject: [PATCH 19/92] feat: add is* operators --- src/tokens/comparison/isbool.ts | 36 +++++++++++++++++++++++++++++++++ src/tokens/comparison/isnull.ts | 21 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/tokens/comparison/isbool.ts create mode 100644 src/tokens/comparison/isnull.ts diff --git a/src/tokens/comparison/isbool.ts b/src/tokens/comparison/isbool.ts new file mode 100644 index 0000000..ad36cd7 --- /dev/null +++ b/src/tokens/comparison/isbool.ts @@ -0,0 +1,36 @@ +import ParserOptions from '../../types/options'; +import ComparisonToken, { IComparisonToken } from './base'; + +export interface IIsToken extends IComparisonToken { + against: null | boolean; +} + +export default class IsToken extends ComparisonToken { + public against : null | boolean; + + constructor(token: IIsToken) { + super({ + ...token, + value: 'is' + }); + + this.against = token.against; + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.left.evaluate(options, meta); + + if (options.verifyOnly) { + return false; + } + + return v1 === this.against; + } + + toToken(): object { + return { + ...(super.toToken()), + against: this.against + } + } +} \ No newline at end of file diff --git a/src/tokens/comparison/isnull.ts b/src/tokens/comparison/isnull.ts new file mode 100644 index 0000000..e942903 --- /dev/null +++ b/src/tokens/comparison/isnull.ts @@ -0,0 +1,21 @@ +import ParserOptions from '../../types/options'; +import ComparisonToken, { IComparisonToken } from './base'; + +export default class IsNull extends ComparisonToken { + constructor(token: IComparisonToken) { + super({ + ...token, + value: 'isnull' + }); + } + + async handle(options: ParserOptions, meta?: any): Promise { + let v1 = await this.left.evaluate(options, meta); + + if (options.verifyOnly) { + return false; + } + + return v1 == null; + } +} \ No newline at end of file From f99391d89682afb608a877d4f5a18b35df588144 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 03:13:55 -0400 Subject: [PATCH 20/92] improve: add null checks --- src/tokenize/argument-list.ts | 5 ++++- src/tokenize/text-quoted.ts | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index abfa835..d3d1263 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -8,7 +8,10 @@ export default (options: ParserOptions, meta: any, state: TokenizeState) : boole let { tokens, cursor } = state; - if (tokens[cursor].value !== '[') { + if ( + tokens[cursor] == null || + tokens[cursor].value !== '[' + ) { return false; } cursor += 1; diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 8b71894..96c40d1 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -14,9 +14,12 @@ export default ( meta: any, state: TokenizeState ) : boolean => { - let { tokens, cursor, output } = state; + let { tokens, cursor } = state; - if (tokens[cursor].value !== '"') { + if ( + tokens[cursor] == null || + tokens[cursor].value !== '"' + ) { return false; } From 8cad70ebd79edb8701ca03990b41403c70ae244e Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 03:14:41 -0400 Subject: [PATCH 21/92] fix: function lookup --- src/tokenize/function.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index 887c61a..c8e8f33 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -16,15 +16,15 @@ export default ( let { tokens, cursor } = state; if ( + tokens[cursor] == null || tokens[cursor].value !== '$' || - cursor + 4 >= tokens.length + tokens[cursor + 1] == null ) { return false; } const startCursor = cursor; let prefix = '$'; - cursor += 1; if (has(options.functionalHandlers, '$' + tokens[cursor].value[0])) { @@ -48,12 +48,22 @@ export default ( } cursor += 1; } - - if (!nameCheck.test(tokens[cursor].value)) { + if (tokens[cursor] == null) { return false; } const varName = tokens[cursor].value; + + if (!nameCheck.test(varName)) { + return false; + } + + const lookupHandler = options.functionalHandlers[prefix]; + if (!lookupHandler || !lookupHandler(varName)) { + console.log(prefix, varName, lookupHandler); + return false; + } + cursor += 1; const mockState : TokenizeState = { From 26858536092519c35db7ce876697ba2ca8bf4da4 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 03:15:19 -0400 Subject: [PATCH 22/92] improve: reverse lookup handler args --- src/types/options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/options.ts b/src/types/options.ts index 178022a..6a753aa 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -3,7 +3,7 @@ interface Handler { evaluator: (meta: any, ...args: any[]) => any; } -type LookupHandler = (meta: any, name: string) => Handler; +type LookupHandler = (name: string, meta?: any) => Handler; export default interface ParserOptions { functionalHandlers: Record; From 5169a1ce56804d7d466ef778ef918b6163c1c411 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 03:16:13 -0400 Subject: [PATCH 23/92] improve: build settings --- tsconfig.json | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index e830a0b..05e524c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,10 @@ { "compilerOptions": { - "module": "commonjs", + "composite": true, + "declaration": true, + "declarationMap": true, "lib": ["ES2020"], "noImplicitAny": true, @@ -12,9 +14,15 @@ "newLine": "lf", "removeComments": true, "sourceMap": true, - "outDir": "./lib", + + "rootDir": "src", + "outDir": "lib", + "tsBuildInfoFile": "lib/tsconfig.tsbuildinfo" }, - "files": [ - "./src/index.ts" + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules/**/*" ] } \ No newline at end of file From 2445fdb69d35a66513aa4df34e728e4c16aab1ba Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 03:57:34 -0400 Subject: [PATCH 24/92] improve: use optional chaining --- src/tokenize/argument-list.ts | 5 +---- src/tokenize/function.ts | 3 +-- src/tokenize/text-escape-block.ts | 2 +- src/tokenize/text-escape-single.ts | 3 +-- src/tokenize/text-quoted.ts | 5 +---- src/tokenize/text-special.ts | 3 +-- 6 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index d3d1263..0efa9b1 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -8,10 +8,7 @@ export default (options: ParserOptions, meta: any, state: TokenizeState) : boole let { tokens, cursor } = state; - if ( - tokens[cursor] == null || - tokens[cursor].value !== '[' - ) { + if (tokens[cursor]?.value !== '[') { return false; } cursor += 1; diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index c8e8f33..b2842e8 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -16,8 +16,7 @@ export default ( let { tokens, cursor } = state; if ( - tokens[cursor] == null || - tokens[cursor].value !== '$' || + tokens[cursor]?.value !== '$' || tokens[cursor + 1] == null ) { return false; diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index db9f9f8..f611257 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -14,7 +14,7 @@ export default ( ) : boolean => { let { tokens, cursor } = state; - if (tokens[cursor].value !== '``') { + if (tokens[cursor]?.value !== '``') { return false; } diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index 8209884..17b6169 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -13,8 +13,7 @@ export default ( } if ( - tokens[cursor] == null || - tokens[cursor].value !== '\\' || + tokens[cursor]?.value !== '\\' || tokens[cursor + 1] == null || !characters.includes(tokens[cursor + 1].value) ) { diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 96c40d1..4eb18ea 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -16,10 +16,7 @@ export default ( ) : boolean => { let { tokens, cursor } = state; - if ( - tokens[cursor] == null || - tokens[cursor].value !== '"' - ) { + if (tokens[cursor]?.value !== '"') { return false; } diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index 4dbbf76..2288469 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -19,8 +19,7 @@ export default ( 't': '\t' } if ( - tokens[cursor] == null || - tokens[cursor].value !== '\\' || + tokens[cursor]?.value !== '\\' || tokens[cursor + 1] == null || !has(characters, tokens[cursor + 1].value) ) { From ec594c43c0b8cc69410530bfd08e4b502c458e99 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 03:58:07 -0400 Subject: [PATCH 25/92] improve: split var prefix into own token --- src/helpers/get-potential-tokens.ts | 13 +++++---- src/tokenize/function.ts | 42 +++++++++-------------------- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/src/helpers/get-potential-tokens.ts b/src/helpers/get-potential-tokens.ts index 543fd20..c07fb7a 100644 --- a/src/helpers/get-potential-tokens.ts +++ b/src/helpers/get-potential-tokens.ts @@ -47,10 +47,13 @@ export default (options: ParserOptions, subject: string) : IToken[] => { return inc; } + const nextChar = subject[position + 1]; + + // \\, $ if ( - char === '\\' && - subject[position + 1] != null && - subject[position + 1] !== '' + nextChar != null && + (char === '\\' && nextChar !== '') || + (char === '$' && nextChar !== '\\') ) { if (textToken !== null) { result.push(textToken); @@ -58,10 +61,10 @@ export default (options: ParserOptions, subject: string) : IToken[] => { } result.push({ position: position, - value: '\\' + value: char }, { position: position + 1, - value: subject[position + 1] + value: nextChar }); return 1; } diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index b2842e8..8da12e1 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -15,10 +15,7 @@ export default ( let { tokens, cursor } = state; - if ( - tokens[cursor]?.value !== '$' || - tokens[cursor + 1] == null - ) { + if (tokens[cursor]?.value !== '$' || tokens[cursor + 1] == null) { return false; } const startCursor = cursor; @@ -26,45 +23,32 @@ export default ( let prefix = '$'; cursor += 1; - if (has(options.functionalHandlers, '$' + tokens[cursor].value[0])) { - - let { position, value } = tokens[cursor]; + let varName = tokens[cursor].value; + if (has(options.functionalHandlers,`$${varName}`)) { + prefix += tokens[cursor].value; + cursor += 1; - prefix += value[0]; + if (cursor >= tokens.length) { + return false; + } - if (value.length > 1) { - tokens = tokens.slice(); - tokens[cursor] = { - position, - value: value[0] - }; + varName = tokens[cursor].value; - tokens.splice(cursor + 1, 0, { - position: position += 1, - value: (value).slice(1) - }); - cursor += 1; - } + } else if ((cursor + 1) < tokens.length) { cursor += 1; + varName += tokens[cursor].value; } - if (tokens[cursor] == null) { - return false; - } - - const varName = tokens[cursor].value; + cursor += 1; if (!nameCheck.test(varName)) { return false; } const lookupHandler = options.functionalHandlers[prefix]; - if (!lookupHandler || !lookupHandler(varName)) { - console.log(prefix, varName, lookupHandler); + if (!lookupHandler?.(varName)) { return false; } - cursor += 1; - const mockState : TokenizeState = { tokens, cursor From f79a499767ac6ed6dd13ff337d5e8d145188a1aa Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 04:40:32 -0400 Subject: [PATCH 26/92] improve: make state vars public --- src/tokens/if-statement.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tokens/if-statement.ts b/src/tokens/if-statement.ts index 7c7526a..a6d859c 100644 --- a/src/tokens/if-statement.ts +++ b/src/tokens/if-statement.ts @@ -11,9 +11,9 @@ export interface IIfStatementToken extends IToken { } export default class IfStatementToken extends Token { - protected condition: OperatorToken; - protected whenTrue: Token; - protected whenFalse?: Token; + public condition: OperatorToken; + public whenTrue: Token; + public whenFalse?: Token; constructor(token: IIfStatementToken) { super({ From c1ca82d2adce86fb7d17c597426811d9196647e9 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 04:40:50 -0400 Subject: [PATCH 27/92] improve: make base token value optional --- src/tokens/base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokens/base.ts b/src/tokens/base.ts index 92b8068..321acda 100644 --- a/src/tokens/base.ts +++ b/src/tokens/base.ts @@ -3,7 +3,7 @@ import TokenType from '../types/token-types'; export interface IToken { type?: TokenType; position: number; - value: any; + value?: any; } export default class Token { From bc8728d710728514236dff2e2ea71e5c8e0f5ee3 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 04:42:10 -0400 Subject: [PATCH 28/92] fix: incorrect positional value --- src/tokenize/argument-list.ts | 3 +-- src/tokenize/argument.ts | 4 ++-- src/tokenize/function.ts | 4 ++-- src/tokenize/text-escape-block.ts | 4 ++-- src/tokenize/text-escape-single.ts | 2 +- src/tokenize/text-quoted.ts | 4 ++-- src/tokenize/text-special.ts | 6 +++--- 7 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index 0efa9b1..0d4f255 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -51,8 +51,7 @@ export default (options: ParserOptions, meta: any, state: TokenizeState) : boole } else { args.push(new Token({ position: cursor, - type: TokenType.EMPTY, - value: undefined + type: TokenType.EMPTY })); } tokens = mockState.tokens; diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts index bef40a9..8538b31 100644 --- a/src/tokenize/argument.ts +++ b/src/tokenize/argument.ts @@ -20,7 +20,7 @@ export default ( let { tokens, cursor } = state; - const startCursor = cursor; + const position = tokens[cursor]?.value; let whitespaceStart = 0, @@ -140,7 +140,7 @@ export default ( state.tokens = tokens; state.cursor = cursor; state.output = new TokenList({ - position: startCursor, + position, value: result }) diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index 8da12e1..d688cc1 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -18,7 +18,7 @@ export default ( if (tokens[cursor]?.value !== '$' || tokens[cursor + 1] == null) { return false; } - const startCursor = cursor; + const position = tokens[cursor].position; let prefix = '$'; cursor += 1; @@ -58,7 +58,7 @@ export default ( state.tokens = mockState.tokens; state.output = new FunctionalToken({ - position: startCursor, + position, prefix, value: varName, arguments: (mockState.output || []) diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index f611257..2ec0da1 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -18,7 +18,7 @@ export default ( return false; } - const startCursor = cursor; + const position = tokens[cursor].position; cursor += 1; @@ -69,7 +69,7 @@ export default ( state.tokens = tokens; state.cursor = cursor + 1; state.output = new TokenList({ - position: startCursor, + position, value: escTokens }); diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index 17b6169..e1aa329 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -21,7 +21,7 @@ export default ( } state.output = new TextToken({ - position: cursor, + position: tokens[cursor].position, value: tokens[cursor + 1].value }); state.cursor += 2; diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 4eb18ea..7e0ba6c 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -20,7 +20,7 @@ export default ( return false; } - const startCursor = cursor; + const position = tokens[cursor]?.position; cursor += 1; @@ -90,7 +90,7 @@ export default ( state.tokens = tokens; state.cursor = cursor + 2; state.output = new TokenList({ - position: startCursor, + position, value: quoteTokens }); diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index 2288469..c967c9a 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -11,13 +11,13 @@ export default ( return false; } - let { tokens, cursor } = state; - const characters : Record = { 'n': '\n', 'r': '\r', 't': '\t' } + + let { tokens, cursor } = state; if ( tokens[cursor]?.value !== '\\' || tokens[cursor + 1] == null || @@ -27,7 +27,7 @@ export default ( } state.output = new TextToken({ - position: state.cursor, + position: tokens[cursor].position, value: tokens[cursor + 1].value }); state.cursor += 2; From d2c100dd944d982fc4813f468bc95e1a3e12839d Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 05:43:20 -0400 Subject: [PATCH 29/92] improve: rename token classes --- src/helpers/get-potential-tokens.ts | 8 +- src/tokenize/argument-list.ts | 10 ++- src/tokenize/argument.ts | 13 +-- src/tokenize/condition.ts | 14 +++ src/tokenize/function-if.ts | 86 ++++++++++++++++++- src/tokenize/function.ts | 11 ++- src/tokenize/text-escape-block.ts | 13 +-- src/tokenize/text-escape-single.ts | 4 +- src/tokenize/text-quoted.ts | 13 +-- src/tokenize/text-special.ts | 9 +- src/tokenize/tokenize.ts | 11 ++- src/tokens/comparison/base.ts | 7 +- src/tokens/logical/base.ts | 7 +- .../{if-statement.ts => token-function-if.ts} | 7 +- .../{functional.ts => token-function.ts} | 8 +- src/tokens/token-list.ts | 5 +- src/tokens/{operator.ts => token-operator.ts} | 11 ++- src/tokens/{text.ts => token-text.ts} | 9 +- src/tokens/{base.ts => token.ts} | 2 +- tsconfig.json | 2 - 20 files changed, 189 insertions(+), 61 deletions(-) create mode 100644 src/tokenize/condition.ts rename src/tokens/{if-statement.ts => token-function-if.ts} (91%) rename src/tokens/{functional.ts => token-function.ts} (93%) rename src/tokens/{operator.ts => token-operator.ts} (78%) rename src/tokens/{text.ts => token-text.ts} (57%) rename src/tokens/{base.ts => token.ts} (97%) diff --git a/src/helpers/get-potential-tokens.ts b/src/helpers/get-potential-tokens.ts index c07fb7a..e33a7ec 100644 --- a/src/helpers/get-potential-tokens.ts +++ b/src/helpers/get-potential-tokens.ts @@ -1,14 +1,14 @@ -import { type IToken } from '../tokens/base'; +import { type IToken } from '../tokens/token'; import ParserOptions from '../types/options'; import split from './unicode-safe-split'; /** Split input string into array of potential tokens */ -export default (options: ParserOptions, subject: string) : IToken[] => { +export default (options: ParserOptions, subject: string) : {position: number, value: string}[] => { - const result : IToken[] = []; + const result : {position: number, value: string}[] = []; - let textToken : null | IToken = null; + let textToken : null | {position: number, value: string} = null; split( subject, (subject: string, char: string, position: number) : number | void => { diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index 0d4f255..053fc61 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -1,9 +1,11 @@ -import ParserOptions from '../types/options'; -import { TokenizeState } from "./tokenize"; -import Token from '../tokens/base'; -import tokenizeArgument from './argument'; +import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; +import Token from '../tokens/token'; + +import type { TokenizeState } from "./tokenize"; +import tokenizeArgument from './argument'; + export default (options: ParserOptions, meta: any, state: TokenizeState) : boolean => { let { tokens, cursor } = state; diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts index 8538b31..4174e59 100644 --- a/src/tokenize/argument.ts +++ b/src/tokenize/argument.ts @@ -1,16 +1,17 @@ -import Token from '../tokens/base'; -import TokenList from '../tokens/token-list'; -import ParserOptions from '../types/options'; -import { TokenizeState } from './tokenize'; +import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; +import type Token from '../tokens/token'; +import TokenList from '../tokens/token-list'; +import TextToken from '../tokens/token-text'; + +import type { TokenizeState } from './tokenize'; import tokenizeTextEscapeSingle from './text-escape-single'; import tokenizeTextEscapeBlock from './text-escape-block'; import tokenizeTextQuoted from './text-quoted'; import tokenizeTextSpecial from './text-special'; import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; -import TextToken from '../tokens/text'; export default ( options: ParserOptions, @@ -20,7 +21,7 @@ export default ( let { tokens, cursor } = state; - const position = tokens[cursor]?.value; + const position = tokens[cursor]?.position; let whitespaceStart = 0, diff --git a/src/tokenize/condition.ts b/src/tokenize/condition.ts new file mode 100644 index 0000000..d2b2ea4 --- /dev/null +++ b/src/tokenize/condition.ts @@ -0,0 +1,14 @@ +import type ParserOptions from '../types/options'; + +import type { TokenizeState } from "./tokenize"; + +export default (options: ParserOptions, meta: any, state: TokenizeState) : boolean => { + + /* + // logical operator + // not operator + // compare operator + */ + + return false; +} \ No newline at end of file diff --git a/src/tokenize/function-if.ts b/src/tokenize/function-if.ts index a04ffa9..b8e70b9 100644 --- a/src/tokenize/function-if.ts +++ b/src/tokenize/function-if.ts @@ -1,9 +1,93 @@ import ParserOptions from '../types/options'; -import { TokenizeState } from './tokenize'; +import TokenType from '../types/token-types'; + +import Token from '../tokens/token'; +import IfToken from '../tokens/token-function-if'; +import type OperatorToken from '../tokens/token-operator'; + +import { type TokenizeState } from './tokenize'; +import tokenizeCondition from './condition'; + export default ( options: ParserOptions, meta: any, state: TokenizeState ) : boolean => { + + let { tokens, cursor } = state; + + if ( + tokens[cursor]?.value !== '$' || + tokens[cursor + 1]?.value !== 'if' || + tokens[cursor + 2]?.value !== '[' + ) { + return false; + } + + const position = tokens[cursor]?.position; + + cursor += 3; + + while (/^\s$/g.test(tokens[cursor]?.value)) { + cursor += 1; + } + + if (cursor >= tokens.length) { + // TODO - custom error - SyntaxError: Unexpected end + throw new Error('TODO - SyntaxError: unexpected end'); + } + + // empty $if[] + if (tokens[cursor].value === ']') { + cursor += 1; + state.cursor = cursor; + state.output = new Token({ + position, + type: TokenType.EMPTY + }); + return true; + } + + let mockState : TokenizeState = { + tokens, + cursor + }; + if (!tokenizeCondition(options, meta, state)) { + // TODO - custom error - SyntaxError: Invalid Condition + throw new Error('TODO - SyntaxError: Invalid Condition'); + } + tokens = mockState.tokens; + cursor = mockState.cursor; + const condition : OperatorToken = mockState.output; + + if (tokens[cursor] == null) { + // TODO - custom error - SyntaxError: unexpected end + throw new Error('TODO - SyntaxError: unexpected end'); + } + + // empty conditional + if (tokens[cursor].value === ']') { + state.tokens = tokens; + state.cursor = cursor; + state.output = new Token({ + position: position, + type: TokenType.EMPTY + }); + return true; + } + + + return false; + /* + state.tokens = tokens; + state.cursor = cursor; + state.output = new IfStatementToken({ + position, + condition, + whenTrue, + whenFalse + }); + return true; + */ } \ No newline at end of file diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index d688cc1..3261c9e 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -1,9 +1,12 @@ -import ParserOptions from '../types/options'; import has from '../helpers/has'; -import type { TokenizeState } from './tokenize'; -import FunctionalToken from '../tokens/functional'; + +import type ParserOptions from '../types/options'; + +import type Token from '../tokens/token'; +import FunctionalToken from '../tokens/token-function'; + import tokenizeArgumentList from './argument-list'; -import Token from '../tokens/base'; +import type { TokenizeState } from './tokenize'; const nameCheck = /^([a-z][a-z\d]{2,})$/i; diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index 2ec0da1..55af727 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -1,11 +1,14 @@ -import ParserOptions from '../types/options'; -import type { TokenizeState } from './tokenize'; -import TextToken from '../tokens/text'; +import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; -import Token from '../tokens/base'; + +import type Token from '../tokens/token'; +import TokenList from '../tokens/token-list'; +import TextToken from '../tokens/token-text'; + import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; -import TokenList from '../tokens/token-list'; + +import type { TokenizeState } from './tokenize'; export default ( options: ParserOptions, diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index e1aa329..7abbfc8 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -1,6 +1,6 @@ +import TextToken from '../tokens/token-text'; + import type { TokenizeState } from './tokenize'; -import TextToken from '../tokens/text'; -import TokenType from '../types/token-types'; export default ( state: TokenizeState, diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 7e0ba6c..d393cd0 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -1,13 +1,16 @@ -import ParserOptions from '../types/options'; -import type { TokenizeState } from './tokenize'; -import TextToken from '../tokens/text'; +import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; -import Token from '../tokens/base'; + +import type Token from '../tokens/token'; +import TokenList from '../tokens/token-list'; +import TextToken from '../tokens/token-text'; + import tokenizeEscapeSingle from './text-escape-single'; import tokenizeTextSpecial from './text-special'; import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; -import TokenList from '../tokens/token-list'; + +import type { TokenizeState } from './tokenize'; export default ( options: ParserOptions, diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index c967c9a..986390e 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -1,8 +1,11 @@ -import ParserOptions from '../types/options'; -import type { TokenizeState } from './tokenize'; -import TextToken from '../tokens/text'; +import type ParserOptions from '../types/options'; + import has from '../helpers/has'; +import TextToken from '../tokens/token-text'; + +import type { TokenizeState } from './tokenize'; + export default ( options: ParserOptions, state: TokenizeState diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index 76b5c86..e9d4fab 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -1,9 +1,12 @@ -import ParserOptions from '../types/options'; +import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; + import getPotentialTokens from '../helpers/get-potential-tokens'; -import Token, { IToken } from '../tokens/base'; + +import Token from '../tokens/token'; import TokenList, { ITokenList } from '../tokens/token-list'; -import TextToken from '../tokens/text'; +import TextToken from '../tokens/token-text'; + import tokenizeTextEscapeSingle from './text-escape-single'; import tokenizeTextEscapeBlock from './text-escape-block'; import tokenizeTextQuoted from './text-quoted'; @@ -12,7 +15,7 @@ import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; export interface TokenizeState { - tokens: IToken[]; + tokens: {position: number, value: string}[]; cursor: number; output?: Token | Token[]; } diff --git a/src/tokens/comparison/base.ts b/src/tokens/comparison/base.ts index 3d458fd..8df38a6 100644 --- a/src/tokens/comparison/base.ts +++ b/src/tokens/comparison/base.ts @@ -1,10 +1,11 @@ import ParserOptions from '../../types/options'; import TokenType from '../../types/token-types'; -import OperatorToken, { IOperatorToken } from '../operator'; +import OperatorToken, { IOperatorToken } from '../token-operator'; export interface IComparisonToken extends IOperatorToken { invert?: boolean; + value: any; } export default class ComparisonToken extends OperatorToken { @@ -12,8 +13,8 @@ export default class ComparisonToken extends OperatorToken { constructor(token: IComparisonToken) { super({ - type: TokenType.COMPARISON, - ...token + ...token, + type: TokenType.COMPARISON }); if (token.invert) { diff --git a/src/tokens/logical/base.ts b/src/tokens/logical/base.ts index 930de4c..5a65228 100644 --- a/src/tokens/logical/base.ts +++ b/src/tokens/logical/base.ts @@ -1,15 +1,16 @@ import TokenType from '../../types/token-types'; -import { default as OperatorToken, IOperatorToken} from '../operator'; +import { default as OperatorToken, IOperatorToken} from '../token-operator'; export interface ILogicalToken extends IOperatorToken { + value: any; } export default class LogicalToken extends OperatorToken { constructor(token: ILogicalToken) { super({ - type: TokenType.LOGICAL, - ...token + ...token, + type: TokenType.LOGICAL }); } } \ No newline at end of file diff --git a/src/tokens/if-statement.ts b/src/tokens/token-function-if.ts similarity index 91% rename from src/tokens/if-statement.ts rename to src/tokens/token-function-if.ts index a6d859c..dc400ab 100644 --- a/src/tokens/if-statement.ts +++ b/src/tokens/token-function-if.ts @@ -1,10 +1,11 @@ import TokenType from '../types/token-types'; import ParserOptions from '../types/options'; -import Token, { IToken } from './base'; -import OperatorToken from './operator'; +import Token from './token'; +import OperatorToken from './token-operator'; -export interface IIfStatementToken extends IToken { +export interface IIfStatementToken { + position: number; condition: OperatorToken; whenTrue: Token, whenFalse?: Token diff --git a/src/tokens/functional.ts b/src/tokens/token-function.ts similarity index 93% rename from src/tokens/functional.ts rename to src/tokens/token-function.ts index 2caeb22..5089653 100644 --- a/src/tokens/functional.ts +++ b/src/tokens/token-function.ts @@ -1,10 +1,12 @@ import TokenType from '../types/token-types'; import ParserOptions from '../types/options'; -import Token, { IToken } from './base'; +import Token from './token'; -export interface IFunctionalToken extends IToken { +export interface IFunctionalToken { + position: number; prefix: string; - arguments: Token[] + value: string; + arguments: Token[]; } export default class FunctionalToken extends Token { diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index 01d4ff4..bbea72f 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -3,9 +3,10 @@ import ParserOptions from '../types/options'; import toText from '../helpers/to-text'; -import Token, { IToken } from './base'; +import Token from './token'; -export interface ITokenList extends IToken { +export interface ITokenList { + position: number; value: Token[]; } diff --git a/src/tokens/operator.ts b/src/tokens/token-operator.ts similarity index 78% rename from src/tokens/operator.ts rename to src/tokens/token-operator.ts index 8e8cbef..dd3f79b 100644 --- a/src/tokens/operator.ts +++ b/src/tokens/token-operator.ts @@ -1,9 +1,12 @@ import ParserOptions from '../types/options'; -import Token, { IToken } from './base'; +import TokenType from '../types/token-types'; +import Token from './token'; -export interface IOperatorToken extends IToken { - left: Token, - right?: Token +export interface IOperatorToken { + type: TokenType; + position: number; + left: Token; + right?: Token; } export default class OperatorToken extends Token { diff --git a/src/tokens/text.ts b/src/tokens/token-text.ts similarity index 57% rename from src/tokens/text.ts rename to src/tokens/token-text.ts index 7ff0a7e..afa9c18 100644 --- a/src/tokens/text.ts +++ b/src/tokens/token-text.ts @@ -1,9 +1,14 @@ import TokenType from '../types/token-types'; -import Token, { IToken } from './base'; +import Token, { type IToken } from './token'; + +export interface ITextToken { + position: number; + value: string; +} export default class TextToken extends Token { public value: string; - constructor(token: IToken) { + constructor(token: ITextToken) { super({ ...token, type: TokenType.TEXT diff --git a/src/tokens/base.ts b/src/tokens/token.ts similarity index 97% rename from src/tokens/base.ts rename to src/tokens/token.ts index 321acda..e5af053 100644 --- a/src/tokens/base.ts +++ b/src/tokens/token.ts @@ -1,7 +1,7 @@ import TokenType from '../types/token-types'; export interface IToken { - type?: TokenType; + type: TokenType; position: number; value?: any; } diff --git a/tsconfig.json b/tsconfig.json index 05e524c..73d4759 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,6 @@ "module": "commonjs", "composite": true, - "declaration": true, - "declarationMap": true, "lib": ["ES2020"], "noImplicitAny": true, From d88cd3b500c03f742a4da8632fdd968b9d0d5afe Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 06:24:35 -0400 Subject: [PATCH 30/92] improve: config --- package.json | 10 +++++++--- src/types/token-types.ts | 2 +- tsconfig.json | 28 ++++++++++++++++------------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 3e2705a..cb02717 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,17 @@ "type": "commonjs", "main": "./lib/index.js", "scripts": { - "lint": "eslint --fix ./src/** ./test/**", - "test": "mocha" + "build": "tsc --build", + "prepack": "tsc --build" }, "devDependencies": { "@types/node": "^18.7.18", "eslint": "^8.4.1", "mocha": "^9.1.3", "typescript": "^4.8.3" - } + }, + + "files": [ + "lib/" + ] } \ No newline at end of file diff --git a/src/types/token-types.ts b/src/types/token-types.ts index c21347b..bdd54ba 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -1,4 +1,4 @@ -enum TokenType { +const enum TokenType { TOKENLIST, IFSTATEMENT, FUNCTIONAL, diff --git a/tsconfig.json b/tsconfig.json index 73d4759..9bede23 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,24 +1,28 @@ { "compilerOptions": { - "module": "commonjs", - - "composite": true, + "target": "ES2020", + "module": "commonjs", + "esModuleInterop": true, "lib": ["ES2020"], "noImplicitAny": true, - "target": "ES2020", - "esModuleInterop": true, - "newLine": "lf", - "removeComments": true, - "sourceMap": true, - "rootDir": "src", "outDir": "lib", - "tsBuildInfoFile": "lib/tsconfig.tsbuildinfo" + + "incremental": true, + "tsBuildInfoFile": "lib/tsconfig.tsbuildinfo", + "declaration": true, + + "sourceMap": true, + + "preserveConstEnums": false, + + "newLine": "lf", + "removeComments": true }, - "include": [ - "src/**/*" + "files":[ + "./src/index.ts" ], "exclude": [ "node_modules/**/*" From 74cdc764b74889caf0d9df75d31138acb45f1324 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 08:08:05 -0400 Subject: [PATCH 31/92] improve: add missing semi-colon --- src/tokenize/argument.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts index 4174e59..15b318d 100644 --- a/src/tokenize/argument.ts +++ b/src/tokenize/argument.ts @@ -37,7 +37,7 @@ export default ( const mockState : TokenizeState = { tokens, cursor - } + }; if ( tokenizeTextEscapeSingle(mockState, ['"', '$', '\\', ',', ']',]) || From 566d18bc047ad0fd3c344abfae755fc7c7f9e6e9 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 08:08:34 -0400 Subject: [PATCH 32/92] improve: implement IPreToken interface --- src/helpers/get-potential-tokens.ts | 6 +++--- src/tokenize/tokenize.ts | 3 ++- src/types/pre-token.ts | 4 ++++ 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 src/types/pre-token.ts diff --git a/src/helpers/get-potential-tokens.ts b/src/helpers/get-potential-tokens.ts index e33a7ec..84198cd 100644 --- a/src/helpers/get-potential-tokens.ts +++ b/src/helpers/get-potential-tokens.ts @@ -1,12 +1,12 @@ -import { type IToken } from '../tokens/token'; +import type IPreToken from '../types/pre-token'; import ParserOptions from '../types/options'; import split from './unicode-safe-split'; /** Split input string into array of potential tokens */ -export default (options: ParserOptions, subject: string) : {position: number, value: string}[] => { +export default (options: ParserOptions, subject: string) : IPreToken[] => { - const result : {position: number, value: string}[] = []; + const result : IPreToken[] = []; let textToken : null | {position: number, value: string} = null; split( diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index e9d4fab..ef1b146 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -1,5 +1,6 @@ import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; +import type IPreToken from '../types/pre-token'; import getPotentialTokens from '../helpers/get-potential-tokens'; @@ -15,7 +16,7 @@ import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; export interface TokenizeState { - tokens: {position: number, value: string}[]; + tokens: IPreToken[]; cursor: number; output?: Token | Token[]; } diff --git a/src/types/pre-token.ts b/src/types/pre-token.ts new file mode 100644 index 0000000..bac54f1 --- /dev/null +++ b/src/types/pre-token.ts @@ -0,0 +1,4 @@ +export default interface IPreToken { + position: number; + value: string; +}; \ No newline at end of file From 1c7fd0d5b93280bc9a4644ca9ebb1771695e788c Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 08:09:17 -0400 Subject: [PATCH 33/92] feat: implement if-statement tokenizer --- src/tokenize/function-if.ts | 117 ++++++++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 17 deletions(-) diff --git a/src/tokenize/function-if.ts b/src/tokenize/function-if.ts index b8e70b9..f28d155 100644 --- a/src/tokenize/function-if.ts +++ b/src/tokenize/function-if.ts @@ -1,5 +1,6 @@ import ParserOptions from '../types/options'; import TokenType from '../types/token-types'; +import type IPreToken from '../types/pre-token'; import Token from '../tokens/token'; import IfToken from '../tokens/token-function-if'; @@ -7,6 +8,67 @@ import type OperatorToken from '../tokens/token-operator'; import { type TokenizeState } from './tokenize'; import tokenizeCondition from './condition'; +import tokenizeArgument from './argument'; + +const consumeWhitespace = (tokens: IPreToken[], cursor: number) : number => { + while (cursor < tokens.length && /^\s$/.test(tokens[cursor].value)) { + cursor += 1; + } + return cursor; +}; + +const getNextArg = (options: ParserOptions, meta: any, state: TokenizeState) : TokenizeState => { + let { tokens, cursor } = state; + + cursor = consumeWhitespace(tokens, cursor); + if (cursor >= tokens.length) { + // TODO - custom error - SyntaxError: Unexpected end + throw new Error('TODO - SyntaxError: unexpected end'); + } + + // no argument + if (tokens[cursor].value === ']') { + return { tokens, cursor }; + } + + // no leading delimiter + if (tokens[cursor].value !== ',') { + // TODO - custom error - SyntaxError: Illegal character + throw new Error('TODO - SyntaxError: Illegal character'); + } + cursor += 1; + + if (cursor >= tokens.length) { + // TODO - custom error - SyntaxError: Unexpected end + throw new Error('TODO - SyntaxError: unexpected end'); + } + + const position = tokens[cursor].position; + + cursor = consumeWhitespace(tokens, cursor); + + // empty argument + if ( + tokens[cursor].value === ',' || + tokens[cursor].value === ']' + ) { + return { + tokens, + cursor: cursor + 1, + output: new Token({ + position, + type: TokenType.EMPTY + }) + }; + } + + const mockState : TokenizeState = { + tokens, + cursor + }; + tokenizeArgument(options, meta, mockState); + return mockState; +}; export default ( options: ParserOptions, @@ -26,11 +88,7 @@ export default ( const position = tokens[cursor]?.position; - cursor += 3; - - while (/^\s$/g.test(tokens[cursor]?.value)) { - cursor += 1; - } + cursor = consumeWhitespace(tokens, cursor + 3); if (cursor >= tokens.length) { // TODO - custom error - SyntaxError: Unexpected end @@ -65,10 +123,10 @@ export default ( throw new Error('TODO - SyntaxError: unexpected end'); } - // empty conditional + // No return values - $if[] if (tokens[cursor].value === ']') { state.tokens = tokens; - state.cursor = cursor; + state.cursor = cursor + 1; state.output = new Token({ position: position, type: TokenType.EMPTY @@ -76,18 +134,43 @@ export default ( return true; } + if (tokens[cursor]?.value !== ',') { + // TODO - custom error - SyntaxError: Illegal character + throw new Error('TODO - SyntaxError: Illegal character'); + } + + const stateWhenTrue = getNextArg(options, meta, { tokens, cursor }); + const stateWhenFalse = getNextArg(options, meta, { ...stateWhenTrue }); + tokens = stateWhenFalse.tokens; + cursor = stateWhenFalse.cursor; + + if (tokens[cursor].value !== ']') { + // TODO - custom error - SyntaxError: Illegal character + throw new Error('TODO - SyntaxError: Illegal character'); + } - return false; - /* state.tokens = tokens; - state.cursor = cursor; - state.output = new IfStatementToken({ - position, - condition, - whenTrue, - whenFalse - }); + state.cursor = cursor + 1; + if (stateWhenFalse.output != null && (stateWhenFalse.output).type !== TokenType.EMPTY) { + state.output = new IfToken({ + position, + condition, + whenTrue: stateWhenTrue.output, + whenFalse: stateWhenFalse.output + }); + } else if (stateWhenTrue.output != null && (stateWhenTrue.output).type !== TokenType.EMPTY) { + state.output = new IfToken({ + position, + condition, + whenTrue: stateWhenTrue.output + }); + } else { + state.output = new Token({ + position: position, + type: TokenType.EMPTY + }); + } + return true; - */ } \ No newline at end of file From af89eef883278e7b48983c650f888ba3eacfda67 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 21:44:03 -0400 Subject: [PATCH 34/92] feat: create custom error classes --- src/errors.ts | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/errors.ts diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..a7d13ab --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,66 @@ +export class ExpressionError extends Error { + public readonly position: number; + + constructor(message: string, position: void | number) { + super(message); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.position = position || 0; + } +} + +export class ExpressionSyntaxError extends ExpressionError { + public readonly character: string; + + constructor(message: string, position: void | number, character?: string) { + super(message, position); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.character = character || ''; + } +} + +export class ExpressionVariableError extends ExpressionError { + public readonly varname: string; + + constructor( + message: string, + position: undefined | number, + varname?: string + ) { + super(message, position); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.varname = varname || ''; + } +} + +export class ExpressionArgumentsError extends ExpressionError { + public readonly index: number; + public readonly varname: undefined | string; + + constructor( + message: string, + position: undefined | number, + varname: undefined | string, + index?: number + ) { + super(message, position); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.index = index || -1; + this.varname = varname; + } +} \ No newline at end of file From 3eeaf731d986c19cfd40ffeb1c71e3afffd5a435 Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 21:44:26 -0400 Subject: [PATCH 35/92] feat: allow lookup handlers to be async --- src/types/options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/options.ts b/src/types/options.ts index 6a753aa..e6771ff 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -3,7 +3,7 @@ interface Handler { evaluator: (meta: any, ...args: any[]) => any; } -type LookupHandler = (name: string, meta?: any) => Handler; +type LookupHandler = (name: string, meta?: any) => Handler | Promise; export default interface ParserOptions { functionalHandlers: Record; From 53bac5f510a8eae79b73ff475d86f04db8344b3b Mon Sep 17 00:00:00 2001 From: SReject Date: Thu, 15 Sep 2022 21:45:40 -0400 Subject: [PATCH 36/92] feat: async tokenization; overhual $if[] logic --- src/tokenize/argument-list.ts | 17 ++- src/tokenize/argument.ts | 20 ++-- src/tokenize/condition.ts | 2 +- src/tokenize/function-if.ts | 161 +++++------------------------ src/tokenize/function.ts | 14 +-- src/tokenize/text-escape-block.ts | 10 +- src/tokenize/text-escape-single.ts | 5 +- src/tokenize/text-quoted.ts | 12 +-- src/tokenize/text-special.ts | 7 +- src/tokenize/tokenize.ts | 15 +-- 10 files changed, 73 insertions(+), 190 deletions(-) diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index 053fc61..1c0e36b 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -5,10 +5,13 @@ import Token from '../tokens/token'; import type { TokenizeState } from "./tokenize"; import tokenizeArgument from './argument'; +import tokenizeCondition from './condition'; -export default (options: ParserOptions, meta: any, state: TokenizeState) : boolean => { +export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { - let { tokens, cursor } = state; + let { tokens, cursor, meta: stateMeta } = state; + + let isCondition = stateMeta.isConditional || false; if (tokens[cursor]?.value !== '[') { return false; @@ -22,7 +25,7 @@ export default (options: ParserOptions, meta: any, state: TokenizeState) : boole tokens[cursor].value != ']' ) { - while (tokens[cursor].value === ' ') { + while (/^\s$/.test(tokens[cursor].value)) { cursor += 1; } @@ -45,8 +48,10 @@ export default (options: ParserOptions, meta: any, state: TokenizeState) : boole cursor }; - if (tokenizeArgument(options, meta, mockState)) { - + if ( + (isCondition && await tokenizeCondition(options, meta, mockState)) || + (!isCondition && await tokenizeArgument(options, meta, mockState)) + ) { if (mockState.output) { args.push(mockState.output); @@ -63,6 +68,8 @@ export default (options: ParserOptions, meta: any, state: TokenizeState) : boole // TODO - custom error - SyntaxError: Illegal character throw new Error('TODO - SyntaxError: Illegal character'); } + + isCondition = false; } if (cursor >= tokens.length) { diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts index 15b318d..78f0ec6 100644 --- a/src/tokenize/argument.ts +++ b/src/tokenize/argument.ts @@ -13,17 +13,12 @@ import tokenizeTextSpecial from './text-special'; import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; -export default ( - options: ParserOptions, - meta: any, - state: TokenizeState -) : boolean => { +export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { let { tokens, cursor } = state; const position = tokens[cursor]?.position; - let whitespaceStart = 0, whitespace = ''; const result : Token[] = []; @@ -40,12 +35,12 @@ export default ( }; if ( - tokenizeTextEscapeSingle(mockState, ['"', '$', '\\', ',', ']',]) || - tokenizeTextEscapeBlock(options, meta, mockState) || - tokenizeTextQuoted(options, meta, mockState) || - tokenizeTextSpecial(options, mockState) || - tokenizeFunctionIf(options, meta, mockState) || - tokenizeFunction(options, meta, mockState) + await tokenizeTextEscapeSingle(mockState, ['"', '$', '\\', ',', ']',]) || + await tokenizeTextEscapeBlock(options, meta, mockState) || + await tokenizeTextQuoted(options, meta, mockState) || + await tokenizeTextSpecial(options, mockState) || + await tokenizeFunctionIf(options, meta, mockState) || + await tokenizeFunction(options, meta, mockState) ) { if (mockState.output) { let output : Token = mockState.output; @@ -137,7 +132,6 @@ export default ( throw new Error('TODO - SyntaxError: Illegal character'); } - state.tokens = tokens; state.cursor = cursor; state.output = new TokenList({ diff --git a/src/tokenize/condition.ts b/src/tokenize/condition.ts index d2b2ea4..cdc8ac5 100644 --- a/src/tokenize/condition.ts +++ b/src/tokenize/condition.ts @@ -2,7 +2,7 @@ import type ParserOptions from '../types/options'; import type { TokenizeState } from "./tokenize"; -export default (options: ParserOptions, meta: any, state: TokenizeState) : boolean => { +export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { /* // logical operator diff --git a/src/tokenize/function-if.ts b/src/tokenize/function-if.ts index f28d155..3776e72 100644 --- a/src/tokenize/function-if.ts +++ b/src/tokenize/function-if.ts @@ -1,80 +1,14 @@ import ParserOptions from '../types/options'; import TokenType from '../types/token-types'; -import type IPreToken from '../types/pre-token'; import Token from '../tokens/token'; import IfToken from '../tokens/token-function-if'; import type OperatorToken from '../tokens/token-operator'; import { type TokenizeState } from './tokenize'; -import tokenizeCondition from './condition'; -import tokenizeArgument from './argument'; +import tokenizeArgumentList from './argument-list'; -const consumeWhitespace = (tokens: IPreToken[], cursor: number) : number => { - while (cursor < tokens.length && /^\s$/.test(tokens[cursor].value)) { - cursor += 1; - } - return cursor; -}; - -const getNextArg = (options: ParserOptions, meta: any, state: TokenizeState) : TokenizeState => { - let { tokens, cursor } = state; - - cursor = consumeWhitespace(tokens, cursor); - if (cursor >= tokens.length) { - // TODO - custom error - SyntaxError: Unexpected end - throw new Error('TODO - SyntaxError: unexpected end'); - } - - // no argument - if (tokens[cursor].value === ']') { - return { tokens, cursor }; - } - - // no leading delimiter - if (tokens[cursor].value !== ',') { - // TODO - custom error - SyntaxError: Illegal character - throw new Error('TODO - SyntaxError: Illegal character'); - } - cursor += 1; - - if (cursor >= tokens.length) { - // TODO - custom error - SyntaxError: Unexpected end - throw new Error('TODO - SyntaxError: unexpected end'); - } - - const position = tokens[cursor].position; - - cursor = consumeWhitespace(tokens, cursor); - - // empty argument - if ( - tokens[cursor].value === ',' || - tokens[cursor].value === ']' - ) { - return { - tokens, - cursor: cursor + 1, - output: new Token({ - position, - type: TokenType.EMPTY - }) - }; - } - - const mockState : TokenizeState = { - tokens, - cursor - }; - tokenizeArgument(options, meta, mockState); - return mockState; -}; - -export default ( - options: ParserOptions, - meta: any, - state: TokenizeState -) : boolean => { +export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { let { tokens, cursor } = state; @@ -88,87 +22,44 @@ export default ( const position = tokens[cursor]?.position; - cursor = consumeWhitespace(tokens, cursor + 3); - - if (cursor >= tokens.length) { - // TODO - custom error - SyntaxError: Unexpected end - throw new Error('TODO - SyntaxError: unexpected end'); + const mockState : TokenizeState = { + tokens, + cursor, + meta: { isConditional: true } } + await tokenizeArgumentList(options, meta, mockState); - // empty $if[] - if (tokens[cursor].value === ']') { - cursor += 1; - state.cursor = cursor; - state.output = new Token({ - position, - type: TokenType.EMPTY - }); - return true; - } + const args = mockState.output; - let mockState : TokenizeState = { - tokens, - cursor - }; - if (!tokenizeCondition(options, meta, state)) { - // TODO - custom error - SyntaxError: Invalid Condition - throw new Error('TODO - SyntaxError: Invalid Condition'); + if (args.length > 3) { + // TODO - custom error - SyntaxError: expected end of arguments + throw new Error('TODO - SyntaxError: Expected end of arguments') } - tokens = mockState.tokens; - cursor = mockState.cursor; - const condition : OperatorToken = mockState.output; - if (tokens[cursor] == null) { - // TODO - custom error - SyntaxError: unexpected end - throw new Error('TODO - SyntaxError: unexpected end'); + if (args.length === 3 && args[2].type === TokenType.EMPTY) { + args.pop(); } + if (args.length === 2 && args[1].type === TokenType.EMPTY) { + args.pop(); + } + + state.tokens = mockState.tokens; + state.cursor = mockState.cursor; - // No return values - $if[] - if (tokens[cursor].value === ']') { - state.tokens = tokens; - state.cursor = cursor + 1; + // $if[] or $if[] or $if[,] or $if[,,] + if (args.length < 2) { state.output = new Token({ position: position, type: TokenType.EMPTY }); - return true; - } - - if (tokens[cursor]?.value !== ',') { - // TODO - custom error - SyntaxError: Illegal character - throw new Error('TODO - SyntaxError: Illegal character'); - } - - const stateWhenTrue = getNextArg(options, meta, { tokens, cursor }); - const stateWhenFalse = getNextArg(options, meta, { ...stateWhenTrue }); - tokens = stateWhenFalse.tokens; - cursor = stateWhenFalse.cursor; - - if (tokens[cursor].value !== ']') { - // TODO - custom error - SyntaxError: Illegal character - throw new Error('TODO - SyntaxError: Illegal character'); - } - - state.tokens = tokens; - state.cursor = cursor + 1; - if (stateWhenFalse.output != null && (stateWhenFalse.output).type !== TokenType.EMPTY) { - state.output = new IfToken({ - position, - condition, - whenTrue: stateWhenTrue.output, - whenFalse: stateWhenFalse.output - }); - } else if (stateWhenTrue.output != null && (stateWhenTrue.output).type !== TokenType.EMPTY) { + // $if[, ] or $if[, ,] or $if[, , ] + } else { state.output = new IfToken({ position, - condition, - whenTrue: stateWhenTrue.output - }); - } else { - state.output = new Token({ - position: position, - type: TokenType.EMPTY + condition: args[0], + whenTrue: args[1], + whenFalse: args[2] }); } diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index 3261c9e..421a8e3 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -10,11 +10,7 @@ import type { TokenizeState } from './tokenize'; const nameCheck = /^([a-z][a-z\d]{2,})$/i; -export default ( - options: ParserOptions, - meta: any, - state: TokenizeState -) : boolean => { +export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { let { tokens, cursor } = state; @@ -48,7 +44,11 @@ export default ( } const lookupHandler = options.functionalHandlers[prefix]; - if (!lookupHandler?.(varName)) { + if (lookupHandler == null) { + return false; + } + const handler = await lookupHandler(varName); + if (!handler) { return false; } @@ -57,7 +57,7 @@ export default ( cursor } - tokenizeArgumentList(options, meta, mockState); + await tokenizeArgumentList(options, meta, mockState); state.tokens = mockState.tokens; state.output = new FunctionalToken({ diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index 55af727..565be51 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -10,11 +10,7 @@ import tokenizeFunction from './function'; import type { TokenizeState } from './tokenize'; -export default ( - options: ParserOptions, - meta: any, - state: TokenizeState, -) : boolean => { +export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { let { tokens, cursor } = state; if (tokens[cursor]?.value !== '``') { @@ -39,8 +35,8 @@ export default ( }; if ( - tokenizeFunctionIf(options, meta, mockState) || - tokenizeFunction(options, meta, mockState) + await tokenizeFunctionIf(options, meta, mockState) || + await tokenizeFunction(options, meta, mockState) ) { if (mockState.output) { escTokens.push(mockState.output); diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index 7abbfc8..422776d 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -2,10 +2,7 @@ import TextToken from '../tokens/token-text'; import type { TokenizeState } from './tokenize'; -export default ( - state: TokenizeState, - characters?: string[] -) : boolean => { +export default async (state: TokenizeState, characters?: string[]) : Promise => { let { tokens, cursor } = state; if (characters == null) { diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index d393cd0..42b82f7 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -12,11 +12,11 @@ import tokenizeFunction from './function'; import type { TokenizeState } from './tokenize'; -export default ( +export default async ( options: ParserOptions, meta: any, state: TokenizeState -) : boolean => { +) : Promise => { let { tokens, cursor } = state; if (tokens[cursor]?.value !== '"') { @@ -45,10 +45,10 @@ export default ( cursor }; if ( - tokenizeEscapeSingle(mockState, ['\\', '"']) || - tokenizeTextSpecial(options, mockState) || - tokenizeFunctionIf(options, meta, mockState) || - tokenizeFunction(options, meta, mockState) + await tokenizeEscapeSingle(mockState, ['\\', '"']) || + await tokenizeTextSpecial(options, mockState) || + await tokenizeFunctionIf(options, meta, mockState) || + await tokenizeFunction(options, meta, mockState) ) { if (mockState.output != null) { diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index 986390e..e0aa496 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -6,10 +6,7 @@ import TextToken from '../tokens/token-text'; import type { TokenizeState } from './tokenize'; -export default ( - options: ParserOptions, - state: TokenizeState -) : boolean => { +export default async (options: ParserOptions, state: TokenizeState) : Promise => { if (!options.specialSequences) { return false; } @@ -18,7 +15,7 @@ export default ( 'n': '\n', 'r': '\r', 't': '\t' - } + }; let { tokens, cursor } = state; if ( diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index ef1b146..e21dd3a 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -18,10 +18,11 @@ import tokenizeFunction from './function'; export interface TokenizeState { tokens: IPreToken[]; cursor: number; + meta?: Record; output?: Token | Token[]; } -export default (subject: string, options: ParserOptions, meta: any = {}) : TokenList => { +export default async (subject: string, options: ParserOptions, meta: any = {}) : Promise => { let tokens = getPotentialTokens(options, subject); @@ -41,12 +42,12 @@ export default (subject: string, options: ParserOptions, meta: any = {}) : Token }; if ( - tokenizeTextEscapeSingle(mockState) || - tokenizeTextEscapeBlock(options, meta, mockState) || - tokenizeTextQuoted(options, meta, mockState) || - tokenizeTextSpecial(options, mockState) || - tokenizeFunctionIf(options, meta, mockState) || - tokenizeFunction(options, meta, mockState) + await tokenizeTextEscapeSingle(mockState) || + await tokenizeTextEscapeBlock(options, meta, mockState) || + await tokenizeTextQuoted(options, meta, mockState) || + await tokenizeTextSpecial(options, mockState) || + await tokenizeFunctionIf(options, meta, mockState) || + await tokenizeFunction(options, meta, mockState) ) { let lastToken : Token = result[result.length - 1]; if ( From 7f84f5a37f6ee3617ca0ea7d1d61aae546c29752 Mon Sep 17 00:00:00 2001 From: SReject Date: Fri, 16 Sep 2022 13:21:41 -0400 Subject: [PATCH 37/92] implement: stack and error --- src/index.ts | 9 ++++++++- src/tokenize/argument-list.ts | 16 ++++++++-------- src/tokenize/argument.ts | 17 +++++++++-------- src/tokenize/function-if.ts | 11 +++++++---- src/tokenize/function.ts | 5 +++-- src/tokenize/text-escape-block.ts | 12 ++++++------ src/tokenize/text-quoted.ts | 15 +++++++-------- src/tokenize/tokenize.ts | 4 +++- 8 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/index.ts b/src/index.ts index d4ff10c..a156612 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,8 @@ -export { default as tokenize } from './tokenize/tokenize'; \ No newline at end of file +export { default as tokenize } from './tokenize/tokenize'; + +export { + ExpressionError, + ExpressionArgumentsError, + ExpressionSyntaxError, + ExpressionVariableError +} from './errors'; \ No newline at end of file diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index 1c0e36b..d4c82ac 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -7,9 +7,11 @@ import type { TokenizeState } from "./tokenize"; import tokenizeArgument from './argument'; import tokenizeCondition from './condition'; +import { ExpressionSyntaxError } from '../errors'; + export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { - let { tokens, cursor, meta: stateMeta } = state; + let { tokens, cursor, stack, meta: stateMeta } = state; let isCondition = stateMeta.isConditional || false; @@ -45,7 +47,8 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : const mockState : TokenizeState = { tokens, - cursor + cursor, + stack: [...stack, args.length] }; if ( @@ -65,21 +68,18 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : cursor = mockState.cursor; } else { - // TODO - custom error - SyntaxError: Illegal character - throw new Error('TODO - SyntaxError: Illegal character'); + throw new ExpressionSyntaxError('illegal character; expected end of argument', tokens[cursor].position); } isCondition = false; } if (cursor >= tokens.length) { - // TODO - custom error - SyntaxError: Unexpected end - throw new Error('TODO - SyntaxError: Unexpected end'); + throw new ExpressionSyntaxError('unexpected end'); } if (tokens[cursor].value !== ']') { - // TODO - custom error - SyntaxError: Expected ']' - throw new Error('TODO - Syntax Error: Expected \']\''); + throw new ExpressionSyntaxError('illegal character; expected \]', tokens[cursor].position); } state.tokens = tokens; diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts index 78f0ec6..b0f1b98 100644 --- a/src/tokenize/argument.ts +++ b/src/tokenize/argument.ts @@ -13,9 +13,11 @@ import tokenizeTextSpecial from './text-special'; import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; +import { ExpressionSyntaxError } from '../errors'; + export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { - let { tokens, cursor } = state; + let { tokens, cursor, stack } = state; const position = tokens[cursor]?.position; @@ -31,7 +33,8 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : const mockState : TokenizeState = { tokens, - cursor + cursor, + stack: [...stack] }; if ( @@ -122,14 +125,12 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : } if (cursor >= tokens.length) { - // TODO - custom error - Syntax Error: Unexpected End - throw new Error('TODO - SyntaxError: Unexpected end'); + throw new ExpressionSyntaxError('unexpected end of expression'); } - const next = tokens[cursor + 1].value; - if (next !== ',' && next !== ']') { - // TODO - custom error - Syntax Error: Illegal character - throw new Error('TODO - SyntaxError: Illegal character'); + const next = tokens[cursor + 1]; + if (next.value !== ',' && next.value !== ']') { + throw new ExpressionSyntaxError('illegal character', next.position, next.value[0]); } state.tokens = tokens; diff --git a/src/tokenize/function-if.ts b/src/tokenize/function-if.ts index 3776e72..dc81f1b 100644 --- a/src/tokenize/function-if.ts +++ b/src/tokenize/function-if.ts @@ -8,9 +8,11 @@ import type OperatorToken from '../tokens/token-operator'; import { type TokenizeState } from './tokenize'; import tokenizeArgumentList from './argument-list'; +import { ExpressionSyntaxError } from '../errors'; + export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { - let { tokens, cursor } = state; + let { tokens, cursor, stack } = state; if ( tokens[cursor]?.value !== '$' || @@ -20,11 +22,13 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : return false; } - const position = tokens[cursor]?.position; + const position = tokens[cursor].position; + cursor += 2; const mockState : TokenizeState = { tokens, cursor, + stack: [...stack, '$if'], meta: { isConditional: true } } await tokenizeArgumentList(options, meta, mockState); @@ -32,8 +36,7 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : const args = mockState.output; if (args.length > 3) { - // TODO - custom error - SyntaxError: expected end of arguments - throw new Error('TODO - SyntaxError: Expected end of arguments') + throw new ExpressionSyntaxError('Expected end of arguments', args[3].position); } if (args.length === 3 && args[2].type === TokenType.EMPTY) { diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index 421a8e3..78be626 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -12,7 +12,7 @@ const nameCheck = /^([a-z][a-z\d]{2,})$/i; export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { - let { tokens, cursor } = state; + let { tokens, cursor, stack } = state; if (tokens[cursor]?.value !== '$' || tokens[cursor + 1] == null) { return false; @@ -54,7 +54,8 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : const mockState : TokenizeState = { tokens, - cursor + cursor, + stack: [...stack, `${prefix}${varName}`] } await tokenizeArgumentList(options, meta, mockState); diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index 565be51..11f46aa 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -9,9 +9,10 @@ import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; import type { TokenizeState } from './tokenize'; +import { ExpressionSyntaxError } from '../errors'; export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { - let { tokens, cursor } = state; + let { tokens, cursor, stack } = state; if (tokens[cursor]?.value !== '``') { return false; @@ -22,8 +23,7 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : cursor += 1; if (cursor < (tokens.length - 1)) { - // TODO - custom error - throw new Error('TODO - Syntax Error: Unexpected end of statement'); + throw new ExpressionSyntaxError('unexpected end of expression'); } const escTokens : Token[] = []; @@ -31,7 +31,8 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : const mockState : TokenizeState = { tokens, - cursor + cursor, + stack: [...stack, 'text-escape-block'] }; if ( @@ -61,8 +62,7 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : } if (tokens[cursor].value !== '``') { - // TODO - custom error - throw new Error('TODO - Syntax Error: Unexpected end'); + throw new ExpressionSyntaxError('unexpected end of expression'); } state.tokens = tokens; diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 42b82f7..be7d498 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -11,13 +11,14 @@ import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; import type { TokenizeState } from './tokenize'; +import { ExpressionSyntaxError } from '../errors'; export default async ( options: ParserOptions, meta: any, state: TokenizeState ) : Promise => { - let { tokens, cursor } = state; + let { tokens, cursor, stack } = state; if (tokens[cursor]?.value !== '"') { return false; @@ -28,8 +29,7 @@ export default async ( cursor += 1; if (cursor === tokens.length) { - // TODO - custom error - unexpected end - throw new Error('TODO - Syntax Error: unexpected end'); + throw new ExpressionSyntaxError('unexpected end of expression'); } const quoteTokens : Token[] = []; @@ -42,7 +42,8 @@ export default async ( const mockState : TokenizeState = { tokens, - cursor + cursor, + stack: [...stack, 'text-quoted'] }; if ( await tokenizeEscapeSingle(mockState, ['\\', '"']) || @@ -81,13 +82,11 @@ export default async ( } if (cursor >= tokens.length) { - // TODO - custom error - unexpected end - throw new Error('TODO - SyntaxError: unexpected end'); + throw new ExpressionSyntaxError('unexpected end of expression'); } if (tokens[cursor].value !== '"') { - // TODO - custom error - expected closing token - throw new Error('TODO - Syntax Error: expected closing quote'); + throw new ExpressionSyntaxError('expected closing quote', tokens[cursor].position); } state.tokens = tokens; diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts index e21dd3a..f727d4c 100644 --- a/src/tokenize/tokenize.ts +++ b/src/tokenize/tokenize.ts @@ -19,6 +19,7 @@ export interface TokenizeState { tokens: IPreToken[]; cursor: number; meta?: Record; + stack: Array; output?: Token | Token[]; } @@ -38,7 +39,8 @@ export default async (subject: string, options: ParserOptions, meta: any = {}) : let mockState : TokenizeState = { tokens: tokens, - cursor: cursor + cursor: cursor, + stack: [] }; if ( From 71697f88b3c217720dab684d1edf9465c49aa57a Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 17 Sep 2022 17:48:16 -0400 Subject: [PATCH 38/92] update mocha --- package-lock.json | 180 ++++++++++++++++++++++++++-------------------- package.json | 5 +- 2 files changed, 106 insertions(+), 79 deletions(-) diff --git a/package-lock.json b/package-lock.json index d5606cf..d3428f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "devDependencies": { "@types/node": "^18.7.18", "eslint": "^8.4.1", - "mocha": "^9.1.3", + "mocha": "^10.0.0", "typescript": "^4.8.3" } }, @@ -249,10 +249,16 @@ } }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -319,9 +325,9 @@ } }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -713,9 +719,9 @@ } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -759,15 +765,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1013,48 +1010,67 @@ } }, "node_modules/mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", + "glob": "7.2.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1062,9 +1078,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -1184,9 +1200,9 @@ } }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -1501,9 +1517,9 @@ } }, "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "node_modules/wrap-ansi": { @@ -1779,9 +1795,9 @@ } }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -1838,9 +1854,9 @@ } }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -2133,9 +2149,9 @@ "dev": true }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2164,12 +2180,6 @@ "type-fest": "^0.20.2" } }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2349,35 +2359,53 @@ } }, "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", + "glob": "7.2.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "ms": { @@ -2387,9 +2415,9 @@ "dev": true }, "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true }, "natural-compare": { @@ -2473,9 +2501,9 @@ "dev": true }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "prelude-ls": { @@ -2682,9 +2710,9 @@ "dev": true }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index cb02717..162948c 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,10 @@ "devDependencies": { "@types/node": "^18.7.18", "eslint": "^8.4.1", - "mocha": "^9.1.3", + "mocha": "^10.0.0", "typescript": "^4.8.3" }, - "files": [ "lib/" ] -} \ No newline at end of file +} From 89a4a088aa39eec863a80c13824c8bf572481a9a Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 17 Sep 2022 17:48:44 -0400 Subject: [PATCH 39/92] feat: implement external-face --- src/expression.ts | 35 +++++++ src/helpers/get-potential-tokens.ts | 12 +++ src/index.ts | 152 +++++++++++++++++++++++++++- src/tokenize/argument-list.ts | 16 ++- src/tokenize/argument.ts | 20 ++-- src/tokenize/condition.ts | 6 +- src/tokenize/function-if.ts | 14 +-- src/tokenize/function.ts | 64 ++++++------ src/tokenize/text-escape-block.ts | 14 +-- src/tokenize/text-escape-single.ts | 4 +- src/tokenize/text-quoted.ts | 20 ++-- src/tokenize/text-special.ts | 9 +- src/tokenize/tokenize.ts | 87 ---------------- src/tokens/token-function.ts | 14 +-- src/types/options.ts | 21 ++-- src/types/token-types.ts | 1 + src/types/tokenize-state.ts | 12 +++ 17 files changed, 307 insertions(+), 194 deletions(-) create mode 100644 src/expression.ts delete mode 100644 src/tokenize/tokenize.ts create mode 100644 src/types/tokenize-state.ts diff --git a/src/expression.ts b/src/expression.ts new file mode 100644 index 0000000..9d1ee21 --- /dev/null +++ b/src/expression.ts @@ -0,0 +1,35 @@ +import type ParserOptions from './types/options'; +import TokenType from './types/token-types'; + +import Token from './tokens/token'; + +interface IExpression { + options: ParserOptions; + value: Token[] +} + +export default class Expression extends Token { + private options: ParserOptions; + + constructor(token: IExpression) { + super({ + position: 0, + type: TokenType.EXPRESSION, + ...token + }); + this.options = token.options; + } + + async evaluate(meta: any = {}) : Promise { + let index = 0; + let result = ''; + while (index < this.value.length) { + const value = await this.value[index].evaluate(this.options, meta); + if (value != null) { + result += JSON.stringify(value); + } + index += 1; + } + return ''; + } +} \ No newline at end of file diff --git a/src/helpers/get-potential-tokens.ts b/src/helpers/get-potential-tokens.ts index 84198cd..f710a3f 100644 --- a/src/helpers/get-potential-tokens.ts +++ b/src/helpers/get-potential-tokens.ts @@ -6,6 +6,18 @@ import split from './unicode-safe-split'; /** Split input string into array of potential tokens */ export default (options: ParserOptions, subject: string) : IPreToken[] => { + // remove leading and trailing spaces + let startCursor = 0; + while (subject.length && subject[startCursor] === ' ') { + startCursor += 1; + } + let endCursor = subject.length - 1; + while (subject.length && subject[endCursor] === ' ') { + endCursor -= 1; + } + subject = subject.slice(startCursor, endCursor + 1); + + const result : IPreToken[] = []; let textToken : null | {position: number, value: string} = null; diff --git a/src/index.ts b/src/index.ts index a156612..4309853 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,156 @@ -export { default as tokenize } from './tokenize/tokenize'; +import has from './helpers/has'; +import getPotentialTokens from './helpers/get-potential-tokens'; + +import { + type default as ParserOptions, + type IFunctionHandler, + type IFunctionLookup +} from './types/options'; +import TokenType from './types/token-types'; +import ITokenizeState from './types/tokenize-state'; + +import Token from './tokens/token'; +import TextToken from './tokens/token-text'; + +import tokenizeTextEscapeSingle from './tokenize/text-escape-single'; +import tokenizeTextEscapeBlock from './tokenize/text-escape-block'; +import tokenizeTextQuoted from './tokenize/text-quoted'; +import tokenizeTextSpecial from './tokenize/text-special'; +import tokenizeFunctionIf from './tokenize/function-if'; +import tokenizeFunction from './tokenize/function'; + +import Expression from './expression'; export { ExpressionError, ExpressionArgumentsError, ExpressionSyntaxError, ExpressionVariableError -} from './errors'; \ No newline at end of file +} from './errors'; + +export class Expressionish { + private functionHandlers : Record; + private functionLookups : Record; + + private config: ParserOptions; + + constructor(options?: ParserOptions) { + this.functionHandlers = options.functionHandlers || {}; + this.functionLookups = options.functionLookups || {}; + + this.config = { + eol: options.eol || 'keep', + specialSequences: options.specialSequences || true + }; + } + + registerFunction(name: string, handler: IFunctionHandler) : void { + name = name.toLowerCase(); + if (has(this.functionHandlers, name)) { + throw new Error(`'$${name}' already registered`); + } + this.functionHandlers[name] = handler; + } + + unregisterFunction(name: string, handler: IFunctionHandler) : void { + name = name.toLowerCase(); + if (has(this.functionHandlers, name) && this.functionHandlers[name] === handler) { + delete this.functionHandlers[name]; + } + } + + registerLookup(prefix: string, handler: IFunctionLookup) : void { + prefix = prefix.toLowerCase(); + if (has(this.functionLookups, prefix)) { + throw new Error(`prefix '$${prefix}' already registered`); + } + this.functionLookups[prefix] = handler; + } + + unregisterLookup(prefix: string, handler: IFunctionLookup) : void { + prefix = prefix.toLowerCase(); + if (has(this.functionLookups, prefix) && this.functionLookups[prefix] === handler) { + delete this.functionLookups[prefix]; + } + } + + async tokenize(subject: string) : Promise { + let tokens = getPotentialTokens( + this.config, + subject + ); + let cursor = 0; + + const result : Token[] = []; + + while (cursor < tokens.length) { + + const mockState : ITokenizeState = { + options: { + ...(this.config), + functionHandlers: { ...(this.functionHandlers) }, + functionLookups: { ...(this.functionLookups)} + }, + tokens: [...tokens], + cursor, + stack: [] + }; + + if ( + await tokenizeTextEscapeSingle(mockState) || + await tokenizeTextEscapeBlock(mockState) || + await tokenizeTextQuoted(mockState) || + await tokenizeTextSpecial(mockState) || + await tokenizeFunctionIf(mockState) || + await tokenizeFunction(mockState) + ) { + let lastToken : Token = result[result.length - 1]; + if ( + lastToken != null && + lastToken.type === TokenType.TEXT && + mockState.output && + (mockState.output).type === TokenType.TEXT + ) { + lastToken.value += (mockState.output).value; + + } else { + result.push(mockState.output); + } + + tokens = mockState.tokens; + cursor = mockState.cursor; + continue; + } + + // Assume anything else is plain text + const last : Token = result[result.length - 1]; + if (last != null && last.type === TokenType.TEXT) { + last.value += tokens[cursor].value; + + } else { + result.push(new TextToken(tokens[cursor])); + } + + cursor += 1; + } + + return new Expression({ + options: { + ...(this.config), + functionHandlers: { ...(this.functionHandlers) }, + functionLookups: { ...(this.functionLookups)} + }, + value: result + }); + } + + static async tokenize(subject: string, options?: ParserOptions) : Promise { + const parser = new Expressionish(options); + return parser.tokenize(subject); + } + + static async evaluate(subject: string, meta: any = {}, options?: ParserOptions) : Promise { + const tokens = await Expressionish.tokenize(subject, options); + return tokens.evaluate(meta); + } +} \ No newline at end of file diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index d4c82ac..2b0a2dd 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -1,19 +1,16 @@ -import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; +import type ITokenizeState from '../types/tokenize-state'; import Token from '../tokens/token'; -import type { TokenizeState } from "./tokenize"; import tokenizeArgument from './argument'; import tokenizeCondition from './condition'; import { ExpressionSyntaxError } from '../errors'; -export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { +export default async (state: ITokenizeState, isCondition: boolean = false) : Promise => { - let { tokens, cursor, stack, meta: stateMeta } = state; - - let isCondition = stateMeta.isConditional || false; + let { tokens, cursor, stack, options } = state; if (tokens[cursor]?.value !== '[') { return false; @@ -45,15 +42,16 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : continue; } - const mockState : TokenizeState = { + const mockState : ITokenizeState = { + options: { ...options }, tokens, cursor, stack: [...stack, args.length] }; if ( - (isCondition && await tokenizeCondition(options, meta, mockState)) || - (!isCondition && await tokenizeArgument(options, meta, mockState)) + (isCondition && await tokenizeCondition(mockState)) || + (!isCondition && await tokenizeArgument(mockState)) ) { if (mockState.output) { args.push(mockState.output); diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts index b0f1b98..5771707 100644 --- a/src/tokenize/argument.ts +++ b/src/tokenize/argument.ts @@ -1,11 +1,10 @@ -import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; +import type ITokenizeState from '../types/tokenize-state'; import type Token from '../tokens/token'; import TokenList from '../tokens/token-list'; import TextToken from '../tokens/token-text'; -import type { TokenizeState } from './tokenize'; import tokenizeTextEscapeSingle from './text-escape-single'; import tokenizeTextEscapeBlock from './text-escape-block'; import tokenizeTextQuoted from './text-quoted'; @@ -15,9 +14,9 @@ import tokenizeFunction from './function'; import { ExpressionSyntaxError } from '../errors'; -export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { +export default async (state: ITokenizeState) : Promise => { - let { tokens, cursor, stack } = state; + let { tokens, cursor, stack, options } = state; const position = tokens[cursor]?.position; @@ -31,7 +30,8 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : ) { const lastToken : Token | void = result[result.length - 1]; - const mockState : TokenizeState = { + const mockState : ITokenizeState = { + options: { ...options }, tokens, cursor, stack: [...stack] @@ -39,11 +39,11 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : if ( await tokenizeTextEscapeSingle(mockState, ['"', '$', '\\', ',', ']',]) || - await tokenizeTextEscapeBlock(options, meta, mockState) || - await tokenizeTextQuoted(options, meta, mockState) || - await tokenizeTextSpecial(options, mockState) || - await tokenizeFunctionIf(options, meta, mockState) || - await tokenizeFunction(options, meta, mockState) + await tokenizeTextEscapeBlock(mockState) || + await tokenizeTextQuoted(mockState) || + await tokenizeTextSpecial(mockState) || + await tokenizeFunctionIf(mockState) || + await tokenizeFunction(mockState) ) { if (mockState.output) { let output : Token = mockState.output; diff --git a/src/tokenize/condition.ts b/src/tokenize/condition.ts index cdc8ac5..65d5a78 100644 --- a/src/tokenize/condition.ts +++ b/src/tokenize/condition.ts @@ -1,8 +1,8 @@ -import type ParserOptions from '../types/options'; +import type ITokenizeState from '../types/tokenize-state'; -import type { TokenizeState } from "./tokenize"; +export default async (state: ITokenizeState) : Promise => { -export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { + const { options, tokens, cursor, stack } = state; /* // logical operator diff --git a/src/tokenize/function-if.ts b/src/tokenize/function-if.ts index dc81f1b..79b0906 100644 --- a/src/tokenize/function-if.ts +++ b/src/tokenize/function-if.ts @@ -5,14 +5,14 @@ import Token from '../tokens/token'; import IfToken from '../tokens/token-function-if'; import type OperatorToken from '../tokens/token-operator'; -import { type TokenizeState } from './tokenize'; +import type ITokenizeState from '../types/tokenize-state'; import tokenizeArgumentList from './argument-list'; import { ExpressionSyntaxError } from '../errors'; -export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { +export default async (state: ITokenizeState) : Promise => { - let { tokens, cursor, stack } = state; + let { tokens, cursor, stack, options } = state; if ( tokens[cursor]?.value !== '$' || @@ -25,13 +25,13 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : const position = tokens[cursor].position; cursor += 2; - const mockState : TokenizeState = { + const mockState : ITokenizeState = { + options: { ...options }, tokens, cursor, - stack: [...stack, '$if'], - meta: { isConditional: true } + stack: [...stack, '$if'] } - await tokenizeArgumentList(options, meta, mockState); + await tokenizeArgumentList(mockState, true); const args = mockState.output; diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index 78be626..a5cd291 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -1,71 +1,67 @@ import has from '../helpers/has'; -import type ParserOptions from '../types/options'; +import { + type IFunctionLookup +} from '../types/options'; + +import type ITokenizeState from '../types/tokenize-state'; import type Token from '../tokens/token'; import FunctionalToken from '../tokens/token-function'; import tokenizeArgumentList from './argument-list'; -import type { TokenizeState } from './tokenize'; const nameCheck = /^([a-z][a-z\d]{2,})$/i; -export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { - - let { tokens, cursor, stack } = state; +export default async (state: ITokenizeState) : Promise => { + let { tokens, cursor, stack, options } = state; if (tokens[cursor]?.value !== '$' || tokens[cursor + 1] == null) { return false; } const position = tokens[cursor].position; - let prefix = '$'; cursor += 1; - let varName = tokens[cursor].value; - if (has(options.functionalHandlers,`$${varName}`)) { - prefix += tokens[cursor].value; - cursor += 1; - - if (cursor >= tokens.length) { - return false; - } - - varName = tokens[cursor].value; + let prefix = '$', + varname : string = tokens[cursor].value.toLowerCase(), + lookupFn : IFunctionLookup; - } else if ((cursor + 1) < tokens.length) { - cursor += 1; - varName += tokens[cursor].value; - } cursor += 1; - if (!nameCheck.test(varName)) { - return false; - } + if (has(options.functionHandlers, varname)) { + const handler = options.functionHandlers[varname]; + lookupFn = () => handler; + + } else if ( + cursor >= tokens.length && + has(options.functionLookups, varname) + ) { + lookupFn = options.functionLookups[varname] + prefix += varname; + varname = tokens[cursor].value.toLowerCase(); + cursor += 1; - const lookupHandler = options.functionalHandlers[prefix]; - if (lookupHandler == null) { - return false; - } - const handler = await lookupHandler(varName); - if (!handler) { + } else { return false; } - const mockState : TokenizeState = { + const mockState : ITokenizeState = { + options: { ...options }, tokens, cursor, - stack: [...stack, `${prefix}${varName}`] + stack: [ ...stack, `${prefix}${varname}`] } - await tokenizeArgumentList(options, meta, mockState); + await tokenizeArgumentList(mockState); state.tokens = mockState.tokens; state.output = new FunctionalToken({ position, prefix, - value: varName, - arguments: (mockState.output || []) + value: varname, + arguments: (mockState.output || []), + lookupFn: lookupFn }); state.cursor = mockState.cursor; diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index 11f46aa..42d085e 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -1,4 +1,3 @@ -import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; import type Token from '../tokens/token'; @@ -8,10 +7,10 @@ import TextToken from '../tokens/token-text'; import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; -import type { TokenizeState } from './tokenize'; +import type ITokenizeState from '../types/tokenize-state'; import { ExpressionSyntaxError } from '../errors'; -export default async (options: ParserOptions, meta: any, state: TokenizeState) : Promise => { +export default async (state: ITokenizeState) : Promise => { let { tokens, cursor, stack } = state; if (tokens[cursor]?.value !== '``') { @@ -29,15 +28,16 @@ export default async (options: ParserOptions, meta: any, state: TokenizeState) : const escTokens : Token[] = []; while (cursor < tokens.length && tokens[cursor].value !== '``') { - const mockState : TokenizeState = { + const mockState : ITokenizeState = { + options: { ...(state.options) }, tokens, cursor, - stack: [...stack, 'text-escape-block'] + stack: [ ...stack, 'text-escape-block' ] }; if ( - await tokenizeFunctionIf(options, meta, mockState) || - await tokenizeFunction(options, meta, mockState) + await tokenizeFunctionIf(mockState) || + await tokenizeFunction(mockState) ) { if (mockState.output) { escTokens.push(mockState.output); diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index 422776d..1846cad 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -1,8 +1,8 @@ import TextToken from '../tokens/token-text'; -import type { TokenizeState } from './tokenize'; +import type ITokenizeState from '../types/tokenize-state'; -export default async (state: TokenizeState, characters?: string[]) : Promise => { +export default async (state: ITokenizeState, characters?: string[]) : Promise => { let { tokens, cursor } = state; if (characters == null) { diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index be7d498..5a95576 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -1,4 +1,3 @@ -import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; import type Token from '../tokens/token'; @@ -10,14 +9,12 @@ import tokenizeTextSpecial from './text-special'; import tokenizeFunctionIf from './function-if'; import tokenizeFunction from './function'; -import type { TokenizeState } from './tokenize'; +import type ITokenizeState from '../types/tokenize-state'; + import { ExpressionSyntaxError } from '../errors'; -export default async ( - options: ParserOptions, - meta: any, - state: TokenizeState -) : Promise => { +export default async (state: ITokenizeState) : Promise => { + let { tokens, cursor, stack } = state; if (tokens[cursor]?.value !== '"') { @@ -40,16 +37,17 @@ export default async ( let lastToken : Token = quoteTokens[quoteTokens.length - 1]; - const mockState : TokenizeState = { + const mockState : ITokenizeState = { + options: {...(state.options)}, tokens, cursor, stack: [...stack, 'text-quoted'] }; if ( await tokenizeEscapeSingle(mockState, ['\\', '"']) || - await tokenizeTextSpecial(options, mockState) || - await tokenizeFunctionIf(options, meta, mockState) || - await tokenizeFunction(options, meta, mockState) + await tokenizeTextSpecial(mockState) || + await tokenizeFunctionIf(mockState) || + await tokenizeFunction(mockState) ) { if (mockState.output != null) { diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index e0aa496..e84c0d7 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -1,13 +1,10 @@ -import type ParserOptions from '../types/options'; - import has from '../helpers/has'; +import type ITokenizeState from '../types/tokenize-state'; import TextToken from '../tokens/token-text'; -import type { TokenizeState } from './tokenize'; - -export default async (options: ParserOptions, state: TokenizeState) : Promise => { - if (!options.specialSequences) { +export default async (state: ITokenizeState) : Promise => { + if (!state.options.specialSequences) { return false; } diff --git a/src/tokenize/tokenize.ts b/src/tokenize/tokenize.ts deleted file mode 100644 index f727d4c..0000000 --- a/src/tokenize/tokenize.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type ParserOptions from '../types/options'; -import TokenType from '../types/token-types'; -import type IPreToken from '../types/pre-token'; - -import getPotentialTokens from '../helpers/get-potential-tokens'; - -import Token from '../tokens/token'; -import TokenList, { ITokenList } from '../tokens/token-list'; -import TextToken from '../tokens/token-text'; - -import tokenizeTextEscapeSingle from './text-escape-single'; -import tokenizeTextEscapeBlock from './text-escape-block'; -import tokenizeTextQuoted from './text-quoted'; -import tokenizeTextSpecial from './text-special'; -import tokenizeFunctionIf from './function-if'; -import tokenizeFunction from './function'; - -export interface TokenizeState { - tokens: IPreToken[]; - cursor: number; - meta?: Record; - stack: Array; - output?: Token | Token[]; -} - -export default async (subject: string, options: ParserOptions, meta: any = {}) : Promise => { - - let tokens = getPotentialTokens(options, subject); - - const result : Array = []; - - let cursor = 0; - - // trim leading and trailing spaces - while (tokens.length && tokens[0].value === ' ') tokens.shift(); - while (tokens.length && tokens[tokens.length - 1].value === ' ') tokens.pop(); - - while (cursor < tokens.length) { - - let mockState : TokenizeState = { - tokens: tokens, - cursor: cursor, - stack: [] - }; - - if ( - await tokenizeTextEscapeSingle(mockState) || - await tokenizeTextEscapeBlock(options, meta, mockState) || - await tokenizeTextQuoted(options, meta, mockState) || - await tokenizeTextSpecial(options, mockState) || - await tokenizeFunctionIf(options, meta, mockState) || - await tokenizeFunction(options, meta, mockState) - ) { - let lastToken : Token = result[result.length - 1]; - if ( - lastToken != null && - lastToken.type === TokenType.TEXT && - mockState.output && - (mockState.output).type === TokenType.TEXT - ) { - lastToken.value += (mockState.output).value; - - } else { - result.push(mockState.output); - } - - tokens = mockState.tokens; - cursor = mockState.cursor; - continue; - } - - // Assume anything else is plain text - const last : Token = result[result.length - 1]; - if (last != null && last.type === TokenType.TEXT) { - last.value += tokens[cursor].value; - - } else { - result.push(new TextToken(tokens[cursor])); - } - - cursor += 1; - } - - return new TokenList({ - value: result - }); -} \ No newline at end of file diff --git a/src/tokens/token-function.ts b/src/tokens/token-function.ts index 5089653..79f4a2d 100644 --- a/src/tokens/token-function.ts +++ b/src/tokens/token-function.ts @@ -1,5 +1,5 @@ import TokenType from '../types/token-types'; -import ParserOptions from '../types/options'; +import ParserOptions, { IFunctionHandler, IFunctionLookup } from '../types/options'; import Token from './token'; export interface IFunctionalToken { @@ -7,11 +7,13 @@ export interface IFunctionalToken { prefix: string; value: string; arguments: Token[]; + lookupFn: IFunctionLookup; } export default class FunctionalToken extends Token { public prefix: string; public arguments: Token[]; + public lookupFn : IFunctionLookup; constructor(token: IFunctionalToken) { super({ @@ -20,17 +22,11 @@ export default class FunctionalToken extends Token { }); this.prefix = token.prefix; this.arguments = token.arguments; + this.lookupFn = token.lookupFn; } async evaluate(options: ParserOptions, meta: any = {}) : Promise { - const lookupHandler = options.functionalHandlers[this.prefix]; - - if (lookupHandler == null) { - // TODO: custom errors - throw new Error(`TODO - no lookup handler for ${this.prefix}`); - } - const handler = lookupHandler(this.value, meta); - + const handler = this.lookupFn(this.value); if (handler == null) { // TODO: custom errors throw new Error(`TODO - No handler for ${this.prefix}${this.value}`); diff --git a/src/types/options.ts b/src/types/options.ts index e6771ff..cffe866 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -1,15 +1,22 @@ -interface Handler { - argsCheck?: (meta: any, ...args: any[]) => any; - evaluator: (meta: any, ...args: any[]) => any; +export interface IFunctionHandler { + stackCheck?: (stack: string[]) => Promise; + argsCheck?: (meta: any, ...args: any[]) => Promise; + evaluator: (meta: any, ...args: any[]) => Promise; } -type LookupHandler = (name: string, meta?: any) => Handler | Promise; +export type IFunctionLookup = ( + name: string, + stack?: string[], + meta?: any +) => IFunctionHandler; export default interface ParserOptions { - functionalHandlers: Record; - verifyOnly?: boolean; - skipArgumentsCheck?: boolean; + functionHandlers?: Record; + functionLookups?: Record; eol?: 'error' | 'remove' | 'space' | 'keep'; specialSequences?: boolean; + + verifyOnly?: boolean; + skipArgumentsCheck?: boolean; }; \ No newline at end of file diff --git a/src/types/token-types.ts b/src/types/token-types.ts index bdd54ba..3852db0 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -1,4 +1,5 @@ const enum TokenType { + EXPRESSION, TOKENLIST, IFSTATEMENT, FUNCTIONAL, diff --git a/src/types/tokenize-state.ts b/src/types/tokenize-state.ts new file mode 100644 index 0000000..a150717 --- /dev/null +++ b/src/types/tokenize-state.ts @@ -0,0 +1,12 @@ +import ParserOptions from './options'; +import IPreToken from './pre-token'; + +import type Token from '../tokens/token'; + +export default interface ITokenizeState { + options: ParserOptions, + tokens: IPreToken[]; + cursor: number; + stack: Array; + output?: Token | Token[]; +} \ No newline at end of file From 6433be1fdbb99372126c3f31d3db42306e457ed2 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 17 Sep 2022 18:18:43 -0400 Subject: [PATCH 40/92] chore: eslint codebase --- .eslintrc.js | 23 +- package-lock.json | 749 ++++++++++++++++++++++++++++- package.json | 2 + src/expression.ts | 2 +- src/helpers/unicode-safe-split.ts | 2 +- src/index.ts | 2 +- src/tokenize/argument-list.ts | 10 +- src/tokenize/argument.ts | 8 +- src/tokenize/function-if.ts | 5 +- src/tokenize/function.ts | 5 +- src/tokenize/text-escape-block.ts | 3 +- src/tokenize/text-escape-single.ts | 2 +- src/tokenize/text-quoted.ts | 5 +- src/tokenize/text-special.ts | 2 +- src/tokens/comparison/base.ts | 3 +- src/tokens/comparison/exists.ts | 2 +- src/tokens/comparison/isbool.ts | 2 +- src/tokens/comparison/isnull.ts | 2 +- src/tokens/comparison/numerical.ts | 2 +- src/tokens/comparison/wildcard.ts | 6 +- src/tokens/logical/logical-and.ts | 2 +- src/tokens/token-function.ts | 16 +- src/tokens/token-list.ts | 6 +- src/tokens/token-operator.ts | 3 +- src/tokens/token-text.ts | 2 +- src/tokens/token.ts | 1 + src/types/options.ts | 8 +- src/types/pre-token.ts | 2 +- src/types/token-types.ts | 2 +- src/types/tokenize-state.ts | 5 +- 30 files changed, 807 insertions(+), 77 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b587d60..49cbf63 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,16 +1,27 @@ module.exports = { + "extends": [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended' + ], + "parser": '@typescript-eslint/parser', + "plugins": [ + '@typescript-eslint' + ], "env": { "node": true, "es2020": true }, - "parserOptions": { "sourceType": "module" }, - - "extends": "eslint:recommended", - "rules": { - "linebreak-style": ["error", "unix"] - } + "linebreak-style": ["error", "unix"], + "@typescript-eslint/no-explicit-any": "off" + }, + + "ignorePatterns": [ + "node_modules/", + "lib/", + "test/", + ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d3428f9..f3268bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "license": "ISC", "devDependencies": { "@types/node": "^18.7.18", + "@typescript-eslint/eslint-plugin": "^5.37.0", + "@typescript-eslint/parser": "^5.37.0", "eslint": "^8.4.1", "mocha": "^10.0.0", "typescript": "^4.8.3" @@ -55,12 +57,269 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "node_modules/@types/node": { "version": "18.7.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", "dev": true }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.37.0.tgz", + "integrity": "sha512-Fde6W0IafXktz1UlnhGkrrmnnGpAo1kyX7dnyHHVrmwJOn72Oqm3eYtddrpOwwel2W8PAK9F3pIL5S+lfoM0og==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.37.0", + "@typescript-eslint/type-utils": "5.37.0", + "@typescript-eslint/utils": "5.37.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.37.0.tgz", + "integrity": "sha512-01VzI/ipYKuaG5PkE5+qyJ6m02fVALmMPY3Qq5BHflDx3y4VobbLdHQkSMg9VPRS4KdNt4oYTMaomFoHonBGAw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.37.0", + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/typescript-estree": "5.37.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.37.0.tgz", + "integrity": "sha512-F67MqrmSXGd/eZnujjtkPgBQzgespu/iCZ+54Ok9X5tALb9L2v3G+QBSoWkXG0p3lcTJsL+iXz5eLUEdSiJU9Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/visitor-keys": "5.37.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.37.0.tgz", + "integrity": "sha512-BSx/O0Z0SXOF5tY0bNTBcDEKz2Ec20GVYvq/H/XNKiUorUFilH7NPbFUuiiyzWaSdN3PA8JV0OvYx0gH/5aFAQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.37.0", + "@typescript-eslint/utils": "5.37.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.37.0.tgz", + "integrity": "sha512-3frIJiTa5+tCb2iqR/bf7XwU20lnU05r/sgPJnRpwvfZaqCJBrl8Q/mw9vr3NrNdB/XtVyMA0eppRMMBqdJ1bA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.37.0.tgz", + "integrity": "sha512-JkFoFIt/cx59iqEDSgIGnQpCTRv96MQnXCYvJi7QhBC24uyuzbD8wVbajMB1b9x4I0octYFJ3OwjAwNqk1AjDA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/visitor-keys": "5.37.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.37.0.tgz", + "integrity": "sha512-jUEJoQrWbZhmikbcWSMDuUSxEE7ID2W/QCV/uz10WtQqfOuKZUqFGjqLJ+qhDd17rjgp+QJPqTdPIBWwoob2NQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.37.0", + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/typescript-estree": "5.37.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.37.0.tgz", + "integrity": "sha512-Hp7rT4cENBPIzMwrlehLW/28EVCOcE9U1Z1BQTc8EA8v5qpr7GRGuG+U58V5tTY48zvUOA3KHvw3rA8tY9fbdA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.37.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -156,6 +415,15 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -374,6 +642,18 @@ "node": ">=0.3.1" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -521,9 +801,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -603,6 +883,22 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -615,6 +911,15 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -765,6 +1070,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -997,6 +1331,28 @@ "node": ">=10" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1199,6 +1555,15 @@ "node": ">=8" } }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1238,6 +1603,26 @@ "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -1289,6 +1674,16 @@ "node": ">=4" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -1304,6 +1699,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1325,9 +1743,9 @@ ] }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1369,6 +1787,15 @@ "node": ">=8" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1440,6 +1867,27 @@ "node": ">=8.0" } }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1650,12 +2098,166 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "@types/node": { "version": "18.7.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.37.0.tgz", + "integrity": "sha512-Fde6W0IafXktz1UlnhGkrrmnnGpAo1kyX7dnyHHVrmwJOn72Oqm3eYtddrpOwwel2W8PAK9F3pIL5S+lfoM0og==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.37.0", + "@typescript-eslint/type-utils": "5.37.0", + "@typescript-eslint/utils": "5.37.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + } + } + }, + "@typescript-eslint/parser": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.37.0.tgz", + "integrity": "sha512-01VzI/ipYKuaG5PkE5+qyJ6m02fVALmMPY3Qq5BHflDx3y4VobbLdHQkSMg9VPRS4KdNt4oYTMaomFoHonBGAw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.37.0", + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/typescript-estree": "5.37.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.37.0.tgz", + "integrity": "sha512-F67MqrmSXGd/eZnujjtkPgBQzgespu/iCZ+54Ok9X5tALb9L2v3G+QBSoWkXG0p3lcTJsL+iXz5eLUEdSiJU9Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/visitor-keys": "5.37.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.37.0.tgz", + "integrity": "sha512-BSx/O0Z0SXOF5tY0bNTBcDEKz2Ec20GVYvq/H/XNKiUorUFilH7NPbFUuiiyzWaSdN3PA8JV0OvYx0gH/5aFAQ==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.37.0", + "@typescript-eslint/utils": "5.37.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.37.0.tgz", + "integrity": "sha512-3frIJiTa5+tCb2iqR/bf7XwU20lnU05r/sgPJnRpwvfZaqCJBrl8Q/mw9vr3NrNdB/XtVyMA0eppRMMBqdJ1bA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.37.0.tgz", + "integrity": "sha512-JkFoFIt/cx59iqEDSgIGnQpCTRv96MQnXCYvJi7QhBC24uyuzbD8wVbajMB1b9x4I0octYFJ3OwjAwNqk1AjDA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/visitor-keys": "5.37.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.37.0.tgz", + "integrity": "sha512-jUEJoQrWbZhmikbcWSMDuUSxEE7ID2W/QCV/uz10WtQqfOuKZUqFGjqLJ+qhDd17rjgp+QJPqTdPIBWwoob2NQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.37.0", + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/typescript-estree": "5.37.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.37.0.tgz", + "integrity": "sha512-Hp7rT4cENBPIzMwrlehLW/28EVCOcE9U1Z1BQTc8EA8v5qpr7GRGuG+U58V5tTY48zvUOA3KHvw3rA8tY9fbdA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.37.0", + "eslint-visitor-keys": "^3.3.0" + } + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -1724,6 +2326,12 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1888,6 +2496,15 @@ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2009,9 +2626,9 @@ } }, "eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, "espree": { @@ -2061,6 +2678,19 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2073,6 +2703,15 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2180,6 +2819,28 @@ "type-fest": "^0.20.2" } }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + } + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2349,6 +3010,22 @@ "yallist": "^4.0.0" } }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -2500,6 +3177,12 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2524,6 +3207,12 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -2560,6 +3249,12 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2569,6 +3264,15 @@ "glob": "^7.1.3" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2576,9 +3280,9 @@ "dev": true }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -2608,6 +3312,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2658,6 +3368,21 @@ "is-number": "^7.0.0" } }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 162948c..4063e65 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ }, "devDependencies": { "@types/node": "^18.7.18", + "@typescript-eslint/eslint-plugin": "^5.37.0", + "@typescript-eslint/parser": "^5.37.0", "eslint": "^8.4.1", "mocha": "^10.0.0", "typescript": "^4.8.3" diff --git a/src/expression.ts b/src/expression.ts index 9d1ee21..b50bd0b 100644 --- a/src/expression.ts +++ b/src/expression.ts @@ -30,6 +30,6 @@ export default class Expression extends Token { } index += 1; } - return ''; + return result; } } \ No newline at end of file diff --git a/src/helpers/unicode-safe-split.ts b/src/helpers/unicode-safe-split.ts index ba0a3f9..ef4aa03 100644 --- a/src/helpers/unicode-safe-split.ts +++ b/src/helpers/unicode-safe-split.ts @@ -91,7 +91,7 @@ export default ( } const char = subject.substring(idx, idx + inc); if (callback) { - let cbres = callback(subject, char, idx); + const cbres = callback(subject, char, idx); if (cbres != null) { inc += cbres; } diff --git a/src/index.ts b/src/index.ts index 4309853..0475299 100644 --- a/src/index.ts +++ b/src/index.ts @@ -104,7 +104,7 @@ export class Expressionish { await tokenizeFunctionIf(mockState) || await tokenizeFunction(mockState) ) { - let lastToken : Token = result[result.length - 1]; + const lastToken : Token = result[result.length - 1]; if ( lastToken != null && lastToken.type === TokenType.TEXT && diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index 2b0a2dd..1de855f 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -8,16 +8,16 @@ import tokenizeCondition from './condition'; import { ExpressionSyntaxError } from '../errors'; -export default async (state: ITokenizeState, isCondition: boolean = false) : Promise => { - - let { tokens, cursor, stack, options } = state; +export default async (state: ITokenizeState, isCondition = false) : Promise => { + const { stack, options } = state; + let { tokens, cursor } = state; if (tokens[cursor]?.value !== '[') { return false; } cursor += 1; - let args : Token[] = []; + const args : Token[] = []; while ( cursor < tokens.length && @@ -77,7 +77,7 @@ export default async (state: ITokenizeState, isCondition: boolean = false) : Pro } if (tokens[cursor].value !== ']') { - throw new ExpressionSyntaxError('illegal character; expected \]', tokens[cursor].position); + throw new ExpressionSyntaxError('illegal character; expected ]', tokens[cursor].position); } state.tokens = tokens; diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts index 5771707..23c62ef 100644 --- a/src/tokenize/argument.ts +++ b/src/tokenize/argument.ts @@ -15,8 +15,8 @@ import tokenizeFunction from './function'; import { ExpressionSyntaxError } from '../errors'; export default async (state: ITokenizeState) : Promise => { - - let { tokens, cursor, stack, options } = state; + const { stack, options } = state; + let { tokens, cursor } = state; const position = tokens[cursor]?.position; @@ -46,7 +46,7 @@ export default async (state: ITokenizeState) : Promise => { await tokenizeFunction(mockState) ) { if (mockState.output) { - let output : Token = mockState.output; + const output : Token = mockState.output; if (lastToken == null) { result.push(output); @@ -91,7 +91,7 @@ export default async (state: ITokenizeState) : Promise => { continue; } - let value = tokens[cursor].value; + const value = tokens[cursor].value; if (value === ' ' || value === '\t' || value === '\n' || value === '\r' ) { if (whitespaceStart === 0) { whitespaceStart = cursor; diff --git a/src/tokenize/function-if.ts b/src/tokenize/function-if.ts index 79b0906..764a137 100644 --- a/src/tokenize/function-if.ts +++ b/src/tokenize/function-if.ts @@ -1,4 +1,3 @@ -import ParserOptions from '../types/options'; import TokenType from '../types/token-types'; import Token from '../tokens/token'; @@ -11,8 +10,8 @@ import tokenizeArgumentList from './argument-list'; import { ExpressionSyntaxError } from '../errors'; export default async (state: ITokenizeState) : Promise => { - - let { tokens, cursor, stack, options } = state; + const { tokens, stack, options } = state; + let cursor = state.cursor; if ( tokens[cursor]?.value !== '$' || diff --git a/src/tokenize/function.ts b/src/tokenize/function.ts index a5cd291..833341a 100644 --- a/src/tokenize/function.ts +++ b/src/tokenize/function.ts @@ -11,11 +11,10 @@ import FunctionalToken from '../tokens/token-function'; import tokenizeArgumentList from './argument-list'; -const nameCheck = /^([a-z][a-z\d]{2,})$/i; - export default async (state: ITokenizeState) : Promise => { + const { tokens, stack, options} = state; + let cursor = state.cursor; - let { tokens, cursor, stack, options } = state; if (tokens[cursor]?.value !== '$' || tokens[cursor + 1] == null) { return false; } diff --git a/src/tokenize/text-escape-block.ts b/src/tokenize/text-escape-block.ts index 42d085e..4ede000 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/tokenize/text-escape-block.ts @@ -11,7 +11,8 @@ import type ITokenizeState from '../types/tokenize-state'; import { ExpressionSyntaxError } from '../errors'; export default async (state: ITokenizeState) : Promise => { - let { tokens, cursor, stack } = state; + let { tokens, cursor } = state; + const stack = state.stack; if (tokens[cursor]?.value !== '``') { return false; diff --git a/src/tokenize/text-escape-single.ts b/src/tokenize/text-escape-single.ts index 1846cad..acf1075 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/tokenize/text-escape-single.ts @@ -3,7 +3,7 @@ import TextToken from '../tokens/token-text'; import type ITokenizeState from '../types/tokenize-state'; export default async (state: ITokenizeState, characters?: string[]) : Promise => { - let { tokens, cursor } = state; + const { tokens, cursor } = state; if (characters == null) { characters = ['\\', '$', '"', '`'] diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 5a95576..75aed43 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -15,7 +15,8 @@ import { ExpressionSyntaxError } from '../errors'; export default async (state: ITokenizeState) : Promise => { - let { tokens, cursor, stack } = state; + let { tokens, cursor } = state; + const stack = state.stack; if (tokens[cursor]?.value !== '"') { return false; @@ -35,7 +36,7 @@ export default async (state: ITokenizeState) : Promise => { tokens[cursor].value !== '"' ) { - let lastToken : Token = quoteTokens[quoteTokens.length - 1]; + const lastToken : Token = quoteTokens[quoteTokens.length - 1]; const mockState : ITokenizeState = { options: {...(state.options)}, diff --git a/src/tokenize/text-special.ts b/src/tokenize/text-special.ts index e84c0d7..eb309eb 100644 --- a/src/tokenize/text-special.ts +++ b/src/tokenize/text-special.ts @@ -14,7 +14,7 @@ export default async (state: ITokenizeState) : Promise => { 't': '\t' }; - let { tokens, cursor } = state; + const { tokens, cursor } = state; if ( tokens[cursor]?.value !== '\\' || tokens[cursor + 1] == null || diff --git a/src/tokens/comparison/base.ts b/src/tokens/comparison/base.ts index 8df38a6..4d8b272 100644 --- a/src/tokens/comparison/base.ts +++ b/src/tokens/comparison/base.ts @@ -9,7 +9,7 @@ export interface IComparisonToken extends IOperatorToken { } export default class ComparisonToken extends OperatorToken { - protected invert : boolean = false; + protected invert = false; constructor(token: IComparisonToken) { super({ @@ -22,6 +22,7 @@ export default class ComparisonToken extends OperatorToken { } } + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ async handle(options: ParserOptions, meta?: any) : Promise { return false; } diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts index 9b8689f..6abd833 100644 --- a/src/tokens/comparison/exists.ts +++ b/src/tokens/comparison/exists.ts @@ -10,7 +10,7 @@ export default class ExistsToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.left.evaluate(options, meta); + const v1 = await this.left.evaluate(options, meta); if (options.verifyOnly) { return false; diff --git a/src/tokens/comparison/isbool.ts b/src/tokens/comparison/isbool.ts index ad36cd7..ed3a4e9 100644 --- a/src/tokens/comparison/isbool.ts +++ b/src/tokens/comparison/isbool.ts @@ -18,7 +18,7 @@ export default class IsToken extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.left.evaluate(options, meta); + const v1 = await this.left.evaluate(options, meta); if (options.verifyOnly) { return false; diff --git a/src/tokens/comparison/isnull.ts b/src/tokens/comparison/isnull.ts index e942903..19fa248 100644 --- a/src/tokens/comparison/isnull.ts +++ b/src/tokens/comparison/isnull.ts @@ -10,7 +10,7 @@ export default class IsNull extends ComparisonToken { } async handle(options: ParserOptions, meta?: any): Promise { - let v1 = await this.left.evaluate(options, meta); + const v1 = await this.left.evaluate(options, meta); if (options.verifyOnly) { return false; diff --git a/src/tokens/comparison/numerical.ts b/src/tokens/comparison/numerical.ts index bc91c4e..4877681 100644 --- a/src/tokens/comparison/numerical.ts +++ b/src/tokens/comparison/numerical.ts @@ -30,7 +30,7 @@ export default class LessThanToken extends ComparisonToken { return true; } - let v2 = await this.right.evaluate(options, meta); + const v2 = await this.right.evaluate(options, meta); if (v2 == null || v2 === '') { return true; } diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts index 27a2f7b..7fcc31a 100644 --- a/src/tokens/comparison/wildcard.ts +++ b/src/tokens/comparison/wildcard.ts @@ -3,13 +3,13 @@ import split from '../../helpers/unicode-safe-split'; import toText from '../../helpers/to-text'; import ComparisonToken, { IComparisonToken } from './base'; -const toRegExp = (subject: string, caseSensitive: boolean = false) : RegExp => { - let wc = split(subject); +const toRegExp = (subject: string, caseSensitive = false) : RegExp => { + const wc = split(subject); let pattern = ''; let anchorStart = true; let anchorEnd = true; let idx = 0; - let len = wc.length; + const len = wc.length; while (idx < len) { const atStart = idx === 0; diff --git a/src/tokens/logical/logical-and.ts b/src/tokens/logical/logical-and.ts index 659dbcc..e16f7be 100644 --- a/src/tokens/logical/logical-and.ts +++ b/src/tokens/logical/logical-and.ts @@ -15,7 +15,7 @@ export default class AndOperator extends LogicalToken { throw new Error('TODO'); } - let left = await this.left.evaluate(options, meta); + const left = await this.left.evaluate(options, meta); if (options.verifyOnly) { await this.right.evaluate(options, meta); return false; diff --git a/src/tokens/token-function.ts b/src/tokens/token-function.ts index 79f4a2d..bfcd2f2 100644 --- a/src/tokens/token-function.ts +++ b/src/tokens/token-function.ts @@ -1,5 +1,5 @@ import TokenType from '../types/token-types'; -import ParserOptions, { IFunctionHandler, IFunctionLookup } from '../types/options'; +import ParserOptions, { type IFunctionLookup } from '../types/options'; import Token from './token'; export interface IFunctionalToken { @@ -32,7 +32,7 @@ export default class FunctionalToken extends Token { throw new Error(`TODO - No handler for ${this.prefix}${this.value}`); } - let args : any[] = []; + const args : any[] = []; if (this.arguments != null) { const argList = this.arguments; for (let idx = 0; idx < argList.length; idx += 1) { @@ -50,18 +50,12 @@ export default class FunctionalToken extends Token { await handler.argsCheck(meta, args); } catch (err) { // TODO - Custom errors - throw err; + throw new Error(`TODO - ArgumentsError: ${err.message}`); } } - try { - const res = handler.evaluator(meta, ...args); - return res; - - } catch (err) { - // TODO: custom errors - throw err; - } + const res = handler.evaluator(meta, ...args); + return res; } toToken() : object { diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index bbea72f..8469607 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -25,7 +25,7 @@ export default class TokenList extends Token { let res : any; for (let idx = 0; idx < parts.length; idx += 1) { - let value = await parts[idx].evaluate(options, meta); + const value = await parts[idx].evaluate(options, meta); if (options.verifyOnly) { continue; @@ -40,13 +40,13 @@ export default class TokenList extends Token { continue; } - let strValue = toText(value); + const strValue = toText(value); if (strValue == null) { continue; } if (typeof res !== 'string') { - let strRes = toText(res); + const strRes = toText(res); if (strRes == null) { res = strValue; diff --git a/src/tokens/token-operator.ts b/src/tokens/token-operator.ts index dd3f79b..9b280ae 100644 --- a/src/tokens/token-operator.ts +++ b/src/tokens/token-operator.ts @@ -19,13 +19,14 @@ export default class OperatorToken extends Token { this.right = token.right; } + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ async evaluate(options: ParserOptions, meta?: any) : Promise { return false; } toToken() : object { - let result : Record = { + const result : Record = { ...(super.toToken()), left: this.left.toToken() } diff --git a/src/tokens/token-text.ts b/src/tokens/token-text.ts index afa9c18..329b5cf 100644 --- a/src/tokens/token-text.ts +++ b/src/tokens/token-text.ts @@ -1,5 +1,5 @@ import TokenType from '../types/token-types'; -import Token, { type IToken } from './token'; +import Token from './token'; export interface ITextToken { position: number; diff --git a/src/tokens/token.ts b/src/tokens/token.ts index e5af053..538a40c 100644 --- a/src/tokens/token.ts +++ b/src/tokens/token.ts @@ -18,6 +18,7 @@ export default class Token { this.value = token.value; } + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ async evaluate(options: any, meta?: any) : Promise { return this.value == null ? '' : this.value; } diff --git a/src/types/options.ts b/src/types/options.ts index cffe866..b54b3a3 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -4,11 +4,7 @@ export interface IFunctionHandler { evaluator: (meta: any, ...args: any[]) => Promise; } -export type IFunctionLookup = ( - name: string, - stack?: string[], - meta?: any -) => IFunctionHandler; +export type IFunctionLookup = (name: string, stack?: string[], meta?: any) => IFunctionHandler; export default interface ParserOptions { functionHandlers?: Record; @@ -19,4 +15,4 @@ export default interface ParserOptions { verifyOnly?: boolean; skipArgumentsCheck?: boolean; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/types/pre-token.ts b/src/types/pre-token.ts index bac54f1..bb73f4e 100644 --- a/src/types/pre-token.ts +++ b/src/types/pre-token.ts @@ -1,4 +1,4 @@ export default interface IPreToken { position: number; value: string; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/types/token-types.ts b/src/types/token-types.ts index 3852db0..20861f2 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -9,6 +9,6 @@ const enum TokenType { COMPARISON, EMPTY, UNKNOWN -}; +} export default TokenType; \ No newline at end of file diff --git a/src/types/tokenize-state.ts b/src/types/tokenize-state.ts index a150717..cfc61aa 100644 --- a/src/types/tokenize-state.ts +++ b/src/types/tokenize-state.ts @@ -1,6 +1,5 @@ -import ParserOptions from './options'; -import IPreToken from './pre-token'; - +import type ParserOptions from './options'; +import type IPreToken from './pre-token'; import type Token from '../tokens/token'; export default interface ITokenizeState { From ea7d8bb8bed77a607a858f88f6fbda9954369db8 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 17 Sep 2022 18:47:28 -0400 Subject: [PATCH 41/92] chore: lint second pass --- .eslintrc.js | 3 +- src/expression.ts | 7 ++-- src/helpers/has.ts | 2 +- src/helpers/is-primitive.ts | 2 +- src/helpers/to-number.ts | 4 +- src/helpers/to-text.ts | 8 ++-- src/index.ts | 4 +- src/tokens/comparison/base.ts | 8 ++-- src/tokens/comparison/equal-loose.ts | 2 +- src/tokens/comparison/equal-strict.ts | 2 +- src/tokens/comparison/exists.ts | 2 +- src/tokens/comparison/greater-than-equal.ts | 2 +- src/tokens/comparison/greater-than.ts | 2 +- src/tokens/comparison/isbool.ts | 2 +- src/tokens/comparison/isnull.ts | 2 +- src/tokens/comparison/less-than-equal.ts | 2 +- src/tokens/comparison/less-than.ts | 2 +- src/tokens/comparison/numerical.ts | 2 +- src/tokens/comparison/regex.ts | 8 ++-- src/tokens/comparison/wildcard.ts | 41 ++++++++++++--------- src/tokens/logical/base.ts | 2 +- src/tokens/logical/logical-and.ts | 2 +- src/tokens/logical/logical-not.ts | 2 +- src/tokens/logical/logical-or.ts | 2 +- src/tokens/token-function-if.ts | 5 +-- src/tokens/token-function.ts | 6 +-- src/tokens/token-list.ts | 4 +- src/tokens/token-operator.ts | 2 +- src/tokens/token.ts | 8 ++-- src/types/options.ts | 6 +-- 30 files changed, 76 insertions(+), 70 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 49cbf63..8807744 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,8 +15,7 @@ module.exports = { "sourceType": "module" }, "rules": { - "linebreak-style": ["error", "unix"], - "@typescript-eslint/no-explicit-any": "off" + "linebreak-style": ["error", "unix"] }, "ignorePatterns": [ diff --git a/src/expression.ts b/src/expression.ts index b50bd0b..047e1b0 100644 --- a/src/expression.ts +++ b/src/expression.ts @@ -20,11 +20,12 @@ export default class Expression extends Token { this.options = token.options; } - async evaluate(meta: any = {}) : Promise { + async evaluate(meta: unknown = {}) : Promise { let index = 0; let result = ''; - while (index < this.value.length) { - const value = await this.value[index].evaluate(this.options, meta); + while (index < (this.value).length) { + + const value = await (this.value)[index].evaluate(this.options, meta); if (value != null) { result += JSON.stringify(value); } diff --git a/src/helpers/has.ts b/src/helpers/has.ts index d2c9df2..1506b81 100644 --- a/src/helpers/has.ts +++ b/src/helpers/has.ts @@ -1,3 +1,3 @@ const hasOwnProperty = Object.prototype.hasOwnProperty; -export default (subject: any, key: string) => hasOwnProperty.call(subject, key); \ No newline at end of file +export default (subject: unknown, key: string) => hasOwnProperty.call(subject, key); \ No newline at end of file diff --git a/src/helpers/is-primitive.ts b/src/helpers/is-primitive.ts index d6a6b0b..2e560aa 100644 --- a/src/helpers/is-primitive.ts +++ b/src/helpers/is-primitive.ts @@ -1,4 +1,4 @@ -export default (subject: any) : boolean => { +export default (subject: unknown) : boolean => { return ( typeof subject === 'boolean' || (typeof subject === 'number' && Number.isFinite(subject)) || diff --git a/src/helpers/to-number.ts b/src/helpers/to-number.ts index 54b261e..be2efbe 100644 --- a/src/helpers/to-number.ts +++ b/src/helpers/to-number.ts @@ -1,8 +1,8 @@ -export default (subject: any) : null | number => { +export default (subject: unknown) : null | number => { if (subject != null && subject !== '') { subject = Number(subject); if (Number.isFinite(subject)) { - return subject; + return subject; } } return null; diff --git a/src/helpers/to-text.ts b/src/helpers/to-text.ts index abc3f9d..6ff8bc9 100644 --- a/src/helpers/to-text.ts +++ b/src/helpers/to-text.ts @@ -1,15 +1,15 @@ import isPrimitive from "./is-primitive"; -export default (subject: any) : string | void => { +export default (subject: unknown) : string | void => { if (subject != null) { if (isPrimitive(subject)) { return String(subject); } - subject = JSON.stringify(subject); - if (subject != null) { - return subject; + const subjectJSON : string = JSON.stringify(subject); + if (subjectJSON != null) { + return subjectJSON; } } }; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 0475299..0c78a42 100644 --- a/src/index.ts +++ b/src/index.ts @@ -111,7 +111,7 @@ export class Expressionish { mockState.output && (mockState.output).type === TokenType.TEXT ) { - lastToken.value += (mockState.output).value; + (lastToken.value) += (mockState.output).value; } else { result.push(mockState.output); @@ -149,7 +149,7 @@ export class Expressionish { return parser.tokenize(subject); } - static async evaluate(subject: string, meta: any = {}, options?: ParserOptions) : Promise { + static async evaluate(subject: string, meta: unknown = {}, options?: ParserOptions) : Promise { const tokens = await Expressionish.tokenize(subject, options); return tokens.evaluate(meta); } diff --git a/src/tokens/comparison/base.ts b/src/tokens/comparison/base.ts index 4d8b272..b1dd330 100644 --- a/src/tokens/comparison/base.ts +++ b/src/tokens/comparison/base.ts @@ -5,7 +5,7 @@ import OperatorToken, { IOperatorToken } from '../token-operator'; export interface IComparisonToken extends IOperatorToken { invert?: boolean; - value: any; + value: unknown; } export default class ComparisonToken extends OperatorToken { @@ -23,16 +23,16 @@ export default class ComparisonToken extends OperatorToken { } /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - async handle(options: ParserOptions, meta?: any) : Promise { + async handle(options: ParserOptions, meta: unknown) : Promise { return false; } - async handleInverse(options: ParserOptions, meta?: any) : Promise { + async handleInverse(options: ParserOptions, meta: unknown) : Promise { const result = await this.handle(options, meta); return !result; } - async evaluate(options: ParserOptions, meta?: any) : Promise { + async evaluate(options: ParserOptions, meta: unknown) : Promise { if (this.invert) { return this.handleInverse(options, meta); } diff --git a/src/tokens/comparison/equal-loose.ts b/src/tokens/comparison/equal-loose.ts index f19839e..ddb67cf 100644 --- a/src/tokens/comparison/equal-loose.ts +++ b/src/tokens/comparison/equal-loose.ts @@ -11,7 +11,7 @@ export default class EqualLooseToken extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom error diff --git a/src/tokens/comparison/equal-strict.ts b/src/tokens/comparison/equal-strict.ts index 35419a4..484ad57 100644 --- a/src/tokens/comparison/equal-strict.ts +++ b/src/tokens/comparison/equal-strict.ts @@ -9,7 +9,7 @@ export default class EqualStrictToken extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom error throw new Error('TODO - Evaluation Error: Right hand argument missing'); diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts index 6abd833..67fb9b1 100644 --- a/src/tokens/comparison/exists.ts +++ b/src/tokens/comparison/exists.ts @@ -9,7 +9,7 @@ export default class ExistsToken extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { const v1 = await this.left.evaluate(options, meta); if (options.verifyOnly) { diff --git a/src/tokens/comparison/greater-than-equal.ts b/src/tokens/comparison/greater-than-equal.ts index b1e24cc..6cb24bc 100644 --- a/src/tokens/comparison/greater-than-equal.ts +++ b/src/tokens/comparison/greater-than-equal.ts @@ -10,7 +10,7 @@ export default class GreaterThanEqualToken extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom error throw new Error('TODO - Evaluation Error: Right hand argument missing'); diff --git a/src/tokens/comparison/greater-than.ts b/src/tokens/comparison/greater-than.ts index 94ee0fd..a4fd11a 100644 --- a/src/tokens/comparison/greater-than.ts +++ b/src/tokens/comparison/greater-than.ts @@ -10,7 +10,7 @@ export default class GreaterThanToken extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom error throw new Error('TODO - Evaluation Error: Right hand argument missing'); diff --git a/src/tokens/comparison/isbool.ts b/src/tokens/comparison/isbool.ts index ed3a4e9..bc65961 100644 --- a/src/tokens/comparison/isbool.ts +++ b/src/tokens/comparison/isbool.ts @@ -17,7 +17,7 @@ export default class IsToken extends ComparisonToken { this.against = token.against; } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { const v1 = await this.left.evaluate(options, meta); if (options.verifyOnly) { diff --git a/src/tokens/comparison/isnull.ts b/src/tokens/comparison/isnull.ts index 19fa248..0ca3441 100644 --- a/src/tokens/comparison/isnull.ts +++ b/src/tokens/comparison/isnull.ts @@ -9,7 +9,7 @@ export default class IsNull extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { const v1 = await this.left.evaluate(options, meta); if (options.verifyOnly) { diff --git a/src/tokens/comparison/less-than-equal.ts b/src/tokens/comparison/less-than-equal.ts index d7541f4..90eb01b 100644 --- a/src/tokens/comparison/less-than-equal.ts +++ b/src/tokens/comparison/less-than-equal.ts @@ -10,7 +10,7 @@ export default class LessThanEqualToken extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom error throw new Error('TODO - Evaluation Error: Right hand argument missing'); diff --git a/src/tokens/comparison/less-than.ts b/src/tokens/comparison/less-than.ts index db82016..5f12859 100644 --- a/src/tokens/comparison/less-than.ts +++ b/src/tokens/comparison/less-than.ts @@ -10,7 +10,7 @@ export default class LessThanToken extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom error throw new Error('TODO - Evaluation Error: Right hand argument missing'); diff --git a/src/tokens/comparison/numerical.ts b/src/tokens/comparison/numerical.ts index 4877681..87591f3 100644 --- a/src/tokens/comparison/numerical.ts +++ b/src/tokens/comparison/numerical.ts @@ -12,7 +12,7 @@ export default class LessThanToken extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { let v1 = await this.left.evaluate(options, meta); if (options.verifyOnly) { diff --git a/src/tokens/comparison/regex.ts b/src/tokens/comparison/regex.ts index 1fe491c..42b61dd 100644 --- a/src/tokens/comparison/regex.ts +++ b/src/tokens/comparison/regex.ts @@ -10,7 +10,7 @@ export default class LessThanToken extends ComparisonToken { }); } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom error throw new Error('TODO - Evaluation Error: Right hand argument missing'); @@ -37,10 +37,10 @@ export default class LessThanToken extends ComparisonToken { return false; } - const parts = /^\/(.*)\/([a-z]*)$/i.exec(v2); + const parts = /^\/(.*)\/([a-z]*)$/i.exec(v2); if (parts) { - return (new RegExp(parts[1], parts[2])).test(v1); + return (new RegExp(parts[1], parts[2])).test(v1); } - return (new RegExp(v2)).test(v1); + return (new RegExp(v2)).test(v1); } } \ No newline at end of file diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts index 7fcc31a..c81ac8b 100644 --- a/src/tokens/comparison/wildcard.ts +++ b/src/tokens/comparison/wildcard.ts @@ -106,64 +106,69 @@ export default class WildcardToken extends ComparisonToken { this.caseSensitive = token.caseSensitive; } - async handle(options: ParserOptions, meta?: any): Promise { + async handle(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom error throw new Error('TODO - Evaluation Error: Right hand argument missing'); } - let v1 = await this.left.evaluate(options, meta); + const v1 = await this.left.evaluate(options, meta); if (options.verifyOnly) { await this.right.evaluate(options, meta); return false; } - v1 = toText(v1); - if (v1 == null) { + const v1Text = toText(v1); + if (v1Text == null) { return false; } - let v2 = await this.right.evaluate(options, meta); - v2 = toText(v2); - if (v2 == null) { + const v2 = await this.right.evaluate(options, meta); + const v2Text = toText(v2); + if (v2Text == null) { return false; } - v2 = toRegExp(v2); - if (v2 == null) { + const v2RegExp = toRegExp(v2Text); + if (v2RegExp == null) { return false; } - return v2.test(v1); + return v2RegExp.test(v1Text); } - async handleInverse(options: ParserOptions, meta?: any): Promise { + async handleInverse(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom error throw new Error('TODO - Evaluation Error: Right hand argument missing'); } - let v1 = await this.left.evaluate(options, meta); + const v1 = await this.left.evaluate(options, meta); if (options.verifyOnly) { await this.right.evaluate(options, meta); return false; } - v1 = toText(v1); - if (v1 == null) { + const v1Text = toText(v1); + if (v1Text == null) { return false; } - let v2 = await this.right.evaluate(options, meta); - v2 = toRegExp(v2); - if (v2 == null) { + const v2 = await this.right.evaluate(options, meta); + const v2Text = toText(v2); + if (v2Text == null) { return false; } - return !v2.test(v1); + const v2RegExp = toRegExp(v2Text); + if (v2RegExp == null) { + return false; + } + + return !v2RegExp.test(v1Text); } toToken() : object { diff --git a/src/tokens/logical/base.ts b/src/tokens/logical/base.ts index 5a65228..324e193 100644 --- a/src/tokens/logical/base.ts +++ b/src/tokens/logical/base.ts @@ -3,7 +3,7 @@ import TokenType from '../../types/token-types'; import { default as OperatorToken, IOperatorToken} from '../token-operator'; export interface ILogicalToken extends IOperatorToken { - value: any; + value: unknown; } export default class LogicalToken extends OperatorToken { diff --git a/src/tokens/logical/logical-and.ts b/src/tokens/logical/logical-and.ts index e16f7be..4de3c9c 100644 --- a/src/tokens/logical/logical-and.ts +++ b/src/tokens/logical/logical-and.ts @@ -9,7 +9,7 @@ export default class AndOperator extends LogicalToken { }); } - async evaluate(options: ParserOptions, meta?: any): Promise { + async evaluate(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom errors throw new Error('TODO'); diff --git a/src/tokens/logical/logical-not.ts b/src/tokens/logical/logical-not.ts index 5ea7a68..6af37fd 100644 --- a/src/tokens/logical/logical-not.ts +++ b/src/tokens/logical/logical-not.ts @@ -9,7 +9,7 @@ export default class NotOperator extends LogicalToken { }); } - async evaluate(options: ParserOptions, meta?: any): Promise { + async evaluate(options: ParserOptions, meta: unknown): Promise { const value = await this.left.evaluate(options, meta); if (options.verifyOnly) { diff --git a/src/tokens/logical/logical-or.ts b/src/tokens/logical/logical-or.ts index 88ca5e6..25c8644 100644 --- a/src/tokens/logical/logical-or.ts +++ b/src/tokens/logical/logical-or.ts @@ -9,7 +9,7 @@ export default class OrOperator extends LogicalToken { }); } - async evaluate(options: ParserOptions, meta?: any): Promise { + async evaluate(options: ParserOptions, meta: unknown): Promise { if (this.right == null) { // TODO - custom errors throw new Error('TODO'); diff --git a/src/tokens/token-function-if.ts b/src/tokens/token-function-if.ts index dc400ab..6e8d372 100644 --- a/src/tokens/token-function-if.ts +++ b/src/tokens/token-function-if.ts @@ -27,7 +27,7 @@ export default class IfStatementToken extends Token { this.whenFalse = token.whenFalse; } - async evaluate(options: ParserOptions, meta?: any) : Promise { + async evaluate(options: ParserOptions, meta: unknown) : Promise { const res = await this.condition.evaluate(options, meta); if (options.verifyOnly) { @@ -48,12 +48,11 @@ export default class IfStatementToken extends Token { } toToken() : object { - const whenFalse : any = this.whenFalse; return { ...(super.toToken()), condition: this.condition.toToken(), whenTrue: this.whenTrue.toToken(), - whenFalse + whenFalse: this.whenFalse?.toToken() } } } \ No newline at end of file diff --git a/src/tokens/token-function.ts b/src/tokens/token-function.ts index bfcd2f2..13366c8 100644 --- a/src/tokens/token-function.ts +++ b/src/tokens/token-function.ts @@ -25,14 +25,14 @@ export default class FunctionalToken extends Token { this.lookupFn = token.lookupFn; } - async evaluate(options: ParserOptions, meta: any = {}) : Promise { - const handler = this.lookupFn(this.value); + async evaluate(options: ParserOptions, meta: unknown) : Promise { + const handler = this.lookupFn(this.value); if (handler == null) { // TODO: custom errors throw new Error(`TODO - No handler for ${this.prefix}${this.value}`); } - const args : any[] = []; + const args : unknown[] = []; if (this.arguments != null) { const argList = this.arguments; for (let idx = 0; idx < argList.length; idx += 1) { diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index 8469607..8b971aa 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -20,10 +20,10 @@ export default class TokenList extends Token { }); } - async evaluate(options: ParserOptions, meta?: any): Promise { + async evaluate(options: ParserOptions, meta: unknown): Promise { const parts = this.value; - let res : any; + let res : unknown; for (let idx = 0; idx < parts.length; idx += 1) { const value = await parts[idx].evaluate(options, meta); diff --git a/src/tokens/token-operator.ts b/src/tokens/token-operator.ts index 9b280ae..47b07d1 100644 --- a/src/tokens/token-operator.ts +++ b/src/tokens/token-operator.ts @@ -20,7 +20,7 @@ export default class OperatorToken extends Token { } /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - async evaluate(options: ParserOptions, meta?: any) : Promise { + async evaluate(options: ParserOptions, meta: unknown) : Promise { return false; } diff --git a/src/tokens/token.ts b/src/tokens/token.ts index 538a40c..5c51302 100644 --- a/src/tokens/token.ts +++ b/src/tokens/token.ts @@ -1,16 +1,18 @@ +import type ParserOptions from '../types/options'; + import TokenType from '../types/token-types'; export interface IToken { type: TokenType; position: number; - value?: any; + value?: unknown; } export default class Token { public type: TokenType; public position : number; - public value : any; + public value : unknown; constructor(token: IToken) { this.type = token.type == null ? TokenType.UNKNOWN : token.type; @@ -19,7 +21,7 @@ export default class Token { } /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - async evaluate(options: any, meta?: any) : Promise { + async evaluate(options: ParserOptions, meta: unknown) : Promise { return this.value == null ? '' : this.value; } diff --git a/src/types/options.ts b/src/types/options.ts index b54b3a3..36a5b40 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -1,10 +1,10 @@ export interface IFunctionHandler { stackCheck?: (stack: string[]) => Promise; - argsCheck?: (meta: any, ...args: any[]) => Promise; - evaluator: (meta: any, ...args: any[]) => Promise; + argsCheck?: (meta: unknown, ...args: unknown[]) => Promise; + evaluator: (meta: unknown, ...args: unknown[]) => Promise; } -export type IFunctionLookup = (name: string, stack?: string[], meta?: any) => IFunctionHandler; +export type IFunctionLookup = (name: string, stack?: string[], meta?: unknown) => IFunctionHandler; export default interface ParserOptions { functionHandlers?: Record; From fb92f36fa5daaa1a5d9965dd401661726945a9aa Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 20 Sep 2022 04:16:45 -0400 Subject: [PATCH 42/92] feat: add manifest to comparison tokens --- src/tokens/comparison/contains.ts | 72 +++++++++++++++++++++ src/tokens/comparison/equal-loose.ts | 18 +++++- src/tokens/comparison/equal-strict.ts | 14 +++- src/tokens/comparison/exists.ts | 14 +++- src/tokens/comparison/greater-than-equal.ts | 14 +++- src/tokens/comparison/greater-than.ts | 14 +++- src/tokens/comparison/isbool.ts | 17 ++++- src/tokens/comparison/isnull.ts | 15 ++++- src/tokens/comparison/less-than-equal.ts | 14 +++- src/tokens/comparison/less-than.ts | 14 +++- src/tokens/comparison/numerical.ts | 17 ++++- src/tokens/comparison/regex.ts | 17 ++++- src/tokens/comparison/wildcard.ts | 17 ++++- src/types/manifest-comparison.ts | 13 ++++ 14 files changed, 249 insertions(+), 21 deletions(-) create mode 100644 src/tokens/comparison/contains.ts create mode 100644 src/types/manifest-comparison.ts diff --git a/src/tokens/comparison/contains.ts b/src/tokens/comparison/contains.ts new file mode 100644 index 0000000..45cc8c9 --- /dev/null +++ b/src/tokens/comparison/contains.ts @@ -0,0 +1,72 @@ +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + +import ComparisonToken, { IComparisonToken } from './base'; + +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand contains the right operand", + casing: true, + alias: ['contains'], + inverse: { + description: "Checks if operands are not loosely equal", + alias: ['!contains'] + } +}; + +export interface IContainsToken extends IComparisonToken { + caseSensitive: void | boolean; +} + +export default class ContainsToken extends ComparisonToken { + private caseSensitive: void | boolean; + + constructor(token: IContainsToken) { + super({ + ...token, + value: 'contains' + }); + this.caseSensitive = token.caseSensitive; + } + + async handle(options: ParserOptions, meta: unknown): Promise { + if (this.right == null) { + // TODO - custom error + throw new Error('TODO'); + } + + const v1 = await this.left.evaluate(options, meta); + const v2 = await this.right.evaluate(options, meta); + + if (options.verifyOnly) { + return false; + } + + if (typeof v1 === 'string') { + if (typeof v2 !== 'string') { + return false; + } + if (this.caseSensitive) { + return v1.includes(v2); + } + return v1.toLowerCase().includes(v2.toLowerCase()) + } + + if (!Array.isArray(v1)) { + return false; + } + + return v1.some((value: unknown) => { + if (typeof value === 'string') { + if (typeof v2 !== 'string') { + return false; + } + if (this.caseSensitive) { + return value === v2; + } + return value.toLowerCase() === v2.toLowerCase(); + } + return v1 === v2; + }); + } +} \ No newline at end of file diff --git a/src/tokens/comparison/equal-loose.ts b/src/tokens/comparison/equal-loose.ts index ddb67cf..f749266 100644 --- a/src/tokens/comparison/equal-loose.ts +++ b/src/tokens/comparison/equal-loose.ts @@ -1,8 +1,22 @@ -import ParserOptions from '../../types/options'; -import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; import isPrimitive from '../../helpers/is-primitive'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + +import ComparisonToken, { IComparisonToken } from './base'; + + +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTREQUIRED, + description: "Checks if operands are loosely equal", + alias: ['=='], + inverse: { + description: "Checks if operands are not loosely equal", + alias: ['!='] + } +}; + export default class EqualLooseToken extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/equal-strict.ts b/src/tokens/comparison/equal-strict.ts index 484ad57..fde048d 100644 --- a/src/tokens/comparison/equal-strict.ts +++ b/src/tokens/comparison/equal-strict.ts @@ -1,6 +1,18 @@ -import ParserOptions from '../../types/options'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + import ComparisonToken, { IComparisonToken } from './base'; +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTREQUIRED, + description: "Checks if operands are strictly equal", + alias: ['==='], + inverse: { + description: "Checks if operands are not strictly equal", + alias: ['!=='] + } +}; + export default class EqualStrictToken extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts index 67fb9b1..4967715 100644 --- a/src/tokens/comparison/exists.ts +++ b/src/tokens/comparison/exists.ts @@ -1,6 +1,18 @@ -import ParserOptions from '../../types/options'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + import ComparisonToken, { IComparisonToken } from './base'; +export const manifest : Manifest = { + arguments: ArgumentQuantifier.LEFTONLY, + description: "Checks if operand is not null, false or empty string", + alias: ['exists'], + inverse: { + description: "Checks if operand is null, false or empty string", + alias: ['!exists'] + } +}; + export default class ExistsToken extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/greater-than-equal.ts b/src/tokens/comparison/greater-than-equal.ts index 6cb24bc..c38f19b 100644 --- a/src/tokens/comparison/greater-than-equal.ts +++ b/src/tokens/comparison/greater-than-equal.ts @@ -1,7 +1,17 @@ -import ParserOptions from '../../types/options'; -import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + +import ComparisonToken, { IComparisonToken } from './base'; + +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is numerical and greater than or equal to the right operand", + alias: ['>='], + inverse: false +}; + export default class GreaterThanEqualToken extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/greater-than.ts b/src/tokens/comparison/greater-than.ts index a4fd11a..669b628 100644 --- a/src/tokens/comparison/greater-than.ts +++ b/src/tokens/comparison/greater-than.ts @@ -1,7 +1,17 @@ -import ParserOptions from '../../types/options'; -import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + +import ComparisonToken, { IComparisonToken } from './base'; + +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is numerical and greater than the right operand", + alias: ['>='], + inverse: false +}; + export default class GreaterThanToken extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/isbool.ts b/src/tokens/comparison/isbool.ts index bc65961..dc0dd3b 100644 --- a/src/tokens/comparison/isbool.ts +++ b/src/tokens/comparison/isbool.ts @@ -1,17 +1,30 @@ -import ParserOptions from '../../types/options'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + import ComparisonToken, { IComparisonToken } from './base'; export interface IIsToken extends IComparisonToken { against: null | boolean; } +export const manifest : Manifest = { + arguments: ArgumentQuantifier.LEFTONLY, + description: "Checks if the left operand is boolean", + alias: ['isbool'], + inverse: { + description: "Checks if the left operand is not a boolean", + alias: ['!isbool'] + } +}; + + export default class IsToken extends ComparisonToken { public against : null | boolean; constructor(token: IIsToken) { super({ ...token, - value: 'is' + value: 'isbool' }); this.against = token.against; diff --git a/src/tokens/comparison/isnull.ts b/src/tokens/comparison/isnull.ts index 0ca3441..aeaf07f 100644 --- a/src/tokens/comparison/isnull.ts +++ b/src/tokens/comparison/isnull.ts @@ -1,6 +1,19 @@ -import ParserOptions from '../../types/options'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + import ComparisonToken, { IComparisonToken } from './base'; +export const manifest : Manifest = { + arguments: ArgumentQuantifier.LEFTONLY, + description: "Checks if the left operand is null or undefined", + alias: ['isnull'], + inverse: { + description: "Checks if the left operand is not null or undefined", + alias: ['!isnull'] + } +}; + + export default class IsNull extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/less-than-equal.ts b/src/tokens/comparison/less-than-equal.ts index 90eb01b..7eab84a 100644 --- a/src/tokens/comparison/less-than-equal.ts +++ b/src/tokens/comparison/less-than-equal.ts @@ -1,7 +1,17 @@ -import ParserOptions from '../../types/options'; -import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + +import ComparisonToken, { IComparisonToken } from './base'; + +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is numerical and less than or equal to the right operand", + alias: ['<='], + inverse: false +}; + export default class LessThanEqualToken extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/less-than.ts b/src/tokens/comparison/less-than.ts index 5f12859..6e07750 100644 --- a/src/tokens/comparison/less-than.ts +++ b/src/tokens/comparison/less-than.ts @@ -1,7 +1,17 @@ -import ParserOptions from '../../types/options'; -import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + +import ComparisonToken, { IComparisonToken } from './base'; + +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is numerical and less than the right operand", + alias: ['<'], + inverse: false +}; + export default class LessThanToken extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/numerical.ts b/src/tokens/comparison/numerical.ts index 87591f3..70a15cf 100644 --- a/src/tokens/comparison/numerical.ts +++ b/src/tokens/comparison/numerical.ts @@ -1,9 +1,22 @@ -import ParserOptions from '../../types/options'; -import ComparisonToken, { IComparisonToken } from './base'; import toNumber from '../../helpers/to-number'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + +import ComparisonToken, { IComparisonToken } from './base'; + const isRange = /^((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))-((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))$/; +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTOPTIONAL, + description: "Checks if the left operand is numerical and if specified within the range of the right operand (inclusive)", + alias: ['isnum', 'isnumber'], + inverse: { + description: "Checks if the left operand is not numerical and if specified not within the range of the right operand (inclusive)", + alias: ['!isnum', '!isnumber'] + } +}; + export default class LessThanToken extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/regex.ts b/src/tokens/comparison/regex.ts index 42b61dd..a00d0bf 100644 --- a/src/tokens/comparison/regex.ts +++ b/src/tokens/comparison/regex.ts @@ -1,7 +1,20 @@ -import ParserOptions from '../../types/options'; -import ComparisonToken, { IComparisonToken } from './base'; import toText from '../../helpers/to-text'; +import type ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + +import ComparisonToken, { IComparisonToken } from './base'; + +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is a match of the right operand regex", + alias: ['regex'], + inverse: { + description: "Checks if the left operand is not a match of the right operand regex", + alias: ['!regex'] + } +}; + export default class LessThanToken extends ComparisonToken { constructor(token: IComparisonToken) { super({ diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts index c81ac8b..67332b6 100644 --- a/src/tokens/comparison/wildcard.ts +++ b/src/tokens/comparison/wildcard.ts @@ -1,6 +1,9 @@ -import ParserOptions from '../../types/options'; import split from '../../helpers/unicode-safe-split'; import toText from '../../helpers/to-text'; + +import ParserOptions from '../../types/options'; +import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; + import ComparisonToken, { IComparisonToken } from './base'; const toRegExp = (subject: string, caseSensitive = false) : RegExp => { @@ -95,6 +98,17 @@ interface IWildcardToken extends IComparisonToken { caseSensitive: boolean; } +export const manifest : Manifest = { + arguments: ArgumentQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is a match of the right operand wildcard", + alias: ['iswm'], + casing: true, + inverse: { + description: "Checks if the left operand is not a match of the right operand wildcard", + alias: ['!iswm'] + } +}; + export default class WildcardToken extends ComparisonToken { readonly caseSensitive: boolean @@ -103,7 +117,6 @@ export default class WildcardToken extends ComparisonToken { ...token, value: 'wildcard' }); - this.caseSensitive = token.caseSensitive; } async handle(options: ParserOptions, meta: unknown): Promise { diff --git a/src/types/manifest-comparison.ts b/src/types/manifest-comparison.ts new file mode 100644 index 0000000..e7c1f87 --- /dev/null +++ b/src/types/manifest-comparison.ts @@ -0,0 +1,13 @@ +export const enum ArgumentQuantifier { + LEFTONLY, + RIGHTOPTIONAL, + RIGHTREQUIRED +} + +export default interface ComparatorManifest { + arguments: ArgumentQuantifier; + description: string; + casing?: boolean; + alias: string[]; + inverse: false | {description: string, alias: string[]} +} \ No newline at end of file From 363a30b8aede0a2604dd38e5b4026f98a9e73fbb Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 20 Sep 2022 04:17:32 -0400 Subject: [PATCH 43/92] feat: improve && and || pre-tokenization --- src/helpers/get-potential-tokens.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/helpers/get-potential-tokens.ts b/src/helpers/get-potential-tokens.ts index f710a3f..0766ea9 100644 --- a/src/helpers/get-potential-tokens.ts +++ b/src/helpers/get-potential-tokens.ts @@ -3,6 +3,10 @@ import ParserOptions from '../types/options'; import split from './unicode-safe-split'; +const isPreLogic = (subject: string, index: number) : boolean => index < 1 ? true : /^\s/.test(subject[index - 1]); + +const isPostLogic = (subject: string, index: number) : boolean => index + 2 >= subject.length ? true : /^\s$/.test(subject[index + 2]); + /** Split input string into array of potential tokens */ export default (options: ParserOptions, subject: string) : IPreToken[] => { @@ -87,8 +91,8 @@ export default (options: ParserOptions, subject: string) : IPreToken[] => { seq === '``' || ( (seq === '&&' || seq === '||') && - subject[position - 1] === ' ' && - subject[position + 2] === ' ' + isPreLogic(subject, position) && + isPostLogic(subject, position) ) ) { if (textToken !== null) { From a8e32e761bbee61ef7dbacf3686d026ad8b7426b Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 20 Sep 2022 04:17:48 -0400 Subject: [PATCH 44/92] feat: add comparison parsing --- src/helpers/whitespace.ts | 21 +++ src/tokenize/comparison/comparator-map.ts | 113 +++++++++++++++ src/tokenize/comparison/comparator.ts | 65 +++++++++ src/tokenize/comparison/comparison.ts | 121 ++++++++++++++++ src/tokenize/comparison/operand.ts | 163 ++++++++++++++++++++++ 5 files changed, 483 insertions(+) create mode 100644 src/helpers/whitespace.ts create mode 100644 src/tokenize/comparison/comparator-map.ts create mode 100644 src/tokenize/comparison/comparator.ts create mode 100644 src/tokenize/comparison/comparison.ts create mode 100644 src/tokenize/comparison/operand.ts diff --git a/src/helpers/whitespace.ts b/src/helpers/whitespace.ts new file mode 100644 index 0000000..abf47bc --- /dev/null +++ b/src/helpers/whitespace.ts @@ -0,0 +1,21 @@ +import IPreToken from '../types/pre-token'; + +export const is = (tokens: IPreToken[], cursor: number) : boolean => { + return ( + cursor < tokens.length && + ( + tokens[cursor].value === ' ' || + tokens[cursor].value === '\t' || + tokens[cursor].value === '\r' || + tokens[cursor].value === '\n' + ) + ); +}; + +export const consume = (tokens: IPreToken[], cursor: number) : number => { + while (is(tokens, cursor)) { + cursor += 1; + } + + return cursor; +} \ No newline at end of file diff --git a/src/tokenize/comparison/comparator-map.ts b/src/tokenize/comparison/comparator-map.ts new file mode 100644 index 0000000..7bf2d97 --- /dev/null +++ b/src/tokenize/comparison/comparator-map.ts @@ -0,0 +1,113 @@ +import { ArgumentQuantifier, type default as Manifest } from '../../types/manifest-comparison'; + +import ComparisonToken from '../../tokens/comparison/base'; +import { default as ContainsToken, manifest as containsManifest } from '../../tokens/comparison/contains'; +import { default as EqualLooseToken, manifest as equalLooseManifest } from '../../tokens/comparison/equal-loose'; +import { default as EqualStrictToken, manifest as equalStrictManifest } from '../../tokens/comparison/equal-strict'; +import { default as ExistsToken, manifest as existsManifest } from '../../tokens/comparison/exists'; +import { default as GreaterThanEqualToken, manifest as greaterThanEqualManifest } from '../../tokens/comparison/greater-than-equal'; +import { default as GreaterThanToken, manifest as greaterThanManifest } from '../../tokens/comparison/greater-than'; +import { default as IsBoolToken, manifest as isBoolManifest } from '../../tokens/comparison/isbool'; +import { default as IsNullToken, manifest as isNullManifest } from '../../tokens/comparison/isnull'; +import { default as LessThanEqualToken, manifest as lessThanEqualManifest } from '../../tokens/comparison/less-than-equal'; +import { default as LessThanToken, manifest as lessThanManifest } from '../../tokens/comparison/less-than'; +import { default as NumericalToken, manifest as numericalManifest } from '../../tokens/comparison/numerical'; +import { default as RegexToken, manifest as regexManifest } from '../../tokens/comparison/regex'; +import { default as WildcardToken, manifest as wildcardManifest } from '../../tokens/comparison/wildcard'; + +interface Type extends Function { + //eslint-disable-next-line @typescript-eslint/no-explicit-any + new (...args: any[]): T; +} + +interface ITokenBase { + invert?: boolean; + caseSensitive?: void | boolean; +} + +export interface IComparator { + tokenClass: Type; + arguments: ArgumentQuantifier; + description: string; + tokenBase?: ITokenBase; +} + +const comparatorMap : Map = new Map(); + +((...args: Array<[Type, Manifest]>) => { + args.forEach((arg) => { + + const [TokenClass, manifest] = arg; + + manifest.alias.forEach(alias => { + alias = alias.toLowerCase(); + + comparatorMap.set( + alias, + { + description: manifest.description, + arguments: manifest.arguments, + tokenClass: TokenClass, + tokenBase: { invert: false } + } + ); + + if (manifest.casing) { + comparatorMap.set( + alias + 'cs', + { + description: manifest.description, + arguments: manifest.arguments, + tokenClass: TokenClass, + tokenBase: { invert: false, caseSensitive: true } + } + ); + } + }); + + if (manifest.inverse !== false) { + const { alias, description } = manifest.inverse; + + alias.forEach(alias => { + alias = alias.toLowerCase(); + comparatorMap.set( + alias, + { + description: description, + arguments: manifest.arguments, + tokenClass: TokenClass, + tokenBase: { invert: true } + } + ); + + if (manifest.casing) { + comparatorMap.set( + alias + 'cs', + { + description: description, + arguments: manifest.arguments, + tokenClass: TokenClass, + tokenBase: { invert: false, caseSensitive: true } + } + ); + } + }); + } + }); +})( + [ContainsToken, containsManifest], + [EqualLooseToken, equalLooseManifest], + [EqualStrictToken, equalStrictManifest], + [ExistsToken, existsManifest], + [GreaterThanEqualToken, greaterThanEqualManifest], + [GreaterThanToken, greaterThanManifest], + [IsBoolToken, isBoolManifest], + [IsNullToken, isNullManifest], + [LessThanEqualToken, lessThanEqualManifest], + [LessThanToken, lessThanManifest], + [NumericalToken, numericalManifest], + [RegexToken, regexManifest], + [WildcardToken, wildcardManifest] +); + +export default comparatorMap; \ No newline at end of file diff --git a/src/tokenize/comparison/comparator.ts b/src/tokenize/comparison/comparator.ts new file mode 100644 index 0000000..b9fa710 --- /dev/null +++ b/src/tokenize/comparison/comparator.ts @@ -0,0 +1,65 @@ +import ITokenizeState from '../../types/tokenize-state'; + +import { consume as consumeWS, is as isWS } from '../../helpers/whitespace'; + +import comparatorMap, { IComparator } from './comparator-map'; + +export interface IComparatorState extends Omit { + output?: IComparator +} + +export default async (state: IComparatorState, asArgument = true) : Promise => { + + const { tokens } = state; + let { cursor } = state; + + if (cursor >= tokens.length) { + return false; + } + + let compname = ''; + + if (tokens[cursor].value === '!') { + compname += '!' + cursor += 1; + if (cursor >= tokens.length) { + return false; + } + } + + if (/^[a-z]+$/i.test(tokens[cursor].value)) { + compname += tokens[cursor].value; + cursor += 1; + + } else { + while ( + cursor < tokens.length && + ( + tokens[cursor].value === '=' || + tokens[cursor].value === '<' || + tokens[cursor].value === '>' + ) + ) { + compname + tokens[cursor].value; + cursor += 1; + } + } + + if ( + compname !== '' && + comparatorMap.has(compname) && + ( + cursor >= tokens.length || + tokens[cursor].value === ']' || + (asArgument && tokens[cursor].value === ',') || + isWS(tokens, cursor) + ) + ) { + + state.cursor = consumeWS(tokens, cursor); + state.output = comparatorMap.get(compname); + return true; + } + + return false; +}; \ No newline at end of file diff --git a/src/tokenize/comparison/comparison.ts b/src/tokenize/comparison/comparison.ts new file mode 100644 index 0000000..22a9d60 --- /dev/null +++ b/src/tokenize/comparison/comparison.ts @@ -0,0 +1,121 @@ +import type ITokenizeState from '../../types/tokenize-state'; +import { ArgumentQuantifier } from '../../types/manifest-comparison'; + +import { consume as consumeWS, is as isWS } from '../../helpers/whitespace'; + +import tokenizeOperand, { IOperandState } from './operand'; +import tokenizeComparator, { IComparatorState } from './comparator'; + +import comparatorMap, { IComparator } from './comparator-map'; + +import Token from '../../tokens/token'; + +export default async (state: ITokenizeState, asArgument = true) : Promise => { + const { options, stack } = state; + let { tokens, cursor } = state; + + cursor = consumeWS(tokens, cursor); + + if ( + cursor >= tokens.length || + tokens[cursor].value === ']' || + (asArgument && tokens[cursor].value) === ',' || + tokens[cursor].value === '&&' || + tokens[cursor].value === '||' + ) { + return false; + } + + const startPosition = tokens[cursor].position; + + const mockState : ITokenizeState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + cursor + }; + + let comparator : void | IComparator = undefined; + + const leftCheck = await tokenizeOperand( + mockState, + asArgument, + async (state: IOperandState, asArgument: boolean) : Promise => { + const { options, stack, operand, tokens, cursor } = state; + + const mockState : IComparatorState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + cursor + }; + + if ( + !operand.tokens.length || + !operand.leadingWhitespace || + !(await tokenizeComparator(mockState, asArgument)) + ) { + return false; + } + + comparator = mockState.output; + state.cursor = mockState.cursor; + state.tokens = mockState.tokens; + state.endOfOperand = true; + + return true; + } + ); + + if (!leftCheck) { + throw new Error('TODO - SyntaxError: Left hand expression expected'); + } + const left = mockState.output; + + tokens = mockState.tokens; + cursor = mockState.cursor; + + if (comparator == null) { + comparator = comparatorMap.get('exists'); + cursor = consumeWS(tokens, cursor); + } + + let right : undefined | Token; + + const argQuant = (comparator).arguments; + if (argQuant !== ArgumentQuantifier.LEFTONLY) { + const mockState : ITokenizeState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + cursor + } + + if (await tokenizeOperand(mockState, asArgument)) { + tokens = mockState.tokens; + cursor = consumeWS(tokens, mockState.cursor); + right = mockState.output; + + } else if (argQuant === ArgumentQuantifier.RIGHTREQUIRED) { + throw new Error('TODO - SyntaxError: Right hand expression expected'); + } + } + + if ( + cursor >= tokens.length || + tokens[cursor].value === ']' || + (asArgument && tokens[cursor].value === ',') + ) { + state.cursor = cursor; + state.tokens = tokens; + state.output = new (comparator).tokenClass({ + position: startPosition, + left, + right + }); + return true; + } + + // TODO - custom error - Illegal character + throw new Error('TODO - SyntaxError: illegal character'); +} \ No newline at end of file diff --git a/src/tokenize/comparison/operand.ts b/src/tokenize/comparison/operand.ts new file mode 100644 index 0000000..66e270c --- /dev/null +++ b/src/tokenize/comparison/operand.ts @@ -0,0 +1,163 @@ +import TokenType from '../../types/token-types'; +import ITokenizeState from '../../types/tokenize-state'; + +import { consume as consumeWS, is as isWS } from '../../helpers/whitespace'; + +import tokenizeTextEscapeSingle from '../text-escape-single'; +import tokenizeTextEscapeBlock from '../text-escape-block'; +import tokenizeTextQuoted from '../text-quoted'; +import tokenizeTextSpecial from '../text-special'; +import tokenizeFunctionIf from '../function-if'; +import tokenizeFunction from '../function'; + +import Token from '../../tokens/token'; +import TextToken from '../../tokens/token-text'; +import TokenList from '../../tokens/token-list'; + +export interface IOperandState extends ITokenizeState { + endOfOperand?: boolean; + operand: { + tokens: Token[]; + leadingWhitespace: boolean; + }; +} + +type EndCheck = (state: IOperandState, asArgument: boolean) => Promise; + +export default async ( + state: ITokenizeState, + asArgument: boolean, + endCheck?: EndCheck +) : Promise => { + + const { options, stack } = state; + let { tokens, cursor } = state; + + cursor = consumeWS(tokens, cursor); + + const startPosition = cursor; + const operand : Token[] = []; + + let ws = '', wspos = 0; + + while ( + cursor < tokens.length && + tokens[cursor].value !== ']' && + tokens[cursor].value !== '&&' && + tokens[cursor].value !== '||' && + (!(asArgument && tokens[cursor].value === ',')) + ) { + + // consume whitespace + if (isWS(tokens, cursor)) { + if (ws === '') { + wspos = tokens[cursor].position; + } + ws += tokens[cursor].value; + cursor += 1; + continue; + } + + // non literal-text token + const mockState : ITokenizeState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + cursor + } + if ( + await tokenizeTextEscapeSingle(mockState, ['"', '$', '\\', ',', ']',]) || + await tokenizeTextEscapeBlock(mockState) || + await tokenizeTextQuoted(mockState) || + await tokenizeTextSpecial(mockState) || + await tokenizeFunctionIf(mockState) || + await tokenizeFunction(mockState) + ) { + const last = operand[operand.length - 1]; + const token = mockState.output; + + if (!last) { + operand.push(mockState.output); + + } else if (last.type === TokenType.TEXT) { + last.value += ws; + if (token.type === TokenType.TEXT) { + last.value += token.value; + } else { + operand.push(token); + } + + } else if (token.type === TokenType.TEXT) { + token.position = wspos; + token.value = ws + token.value; + operand.push(token); + + } else { + operand.push(new TextToken({ + position: wspos, + value: ws + })); + operand.push(token); + } + + ws = ''; + wspos = 0; + + tokens = mockState.tokens; + cursor = mockState.cursor; + + continue; + } + + if (endCheck) { + const checkState : IOperandState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + operand: { + tokens: operand, + leadingWhitespace: ws !== '' + }, + cursor + }; + if (await endCheck(checkState, asArgument)) { + tokens = checkState.tokens; + cursor = checkState.cursor; + if (checkState.endOfOperand) { + break; + } + } + } + + // plain text token + if (!operand.length) { + operand.push(new TextToken(tokens[cursor])); + + } else if (operand[operand.length - 1].type === TokenType.TEXT) { + operand[operand.length - 1].value += ws + tokens[cursor].value; + + } else { + operand.push(new TextToken({ + position: ws !== '' ? wspos : tokens[cursor].position, + value: tokens[cursor].value + })); + } + + ws = ''; + wspos = 0; + cursor += 1; + } + + if (!operand.length) { + return false; + } + + state.tokens = tokens; + state.cursor = cursor; + state.output = new TokenList({ + position: startPosition, + value: operand + }); + + return true; +} \ No newline at end of file From dbd2496cba68f47bf648aa1154ec18aa00947771 Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 20 Sep 2022 04:22:01 -0400 Subject: [PATCH 45/92] feat: add lint as script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4063e65..8d14b88 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "type": "commonjs", "main": "./lib/index.js", "scripts": { + "lint": "eslint .", "build": "tsc --build", "prepack": "tsc --build" }, From 425f65aec9b72b50f7816bb7627efc1c451cb064 Mon Sep 17 00:00:00 2001 From: SReject Date: Tue, 20 Sep 2022 04:22:18 -0400 Subject: [PATCH 46/92] chore: lint --- src/tokenize/argument.ts | 2 +- src/tokenize/comparison/comparison.ts | 2 +- src/tokenize/text-quoted.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tokenize/argument.ts b/src/tokenize/argument.ts index 23c62ef..2472edf 100644 --- a/src/tokenize/argument.ts +++ b/src/tokenize/argument.ts @@ -59,7 +59,7 @@ export default async (state: ITokenizeState) : Promise => { if (lastTokenIsText) { lastToken.value += whitespace; if (mockTokenIsText) { - lastToken.value += output.value; + lastToken.value += output.value; } else { result.push(output); diff --git a/src/tokenize/comparison/comparison.ts b/src/tokenize/comparison/comparison.ts index 22a9d60..a25eb3d 100644 --- a/src/tokenize/comparison/comparison.ts +++ b/src/tokenize/comparison/comparison.ts @@ -1,7 +1,7 @@ import type ITokenizeState from '../../types/tokenize-state'; import { ArgumentQuantifier } from '../../types/manifest-comparison'; -import { consume as consumeWS, is as isWS } from '../../helpers/whitespace'; +import { consume as consumeWS } from '../../helpers/whitespace'; import tokenizeOperand, { IOperandState } from './operand'; import tokenizeComparator, { IComparatorState } from './comparator'; diff --git a/src/tokenize/text-quoted.ts b/src/tokenize/text-quoted.ts index 75aed43..0d3a445 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/tokenize/text-quoted.ts @@ -57,7 +57,7 @@ export default async (state: ITokenizeState) : Promise => { lastToken.type === TokenType.TEXT && (mockState.output).type === TokenType.TEXT ) { - lastToken.value += (mockState.output).value; + lastToken.value += (mockState.output).value; } else { quoteTokens.push(mockState.output); } From 8e5f5950361c72bd0c48adb0c04ed72dbe693f4b Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 03:40:14 -0400 Subject: [PATCH 47/92] implement: conditions --- src/tokenize/argument-list.ts | 2 +- .../comparison/{comparison.ts => index.ts} | 0 src/tokenize/condition-block.ts | 45 +++++++++++++ src/tokenize/condition-logical.ts | 67 +++++++++++++++++++ src/tokenize/condition-not.ts | 44 ++++++++++++ src/tokenize/condition.ts | 54 ++++++++++++--- src/tokens/logical/base.ts | 2 +- src/tokens/logical/logical-not.ts | 4 +- 8 files changed, 207 insertions(+), 11 deletions(-) rename src/tokenize/comparison/{comparison.ts => index.ts} (100%) create mode 100644 src/tokenize/condition-block.ts create mode 100644 src/tokenize/condition-logical.ts create mode 100644 src/tokenize/condition-not.ts diff --git a/src/tokenize/argument-list.ts b/src/tokenize/argument-list.ts index 1de855f..9ae3bd7 100644 --- a/src/tokenize/argument-list.ts +++ b/src/tokenize/argument-list.ts @@ -50,7 +50,7 @@ export default async (state: ITokenizeState, isCondition = false) : Promise => { + const { options, stack, tokens } = state; + let { cursor } = state; + + cursor = consumeWS(tokens, cursor); + + if (cursor + 1 >= tokens.length || tokens[cursor].value !== '[') { + return false; + } + cursor += 1; + + const mockState : ITokenizeState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + cursor + }; + + if (!await tokenizeCondition(mockState)) { + throw new Error('TODO - SyntaxError: conditional expected'); + } + + cursor = consumeWS(tokens, mockState.cursor); + if ( + cursor >= tokens.length + ) { + throw new Error('TODO - SyntaxError: Unexpected end'); + } + + if (tokens[cursor].value !== ']') { + throw new Error('TODO - SyntaxError: expected \']\''); + } + + state.tokens = mockState.tokens; + state.cursor = cursor + 1; + state.output = mockState.output; + return true; +} \ No newline at end of file diff --git a/src/tokenize/condition-logical.ts b/src/tokenize/condition-logical.ts new file mode 100644 index 0000000..3241a17 --- /dev/null +++ b/src/tokenize/condition-logical.ts @@ -0,0 +1,67 @@ +import { consume as consumeWS } from '../helpers/whitespace'; + +import type ITokenizeState from '../types/tokenize-state'; + +import type Token from '../tokens/token'; +import type LogicalToken from '../tokens/logical/base'; +import AndLogicalToken from '../tokens/logical/logical-and'; +import OrLogicalToken from '../tokens/logical/logical-or'; + +import tokenizeConditionalNot from './condition-not'; +import tokenizeConditionBlock from './condition-block'; +import tokenizeComparison from './comparison/index'; + +interface Type extends Function { + //eslint-disable-next-line @typescript-eslint/no-explicit-any + new (...args: any[]): T; +} + +export default async (state: ITokenizeState, leftCondition: Token, asArgument = false) : Promise => { + + const { options, stack } = state; + let { tokens, cursor } = state; + + cursor = consumeWS(tokens, cursor); + + const startPosition = tokens[cursor].position; + + if ( + tokens[cursor].value !== '&&' && + tokens[cursor].value !== '||' + ) { + return false; + } + const TokenClass : Type = (tokens[cursor].value === '||' ? OrLogicalToken : AndLogicalToken); + + cursor = consumeWS(tokens, cursor + 1); + + const mockState : ITokenizeState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + cursor + }; + + let rightCondition: Token; + if ( + await tokenizeConditionalNot(mockState) || + await tokenizeConditionBlock(mockState) || + await tokenizeComparison(mockState, asArgument) + ) { + tokens = mockState.tokens; + cursor = mockState.cursor; + rightCondition = mockState.output; + + } else { + throw new Error('TODO - SyntaxError: invalid right handle conditional'); + } + + state.tokens = tokens; + state.cursor = cursor; + state.output = new TokenClass({ + position: startPosition, + left: leftCondition, + right: rightCondition + }); + return true; +} \ No newline at end of file diff --git a/src/tokenize/condition-not.ts b/src/tokenize/condition-not.ts new file mode 100644 index 0000000..b76b172 --- /dev/null +++ b/src/tokenize/condition-not.ts @@ -0,0 +1,44 @@ +import type ITokenizeState from '../types/tokenize-state'; + +import type Token from '../tokens/token'; +import NotToken from '../tokens/logical/logical-not'; + +import tokenizeConditionBlock from './condition-block'; + +export default async (state: ITokenizeState) : Promise => { + + const { options, stack, tokens } = state; + let { cursor } = state; + + const startPosition = cursor; + + if ( + cursor + 1 >= tokens.length || + tokens[cursor].value !== '!' || + tokens[cursor + 1].value !== '[' + ) { + return false; + } + + cursor += 1; + + const mockState : ITokenizeState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + cursor + }; + + if (await tokenizeConditionBlock(mockState)) { + state.tokens = mockState.tokens; + state.cursor = mockState.cursor; + state.output = new NotToken({ + position: startPosition, + left: mockState.output + }); + + return true; + } + + return false; +}; \ No newline at end of file diff --git a/src/tokenize/condition.ts b/src/tokenize/condition.ts index 65d5a78..feef757 100644 --- a/src/tokenize/condition.ts +++ b/src/tokenize/condition.ts @@ -1,14 +1,52 @@ import type ITokenizeState from '../types/tokenize-state'; -export default async (state: ITokenizeState) : Promise => { +import type Token from '../tokens/token'; - const { options, tokens, cursor, stack } = state; +import tokenizeComparison from './comparison'; +import tokenizeConditionBlock from './condition-block'; +import tokenizeConditionNot from './condition-not'; +import tokenizeLogicalOperator from './condition-logical'; - /* - // logical operator - // not operator - // compare operator - */ +export default async (state: ITokenizeState, asArgument = false) : Promise => { - return false; + const { options, stack, tokens, cursor } = state; + + let mockState: ITokenizeState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + cursor + }; + + if ( + await tokenizeConditionNot(mockState) || + await tokenizeConditionBlock(mockState) || + await tokenizeComparison(mockState, asArgument) + ) { + let condition = mockState.output; + + mockState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...(mockState.tokens) ], + cursor: mockState.cursor + }; + while (await tokenizeLogicalOperator(mockState, condition, asArgument)) { + condition = mockState.output; + mockState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...(mockState.tokens) ], + cursor: mockState.cursor + }; + } + + state.tokens = mockState.tokens; + state.cursor = mockState.cursor; + state.output = condition; + + return true; + } + + return false } \ No newline at end of file diff --git a/src/tokens/logical/base.ts b/src/tokens/logical/base.ts index 324e193..c06d838 100644 --- a/src/tokens/logical/base.ts +++ b/src/tokens/logical/base.ts @@ -2,7 +2,7 @@ import TokenType from '../../types/token-types'; import { default as OperatorToken, IOperatorToken} from '../token-operator'; -export interface ILogicalToken extends IOperatorToken { +export interface ILogicalToken extends Omit { value: unknown; } diff --git a/src/tokens/logical/logical-not.ts b/src/tokens/logical/logical-not.ts index 6af37fd..cf16be6 100644 --- a/src/tokens/logical/logical-not.ts +++ b/src/tokens/logical/logical-not.ts @@ -1,8 +1,10 @@ import ParserOptions from '../../types/options'; import { default as LogicalToken, ILogicalToken } from './base'; +export type INotOperator = Omit; + export default class NotOperator extends LogicalToken { - constructor(token: ILogicalToken) { + constructor(token: INotOperator) { super({ ...token, value: 'not' From 8570e5d71ddf5b74b078efbbadaa128fc59f6014 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 03:42:27 -0400 Subject: [PATCH 48/92] chore: bump version --- package-lock.json | 4 ++-- package.json | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f3268bf..a35a3b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "expressionish", - "version": "0.0.1", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "expressionish", - "version": "0.0.1", + "version": "2.0.0", "license": "ISC", "devDependencies": { "@types/node": "^18.7.18", diff --git a/package.json b/package.json index 8d14b88..05b51de 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,7 @@ { "name": "expressionish", "author": "SReject", - "version": "0.0.1", - "private": true, + "version": "2.0.0", "license": "ISC", "type": "commonjs", "main": "./lib/index.js", From 14f3a3311bab408ff78ef9d6021a280927a3b4a1 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 03:45:25 -0400 Subject: [PATCH 49/92] fix: wildcard typings --- src/tokens/comparison/wildcard.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts index 67332b6..aed0ed2 100644 --- a/src/tokens/comparison/wildcard.ts +++ b/src/tokens/comparison/wildcard.ts @@ -143,12 +143,12 @@ export default class WildcardToken extends ComparisonToken { return false; } - const v2RegExp = toRegExp(v2Text); + const v2RegExp = toRegExp(v2Text); if (v2RegExp == null) { return false; } - return v2RegExp.test(v1Text); + return v2RegExp.test(v1Text); } async handleInverse(options: ParserOptions, meta: unknown): Promise { @@ -176,12 +176,12 @@ export default class WildcardToken extends ComparisonToken { return false; } - const v2RegExp = toRegExp(v2Text); + const v2RegExp = toRegExp(v2Text); if (v2RegExp == null) { return false; } - return !v2RegExp.test(v1Text); + return !v2RegExp.test(v1Text); } toToken() : object { From 9cb1bc44fc43287f8e267205627bf9175990d924 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 07:36:13 -0400 Subject: [PATCH 50/92] fix: input must be a string --- src/index.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 0c78a42..e1decc1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { type IFunctionHandler, type IFunctionLookup } from './types/options'; + import TokenType from './types/token-types'; import ITokenizeState from './types/tokenize-state'; @@ -34,7 +35,7 @@ export class Expressionish { private config: ParserOptions; - constructor(options?: ParserOptions) { + constructor(options: ParserOptions = {}) { this.functionHandlers = options.functionHandlers || {}; this.functionLookups = options.functionLookups || {}; @@ -75,10 +76,14 @@ export class Expressionish { } async tokenize(subject: string) : Promise { - let tokens = getPotentialTokens( - this.config, - subject - ); + if ( + subject == null || + typeof subject !== 'string' + ) { + throw new TypeError('input must be a string'); + } + + let tokens = getPotentialTokens(this.config, subject); let cursor = 0; const result : Token[] = []; From 10e13b52621ffc71fd2f93d6f22f52a28fa84e8f Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 07:36:52 -0400 Subject: [PATCH 51/92] feat: move unknown to be index 0 --- src/types/token-types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/token-types.ts b/src/types/token-types.ts index 20861f2..b10b45b 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -1,4 +1,5 @@ const enum TokenType { + UNKNOWN, EXPRESSION, TOKENLIST, IFSTATEMENT, @@ -7,8 +8,7 @@ const enum TokenType { ARGUMENT, LOGICAL, COMPARISON, - EMPTY, - UNKNOWN + EMPTY } export default TokenType; \ No newline at end of file From 80ffa7fc414101f61ccdfb1a672006a740956b40 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 07:37:20 -0400 Subject: [PATCH 52/92] feat: change toToken to toJSON --- src/tokens/comparison/base.ts | 2 +- src/tokens/token-function-if.ts | 8 ++++---- src/tokens/token-function.ts | 4 ++-- src/tokens/token-list.ts | 4 ++-- src/tokens/token-operator.ts | 8 ++++---- src/tokens/token.ts | 22 +++++++++++----------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/tokens/comparison/base.ts b/src/tokens/comparison/base.ts index b1dd330..dfb47ba 100644 --- a/src/tokens/comparison/base.ts +++ b/src/tokens/comparison/base.ts @@ -41,7 +41,7 @@ export default class ComparisonToken extends OperatorToken { toToken() : object { return { - ...(super.toToken()), + ...(super.toJSON()), invert: this.invert } } diff --git a/src/tokens/token-function-if.ts b/src/tokens/token-function-if.ts index 6e8d372..10993e8 100644 --- a/src/tokens/token-function-if.ts +++ b/src/tokens/token-function-if.ts @@ -49,10 +49,10 @@ export default class IfStatementToken extends Token { toToken() : object { return { - ...(super.toToken()), - condition: this.condition.toToken(), - whenTrue: this.whenTrue.toToken(), - whenFalse: this.whenFalse?.toToken() + ...(super.toJSON()), + condition: this.condition.toJSON(), + whenTrue: this.whenTrue.toJSON(), + whenFalse: this.whenFalse?.toJSON() } } } \ No newline at end of file diff --git a/src/tokens/token-function.ts b/src/tokens/token-function.ts index 13366c8..436c3a8 100644 --- a/src/tokens/token-function.ts +++ b/src/tokens/token-function.ts @@ -60,9 +60,9 @@ export default class FunctionalToken extends Token { toToken() : object { return { - ...(super.toToken()), + ...(super.toJSON()), prefix: this.prefix, - arguments: this.arguments.map(value => value.toToken()) + arguments: this.arguments.map(value => value.toJSON()) } } } \ No newline at end of file diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index 8b971aa..4a0f6c3 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -64,8 +64,8 @@ export default class TokenList extends Token { toToken() : object { return { - ...(super.toToken()), - value: this.value.map(value => value.toToken()) + ...(super.toJSON()), + value: this.value.map(value => value.toJSON()) }; } } \ No newline at end of file diff --git a/src/tokens/token-operator.ts b/src/tokens/token-operator.ts index 47b07d1..bd9b654 100644 --- a/src/tokens/token-operator.ts +++ b/src/tokens/token-operator.ts @@ -24,15 +24,15 @@ export default class OperatorToken extends Token { return false; } - toToken() : object { + toJSON() : object { const result : Record = { - ...(super.toToken()), - left: this.left.toToken() + ...(super.toJSON()), + left: this.left.toJSON() } if (this.right != null) { - result.right = this.right.toToken(); + result.right = this.right.toJSON(); } return result; } diff --git a/src/tokens/token.ts b/src/tokens/token.ts index 5c51302..84df6e7 100644 --- a/src/tokens/token.ts +++ b/src/tokens/token.ts @@ -3,18 +3,18 @@ import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; export interface IToken { - type: TokenType; - position: number; + type?: TokenType; + position?: number; value?: unknown; } export default class Token { - public type: TokenType; + public type : TokenType; public position : number; public value : unknown; - constructor(token: IToken) { + constructor(token: IToken = {}) { this.type = token.type == null ? TokenType.UNKNOWN : token.type; this.position = token.position != null ? token.position : -1; this.value = token.value; @@ -22,18 +22,18 @@ export default class Token { /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ async evaluate(options: ParserOptions, meta: unknown) : Promise { - return this.value == null ? '' : this.value; + return this.value == null ? null : this.value; } - toString() : string { - return JSON.stringify(this.toToken()); - } - - toToken() : object { + toJSON() : Record { return { type: this.type, position: this.position, - value: this.value + value: this.value == null ? null : this.value }; } + + toString() : string { + return JSON.stringify(this.toJSON()); + } } \ No newline at end of file From 7a8bf7de9080cc90dfb2ecbc7a1afb23f1c66f71 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 07:37:58 -0400 Subject: [PATCH 53/92] feat: setup jest testing --- jest.config.js | 14 + jest/helpers.ts | 31 + package-lock.json | 7024 +++++++++++++++++++++++++++++++++++++-------- package.json | 7 +- test/evaluate.js | 351 --- 5 files changed, 5811 insertions(+), 1616 deletions(-) create mode 100644 jest.config.js create mode 100644 jest/helpers.ts delete mode 100644 test/evaluate.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..67ba8a1 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,14 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + transform: { + '.ts$': [ + 'ts-jest', + { + tsconfig: "tsconfig.test.json" + } + ] + }, + setupFilesAfterEnv: ["./jest/helpers.ts"] +}; \ No newline at end of file diff --git a/jest/helpers.ts b/jest/helpers.ts new file mode 100644 index 0000000..ab2b861 --- /dev/null +++ b/jest/helpers.ts @@ -0,0 +1,31 @@ +/* eslint @typescript-eslint/no-empty-interface: off, @typescript-eslint/no-namespace: off */ +export {} + +interface CustomMatchers { + hasProperty(key: string): R; +} + +declare global { + namespace jest { + interface Expect extends CustomMatchers {} + interface Matchers extends CustomMatchers {} + interface InverseAsymmetricMatchers extends CustomMatchers {} + } +} + +const hasOwnProperty = Object.prototype.hasOwnProperty + +expect.extend({ + hasProperty: (subject: unknown, key: string) : {pass: boolean, message: () => string} => { + if (hasOwnProperty.call(subject, key)) { + return { + pass: true, + message: () => `expected subject not to have '${key}' as a property` + }; + } + return { + pass: false, + message: () => `expected subject to have '${key}' as a property` + } + } +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a35a3b1..c4645b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,1240 +9,3355 @@ "version": "2.0.0", "license": "ISC", "devDependencies": { + "@types/jest": "^29.0.3", "@types/node": "^18.7.18", "@typescript-eslint/eslint-plugin": "^5.37.0", "@typescript-eslint/parser": "^5.37.0", "eslint": "^8.4.1", - "mocha": "^10.0.0", + "jest": "^29.0.3", + "ts-jest": "^29.0.1", "typescript": "^4.8.3" } }, - "node_modules/@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.2.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "@babel/highlight": "^7.18.6" }, "engines": { - "node": ">=10.10.0" + "node": ">=6.9.0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "node_modules/@babel/compat-data": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.1.tgz", + "integrity": "sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@babel/core": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.1.tgz", + "integrity": "sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.0", + "@babel/helper-compilation-targets": "^7.19.1", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helpers": "^7.19.0", + "@babel/parser": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, - "engines": { - "node": ">= 8" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@babel/generator": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", + "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@babel/types": "^7.19.0", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.7.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", - "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.37.0.tgz", - "integrity": "sha512-Fde6W0IafXktz1UlnhGkrrmnnGpAo1kyX7dnyHHVrmwJOn72Oqm3eYtddrpOwwel2W8PAK9F3pIL5S+lfoM0og==", + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.37.0", - "@typescript-eslint/type-utils": "5.37.0", - "@typescript-eslint/utils": "5.37.0", - "debug": "^4.3.4", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz", + "integrity": "sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.19.1", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/core": "^7.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true, "engines": { - "node": ">= 4" + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.37.0.tgz", - "integrity": "sha512-01VzI/ipYKuaG5PkE5+qyJ6m02fVALmMPY3Qq5BHflDx3y4VobbLdHQkSMg9VPRS4KdNt4oYTMaomFoHonBGAw==", + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.37.0", - "@typescript-eslint/types": "5.37.0", - "@typescript-eslint/typescript-estree": "5.37.0", - "debug": "^4.3.4" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.37.0.tgz", - "integrity": "sha512-F67MqrmSXGd/eZnujjtkPgBQzgespu/iCZ+54Ok9X5tALb9L2v3G+QBSoWkXG0p3lcTJsL+iXz5eLUEdSiJU9Q==", + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.37.0", - "@typescript-eslint/visitor-keys": "5.37.0" + "@babel/types": "^7.18.6" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.37.0.tgz", - "integrity": "sha512-BSx/O0Z0SXOF5tY0bNTBcDEKz2Ec20GVYvq/H/XNKiUorUFilH7NPbFUuiiyzWaSdN3PA8JV0OvYx0gH/5aFAQ==", + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.37.0", - "@typescript-eslint/utils": "5.37.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" + "@babel/types": "^7.18.6" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.37.0.tgz", - "integrity": "sha512-3frIJiTa5+tCb2iqR/bf7XwU20lnU05r/sgPJnRpwvfZaqCJBrl8Q/mw9vr3NrNdB/XtVyMA0eppRMMBqdJ1bA==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.37.0.tgz", - "integrity": "sha512-JkFoFIt/cx59iqEDSgIGnQpCTRv96MQnXCYvJi7QhBC24uyuzbD8wVbajMB1b9x4I0octYFJ3OwjAwNqk1AjDA==", + "node_modules/@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.37.0", - "@typescript-eslint/visitor-keys": "5.37.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@babel/types": "^7.18.6" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.37.0.tgz", - "integrity": "sha512-jUEJoQrWbZhmikbcWSMDuUSxEE7ID2W/QCV/uz10WtQqfOuKZUqFGjqLJ+qhDd17rjgp+QJPqTdPIBWwoob2NQ==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.37.0", - "@typescript-eslint/types": "5.37.0", - "@typescript-eslint/typescript-estree": "5.37.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@babel/types": "^7.18.6" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, "engines": { - "node": ">=8.0.0" + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, "engines": { - "node": ">=4.0" + "node": ">=6.9.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.37.0.tgz", - "integrity": "sha512-Hp7rT4cENBPIzMwrlehLW/28EVCOcE9U1Z1BQTc8EA8v5qpr7GRGuG+U58V5tTY48zvUOA3KHvw3rA8tY9fbdA==", + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.37.0", - "eslint-visitor-keys": "^3.3.0" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=6.9.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=4" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "color-name": "1.1.3" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.8.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4" } }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "has-flag": "^3.0.0" }, "engines": { - "node": ">= 8" + "node": ">=4" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/@babel/parser": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.1.tgz", + "integrity": "sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==", "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "@babel/helper-plugin-utils": "^7.12.13" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" + "@babel/helper-plugin-utils": "^7.8.0" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=7.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" + "@babel/helper-plugin-utils": "^7.8.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", "dev": true, "dependencies": { - "path-type": "^4.0.0" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "node_modules/@babel/traverse": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.1.tgz", + "integrity": "sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1" + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.19.1", + "@babel/types": "^7.19.0", + "debug": "^4.1.0", + "globals": "^11.1.0" }, "engines": { - "node": ">=8.6" + "node": ">=6.9.0" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/@babel/types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.9.0" } }, - "node_modules/eslint": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz", - "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@eslint/eslintrc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.0.5", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "ajv": "^6.12.4", "debug": "^4.3.2", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.1.0", "espree": "^9.2.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.9.0", "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", + "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.2.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "strip-json-comments": "^3.1.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10.10.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^2.0.0" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "peerDependencies": { - "eslint": ">=5" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/@jest/console": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.0.3.tgz", + "integrity": "sha512-cGg0r+klVHSYnfE977S9wmpuQ9L+iYuYgL+5bPXiUlUynLLYunRxswEmhBzvrSKGof5AKiHuTTmUKAqRcDY9dg==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.0.3", + "jest-util": "^29.0.3", + "slash": "^3.0.0" }, "engines": { - "node": ">=10.13.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/espree": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", - "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "node_modules/@jest/core": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.0.3.tgz", + "integrity": "sha512-1d0hLbOrM1qQE3eP3DtakeMbKTcXiXP3afWxqz103xPyddS2NhnNghS7MaXx1dcDt4/6p4nlhmeILo2ofgi8cQ==", "dev": true, "dependencies": { - "acorn": "^8.6.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" + "@jest/console": "^29.0.3", + "@jest/reporters": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.0.0", + "jest-config": "^29.0.3", + "jest-haste-map": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.3", + "jest-resolve-dependencies": "^29.0.3", + "jest-runner": "^29.0.3", + "jest-runtime": "^29.0.3", + "jest-snapshot": "^29.0.3", + "jest-util": "^29.0.3", + "jest-validate": "^29.0.3", + "jest-watcher": "^29.0.3", + "micromatch": "^4.0.4", + "pretty-format": "^29.0.3", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/@jest/environment": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.0.3.tgz", + "integrity": "sha512-iKl272NKxYNQNqXMQandAIwjhQaGw5uJfGXduu8dS9llHi8jV2ChWrtOAVPnMbaaoDhnI3wgUGNDvZgHeEJQCA==", "dev": true, "dependencies": { - "estraverse": "^5.1.0" + "@jest/fake-timers": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "jest-mock": "^29.0.3" }, "engines": { - "node": ">=0.10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/@jest/expect": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.0.3.tgz", + "integrity": "sha512-6W7K+fsI23FQ01H/BWccPyDZFrnU9QlzDcKOjrNVU5L8yUORFAJJIpmyxWPW70+X624KUNqzZwPThPMX28aXEQ==", "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "expect": "^29.0.3", + "jest-snapshot": "^29.0.3" }, "engines": { - "node": ">=4.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/@jest/expect-utils": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.0.3.tgz", + "integrity": "sha512-i1xUkau7K/63MpdwiRqaxgZOjxYs4f0WMTGJnYwUKubsNRZSeQbLorS7+I4uXVF9KQ5r61BUPAUMZ7Lf66l64Q==", "dev": true, + "dependencies": { + "jest-get-type": "^29.0.0" + }, "engines": { - "node": ">=4.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/@jest/fake-timers": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.0.3.tgz", + "integrity": "sha512-tmbUIo03x0TdtcZCESQ0oQSakPCpo7+s6+9mU19dd71MptkP4zCwoeZqna23//pgbhtT1Wq02VmA9Z9cNtvtCQ==", "dev": true, + "dependencies": { + "@jest/types": "^29.0.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^29.0.3", + "jest-mock": "^29.0.3", + "jest-util": "^29.0.3" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "node_modules/@jest/globals": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.0.3.tgz", + "integrity": "sha512-YqGHT65rFY2siPIHHFjuCGUsbzRjdqkwbat+Of6DmYRg5shIXXrLdZoVE/+TJ9O1dsKsFmYhU58JvIbZRU1Z9w==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "@jest/environment": "^29.0.3", + "@jest/expect": "^29.0.3", + "@jest/types": "^29.0.3", + "jest-mock": "^29.0.3" }, "engines": { - "node": ">=8.6.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "node_modules/@jest/reporters": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.0.3.tgz", + "integrity": "sha512-3+QU3d4aiyOWfmk1obDerie4XNCaD5Xo1IlKNde2yGEi02WQD+ZQD0i5Hgqm1e73sMV7kw6pMlCnprtEwEVwxw==", "dev": true, "dependencies": { - "reusify": "^1.0.4" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.0.3", + "jest-util": "^29.0.3", + "jest-worker": "^29.0.3", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "@sinclair/typebox": "^0.24.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/@jest/source-map": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.0.0.tgz", + "integrity": "sha512-nOr+0EM8GiHf34mq2GcJyz/gYFyLQ2INDhAylrZJ9mMWoW21mLBfZa0BUVPPMxVYrLjeiRe2Z7kWXOGnS0TFhQ==", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/@jest/test-result": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.0.3.tgz", + "integrity": "sha512-vViVnQjCgTmbhDKEonKJPtcFe9G/CJO4/Np4XwYJah+lF2oI7KKeRp8t1dFvv44wN2NdbDb/qC6pi++Vpp0Dlg==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "@jest/console": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.0.3.tgz", + "integrity": "sha512-Hf4+xYSWZdxTNnhDykr8JBs0yBN/nxOXyUQWfotBUqqy0LF9vzcFB0jm/EDNZCx587znLWTIgxcokW7WeZMobQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.0.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.3", + "slash": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "node_modules/@jest/transform": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.3.tgz", + "integrity": "sha512-C5ihFTRYaGDbi/xbRQRdbo5ddGtI4VSpmL6AIcZxdhwLbXMa7PcXxxqyI91vGOFHnn5aVM3WYnYKCHEqmLVGzg==", "dev": true, - "bin": { - "flat": "cli.js" + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.0.3", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.3", + "jest-regex-util": "^29.0.0", + "jest-util": "^29.0.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/@jest/types": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.3.tgz", + "integrity": "sha512-coBJmOQvurXjN1Hh5PzF7cmsod0zLIOXpP8KD161mqNlroMhLcwpODiEzi7ZsRl5Z/AIuxpeNm8DCl43F4kz8A==", "dev": true, "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", - "dev": true + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=6.0.0" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, - "node_modules/get-caller-file": { + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 8" } }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": "*" + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.42", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.42.tgz", + "integrity": "sha512-d+2AtrHGyWek2u2ITF0lHRIv6Tt7X0dEHW+0rP+5aDCEjC3fiN2RBjrLD0yU0at52BcZbRGxLbAtXiR0hFCjYw==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.1.tgz", + "integrity": "sha512-FSdLaZh2UxaMuLp9lixWaHq/golWTRWOnRsAXzDTDSDOQLuZb1nsdCt6pJSPWSEQt2eFZ2YVk3oYhn+1kLMeMA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.0.3.tgz", + "integrity": "sha512-F6ukyCTwbfsEX5F2YmVYmM5TcTHy1q9P5rWlRbrk56KyMh3v9xRGUO3aa8+SkvMi0SHXtASJv1283enXimC0Og==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", + "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz", + "integrity": "sha512-Nz4MPhecOFArtm81gFQvQqdV7XYCrWKx5uUt6GNHredFHn1i2mtWqXTON7EPXMtNi1qjtjEM/VCHDhcHsAMLXQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.37.0.tgz", + "integrity": "sha512-Fde6W0IafXktz1UlnhGkrrmnnGpAo1kyX7dnyHHVrmwJOn72Oqm3eYtddrpOwwel2W8PAK9F3pIL5S+lfoM0og==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.37.0", + "@typescript-eslint/type-utils": "5.37.0", + "@typescript-eslint/utils": "5.37.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.37.0.tgz", + "integrity": "sha512-01VzI/ipYKuaG5PkE5+qyJ6m02fVALmMPY3Qq5BHflDx3y4VobbLdHQkSMg9VPRS4KdNt4oYTMaomFoHonBGAw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.37.0", + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/typescript-estree": "5.37.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.37.0.tgz", + "integrity": "sha512-F67MqrmSXGd/eZnujjtkPgBQzgespu/iCZ+54Ok9X5tALb9L2v3G+QBSoWkXG0p3lcTJsL+iXz5eLUEdSiJU9Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/visitor-keys": "5.37.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.37.0.tgz", + "integrity": "sha512-BSx/O0Z0SXOF5tY0bNTBcDEKz2Ec20GVYvq/H/XNKiUorUFilH7NPbFUuiiyzWaSdN3PA8JV0OvYx0gH/5aFAQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.37.0", + "@typescript-eslint/utils": "5.37.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.37.0.tgz", + "integrity": "sha512-3frIJiTa5+tCb2iqR/bf7XwU20lnU05r/sgPJnRpwvfZaqCJBrl8Q/mw9vr3NrNdB/XtVyMA0eppRMMBqdJ1bA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.37.0.tgz", + "integrity": "sha512-JkFoFIt/cx59iqEDSgIGnQpCTRv96MQnXCYvJi7QhBC24uyuzbD8wVbajMB1b9x4I0octYFJ3OwjAwNqk1AjDA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/visitor-keys": "5.37.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.37.0.tgz", + "integrity": "sha512-jUEJoQrWbZhmikbcWSMDuUSxEE7ID2W/QCV/uz10WtQqfOuKZUqFGjqLJ+qhDd17rjgp+QJPqTdPIBWwoob2NQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.37.0", + "@typescript-eslint/types": "5.37.0", + "@typescript-eslint/typescript-estree": "5.37.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.37.0.tgz", + "integrity": "sha512-Hp7rT4cENBPIzMwrlehLW/28EVCOcE9U1Z1BQTc8EA8v5qpr7GRGuG+U58V5tTY48zvUOA3KHvw3rA8tY9fbdA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.37.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.0.3.tgz", + "integrity": "sha512-ApPyHSOhS/sVzwUOQIWJmdvDhBsMG01HX9z7ogtkp1TToHGGUWFlnXJUIzCgKPSfiYLn3ibipCYzsKSURHEwLg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.0.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.0.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.0.2.tgz", + "integrity": "sha512-eBr2ynAEFjcebVvu8Ktx580BD1QKCrBG1XwEUTXJe285p9HA/4hOhfWCFRQhTKSyBV0VzjhG7H91Eifz9s29hg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.0.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.0.2.tgz", + "integrity": "sha512-BeVXp7rH5TK96ofyEnHjznjLMQ2nAeDJ+QzxKnHAAMs0RgrQsCywjAN8m4mOm5Di0pxU//3AoEeJJrerMH5UeA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.0.2", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001409", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001409.tgz", + "integrity": "sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.4.0.tgz", + "integrity": "sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.0.0.tgz", + "integrity": "sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.256", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", + "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.2.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", + "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/espree": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "dev": true, + "dependencies": { + "acorn": "^8.6.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.0.3.tgz", + "integrity": "sha512-t8l5DTws3212VbmPL+tBFXhjRHLmctHB0oQbL8eUc6S7NzZtYUhycrFO9mkxA0ZUC6FAWdNi7JchJSkODtcu1Q==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.0.3", + "jest-get-type": "^29.0.0", + "jest-matcher-utils": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-util": "^29.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.0.3.tgz", + "integrity": "sha512-ElgUtJBLgXM1E8L6K1RW1T96R897YY/3lRYqq9uVcPWtP2AAl/nQ16IYDh/FzQOOQ12VEuLdcPU83mbhG2C3PQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.0.3", + "@jest/types": "^29.0.3", + "import-local": "^3.0.2", + "jest-cli": "^29.0.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.0.0.tgz", + "integrity": "sha512-28/iDMDrUpGoCitTURuDqUzWQoWmOmOKOFST1mi2lwh62X4BFf6khgH3uSuo1e49X/UDjuApAj3w0wLOex4VPQ==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.0.3.tgz", + "integrity": "sha512-QeGzagC6Hw5pP+df1+aoF8+FBSgkPmraC1UdkeunWh0jmrp7wC0Hr6umdUAOELBQmxtKAOMNC3KAdjmCds92Zg==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.0.3", + "@jest/expect": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.0.3", + "jest-matcher-utils": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-runtime": "^29.0.3", + "jest-snapshot": "^29.0.3", + "jest-util": "^29.0.3", + "p-limit": "^3.1.0", + "pretty-format": "^29.0.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.0.3.tgz", + "integrity": "sha512-aUy9Gd/Kut1z80eBzG10jAn6BgS3BoBbXyv+uXEqBJ8wnnuZ5RpNfARoskSrTIy1GY4a8f32YGuCMwibtkl9CQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/types": "^29.0.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.0.3", + "jest-util": "^29.0.3", + "jest-validate": "^29.0.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.0.3.tgz", + "integrity": "sha512-U5qkc82HHVYe3fNu2CRXLN4g761Na26rWKf7CjM8LlZB3In1jadEkZdMwsE37rd9RSPV0NfYaCjHdk/gu3v+Ew==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.0.3", + "@jest/types": "^29.0.3", + "babel-jest": "^29.0.3", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.0.3", + "jest-environment-node": "^29.0.3", + "jest-get-type": "^29.0.0", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.3", + "jest-runner": "^29.0.3", + "jest-util": "^29.0.3", + "jest-validate": "^29.0.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.0.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.0.3.tgz", + "integrity": "sha512-+X/AIF5G/vX9fWK+Db9bi9BQas7M9oBME7egU7psbn4jlszLFCu0dW63UgeE6cs/GANq4fLaT+8sGHQQ0eCUfg==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.0.0", + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.0.0.tgz", + "integrity": "sha512-s5Kpra/kLzbqu9dEjov30kj1n4tfu3e7Pl8v+f8jOkeWNqM6Ds8jRaJfZow3ducoQUrf2Z4rs2N5S3zXnb83gw==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.0.3.tgz", + "integrity": "sha512-wILhZfESURHHBNvPMJ0lZlYZrvOQJxAo3wNHi+ycr90V7M+uGR9Gh4+4a/BmaZF0XTyZsk4OiYEf3GJN7Ltqzg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.0.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.0.0", + "jest-util": "^29.0.3", + "pretty-format": "^29.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.0.3.tgz", + "integrity": "sha512-cdZqRCnmIlTXC+9vtvmfiY/40Cj6s2T0czXuq1whvQdmpzAnj4sbqVYuZ4zFHk766xTTJ+Ij3uUqkk8KCfXoyg==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.0.3", + "@jest/fake-timers": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "jest-mock": "^29.0.3", + "jest-util": "^29.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.0.0.tgz", + "integrity": "sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.3.tgz", + "integrity": "sha512-uMqR99+GuBHo0RjRhOE4iA6LmsxEwRdgiIAQgMU/wdT2XebsLDz5obIwLZm/Psj+GwSEQhw9AfAVKGYbh2G55A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "@jest/types": "^29.0.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.0.0", + "jest-util": "^29.0.3", + "jest-worker": "^29.0.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, "engines": { - "node": ">= 6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "node_modules/jest-leak-detector": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.0.3.tgz", + "integrity": "sha512-YfW/G63dAuiuQ3QmQlh8hnqLDe25WFY3eQhuc/Ev1AGmkw5zREblTh7TCSKLoheyggu6G9gxO2hY8p9o6xbaRQ==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/jest-matcher-utils": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.0.3.tgz", + "integrity": "sha512-RsR1+cZ6p1hDV4GSCQTg+9qjeotQCgkaleIKLK7dm+U4V/H2bWedU3RAtLm8+mANzZ7eDV33dMar4pejd7047w==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "chalk": "^4.0.0", + "jest-diff": "^29.0.3", + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "node_modules/jest-message-util": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.0.3.tgz", + "integrity": "sha512-7T8JiUTtDfppojosORAflABfLsLKMLkBHSWkjNQrjIltGoDzNGn7wEPOSfjqYAGTYME65esQzMJxGDjuLBKdOg==", "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.0.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.0.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, "engines": { - "node": ">= 4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-mock": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.0.3.tgz", + "integrity": "sha512-ort9pYowltbcrCVR43wdlqfAiFJXBx8l4uJDsD8U72LgBcetvEp+Qxj1W9ZYgMRoeAo+ov5cnAGF2B6+Oth+ww==", "dev": true, + "dependencies": { + "@jest/types": "^29.0.3", + "@types/node": "*" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", "dev": true, - "bin": { - "he": "bin/he" + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/jest-regex-util": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.0.0.tgz", + "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true, "engines": { - "node": ">= 4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/jest-resolve": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.0.3.tgz", + "integrity": "sha512-toVkia85Y/BPAjJasTC9zIPY6MmVXQPtrCk8SmiheC4MwVFE/CMFlOtMN6jrwPMC6TtNh8+sTMllasFeu1wMPg==", "dev": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.0.3", + "jest-validate": "^29.0.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "node_modules/jest-resolve-dependencies": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.0.3.tgz", + "integrity": "sha512-KzuBnXqNvbuCdoJpv8EanbIGObk7vUBNt/PwQPPx2aMhlv/jaXpUJsqWYRpP/0a50faMBY7WFFP8S3/CCzwfDw==", "dev": true, + "dependencies": { + "jest-regex-util": "^29.0.0", + "jest-snapshot": "^29.0.3" + }, "engines": { - "node": ">=0.8.19" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "node_modules/jest-runner": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.0.3.tgz", + "integrity": "sha512-Usu6VlTOZlCZoNuh3b2Tv/yzDpKqtiNAetG9t3kJuHfUyVMNW7ipCCJOUojzKkjPoaN7Bl1f7Buu6PE0sGpQxw==", "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "@jest/console": "^29.0.3", + "@jest/environment": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.0.0", + "jest-environment-node": "^29.0.3", + "jest-haste-map": "^29.0.3", + "jest-leak-detector": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-resolve": "^29.0.3", + "jest-runtime": "^29.0.3", + "jest-util": "^29.0.3", + "jest-watcher": "^29.0.3", + "jest-worker": "^29.0.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/jest-runtime": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.0.3.tgz", + "integrity": "sha512-12gZXRQ7ozEeEHKTY45a+YLqzNDR/x4c//X6AqwKwKJPpWM8FY4vwn4VQJOcLRS3Nd1fWwgP7LU4SoynhuUMHQ==", "dev": true, "dependencies": { - "binary-extensions": "^2.0.0" + "@jest/environment": "^29.0.3", + "@jest/fake-timers": "^29.0.3", + "@jest/globals": "^29.0.3", + "@jest/source-map": "^29.0.0", + "@jest/test-result": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-mock": "^29.0.3", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.3", + "jest-snapshot": "^29.0.3", + "jest-util": "^29.0.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "node_modules/jest-snapshot": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.0.3.tgz", + "integrity": "sha512-52q6JChm04U3deq+mkQ7R/7uy7YyfVIrebMi6ZkBoDJ85yEjm/sJwdr1P0LOIEHmpyLlXrxy3QP0Zf5J2kj0ew==", "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.0.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.0.3", + "jest-get-type": "^29.0.0", + "jest-haste-map": "^29.0.3", + "jest-matcher-utils": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-util": "^29.0.3", + "natural-compare": "^1.4.0", + "pretty-format": "^29.0.3", + "semver": "^7.3.5" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/jest-util": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.3.tgz", + "integrity": "sha512-Q0xaG3YRG8QiTC4R6fHjHQPaPpz9pJBEi0AeOE4mQh/FuWOijFjGXMMOfQEaU9i3z76cNR7FobZZUQnL6IyfdQ==", "dev": true, + "dependencies": { + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/jest-validate": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.0.3.tgz", + "integrity": "sha512-OebiqqT6lK8cbMPtrSoS3aZP4juID762lZvpf1u+smZnwTEBCBInan0GAIIhv36MxGaJvmq5uJm7dl5gVt+Zrw==", "dev": true, "dependencies": { - "is-extglob": "^2.1.1" + "@jest/types": "^29.0.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.0.0", + "leven": "^3.1.0", + "pretty-format": "^29.0.3" }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": ">=0.12.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "node_modules/jest-watcher": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.0.3.tgz", + "integrity": "sha512-tQX9lU91A+9tyUQKUMp0Ns8xAcdhC9fo73eqA3LFxP2bSgiF49TNcc+vf3qgGYYK9qRjFpXW9+4RgF/mbxyOOw==", "dev": true, + "dependencies": { + "@jest/test-result": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^29.0.3", + "string-length": "^4.0.1" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/jest-worker": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.3.tgz", + "integrity": "sha512-Tl/YWUugQOjoTYwjKdfJWkSOfhufJHO5LhXTSZC3TRoQKO+fuXnZAdoXXBlpLXKGODBL3OvdUasfDD4PcMe6ng==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "node_modules/js-yaml": { @@ -1257,6 +3372,24 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1269,6 +3402,36 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1282,55 +3445,93 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "yallist": "^4.0.0" }, "engines": { "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "tmpl": "1.0.5" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1353,96 +3554,25 @@ "node": ">=8.6" } }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "*" } }, "node_modules/natural-compare": { @@ -1451,6 +3581,18 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1460,6 +3602,18 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1469,6 +3623,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -1502,20 +3671,41 @@ } }, "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=10" + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1528,6 +3718,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1555,6 +3763,12 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -1564,6 +3778,12 @@ "node": ">=8" } }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1576,6 +3796,27 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1585,6 +3826,32 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.0.3.tgz", + "integrity": "sha512-cHudsvQr1K5vNVLbvYF/nv3Qy/F/BcEKxGuIeMiVMRHxPOO1RxXooP8g/ZrwAp7Dx+KdMZoOc7NxLHhMrP2f9Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -1594,6 +3861,19 @@ "node": ">=0.4.0" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -1623,26 +3903,11 @@ } ] }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true }, "node_modules/regexpp": { "version": "3.2.0", @@ -1659,12 +3924,50 @@ "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1674,6 +3977,15 @@ "node": ">=4" } }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -1723,24 +4035,10 @@ } }, "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/semver": { "version": "7.3.7", @@ -1757,15 +4055,6 @@ "node": ">=10" } }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1787,6 +4076,18 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -1796,6 +4097,65 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1822,6 +4182,24 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1835,18 +4213,70 @@ } }, "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, "node_modules/text-table": { @@ -1855,6 +4285,21 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1867,6 +4312,49 @@ "node": ">=8.0" } }, + "node_modules/ts-jest": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.1.tgz", + "integrity": "sha512-htQOHshgvhn93QLxrmxpiQPk69+M1g7govO1g6kf6GsjCv4uvRV0znVmDrrvjUrVCnTYeY4FBxTYYYD4airyJA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.1", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -1900,6 +4388,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -1925,6 +4422,32 @@ "node": ">=4.2.0" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1940,6 +4463,29 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1964,12 +4510,6 @@ "node": ">=0.10.0" } }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -1987,83 +4527,539 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.1.tgz", + "integrity": "sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==", + "dev": true + }, + "@babel/core": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.1.tgz", + "integrity": "sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.0", + "@babel/helper-compilation-targets": "^7.19.1", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helpers": "^7.19.0", + "@babel/parser": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", + "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", + "dev": true, + "requires": { + "@babel/types": "^7.19.0", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz", + "integrity": "sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.19.1", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.1.tgz", + "integrity": "sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "engines": { - "node": ">=10" + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "engines": { - "node": ">=10" + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "@babel/plugin-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, - "engines": { - "node": ">=10" + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.1.tgz", + "integrity": "sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.19.1", + "@babel/types": "^7.19.0", + "debug": "^4.1.0", + "globals": "^11.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } } - } - }, - "dependencies": { + }, + "@babel/types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "@eslint/eslintrc": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", @@ -2098,6 +5094,311 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.0.3.tgz", + "integrity": "sha512-cGg0r+klVHSYnfE977S9wmpuQ9L+iYuYgL+5bPXiUlUynLLYunRxswEmhBzvrSKGof5AKiHuTTmUKAqRcDY9dg==", + "dev": true, + "requires": { + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.0.3", + "jest-util": "^29.0.3", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.0.3.tgz", + "integrity": "sha512-1d0hLbOrM1qQE3eP3DtakeMbKTcXiXP3afWxqz103xPyddS2NhnNghS7MaXx1dcDt4/6p4nlhmeILo2ofgi8cQ==", + "dev": true, + "requires": { + "@jest/console": "^29.0.3", + "@jest/reporters": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.0.0", + "jest-config": "^29.0.3", + "jest-haste-map": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.3", + "jest-resolve-dependencies": "^29.0.3", + "jest-runner": "^29.0.3", + "jest-runtime": "^29.0.3", + "jest-snapshot": "^29.0.3", + "jest-util": "^29.0.3", + "jest-validate": "^29.0.3", + "jest-watcher": "^29.0.3", + "micromatch": "^4.0.4", + "pretty-format": "^29.0.3", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.0.3.tgz", + "integrity": "sha512-iKl272NKxYNQNqXMQandAIwjhQaGw5uJfGXduu8dS9llHi8jV2ChWrtOAVPnMbaaoDhnI3wgUGNDvZgHeEJQCA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "jest-mock": "^29.0.3" + } + }, + "@jest/expect": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.0.3.tgz", + "integrity": "sha512-6W7K+fsI23FQ01H/BWccPyDZFrnU9QlzDcKOjrNVU5L8yUORFAJJIpmyxWPW70+X624KUNqzZwPThPMX28aXEQ==", + "dev": true, + "requires": { + "expect": "^29.0.3", + "jest-snapshot": "^29.0.3" + } + }, + "@jest/expect-utils": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.0.3.tgz", + "integrity": "sha512-i1xUkau7K/63MpdwiRqaxgZOjxYs4f0WMTGJnYwUKubsNRZSeQbLorS7+I4uXVF9KQ5r61BUPAUMZ7Lf66l64Q==", + "dev": true, + "requires": { + "jest-get-type": "^29.0.0" + } + }, + "@jest/fake-timers": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.0.3.tgz", + "integrity": "sha512-tmbUIo03x0TdtcZCESQ0oQSakPCpo7+s6+9mU19dd71MptkP4zCwoeZqna23//pgbhtT1Wq02VmA9Z9cNtvtCQ==", + "dev": true, + "requires": { + "@jest/types": "^29.0.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^29.0.3", + "jest-mock": "^29.0.3", + "jest-util": "^29.0.3" + } + }, + "@jest/globals": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.0.3.tgz", + "integrity": "sha512-YqGHT65rFY2siPIHHFjuCGUsbzRjdqkwbat+Of6DmYRg5shIXXrLdZoVE/+TJ9O1dsKsFmYhU58JvIbZRU1Z9w==", + "dev": true, + "requires": { + "@jest/environment": "^29.0.3", + "@jest/expect": "^29.0.3", + "@jest/types": "^29.0.3", + "jest-mock": "^29.0.3" + } + }, + "@jest/reporters": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.0.3.tgz", + "integrity": "sha512-3+QU3d4aiyOWfmk1obDerie4XNCaD5Xo1IlKNde2yGEi02WQD+ZQD0i5Hgqm1e73sMV7kw6pMlCnprtEwEVwxw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.0.3", + "jest-util": "^29.0.3", + "jest-worker": "^29.0.3", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" + } + }, + "@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.0.0.tgz", + "integrity": "sha512-nOr+0EM8GiHf34mq2GcJyz/gYFyLQ2INDhAylrZJ9mMWoW21mLBfZa0BUVPPMxVYrLjeiRe2Z7kWXOGnS0TFhQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.0.3.tgz", + "integrity": "sha512-vViVnQjCgTmbhDKEonKJPtcFe9G/CJO4/Np4XwYJah+lF2oI7KKeRp8t1dFvv44wN2NdbDb/qC6pi++Vpp0Dlg==", + "dev": true, + "requires": { + "@jest/console": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.0.3.tgz", + "integrity": "sha512-Hf4+xYSWZdxTNnhDykr8JBs0yBN/nxOXyUQWfotBUqqy0LF9vzcFB0jm/EDNZCx587znLWTIgxcokW7WeZMobQ==", + "dev": true, + "requires": { + "@jest/test-result": "^29.0.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.3", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.3.tgz", + "integrity": "sha512-C5ihFTRYaGDbi/xbRQRdbo5ddGtI4VSpmL6AIcZxdhwLbXMa7PcXxxqyI91vGOFHnn5aVM3WYnYKCHEqmLVGzg==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.0.3", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.3", + "jest-regex-util": "^29.0.0", + "jest-util": "^29.0.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + } + }, + "@jest/types": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.3.tgz", + "integrity": "sha512-coBJmOQvurXjN1Hh5PzF7cmsod0zLIOXpP8KD161mqNlroMhLcwpODiEzi7ZsRl5Z/AIuxpeNm8DCl43F4kz8A==", + "dev": true, + "requires": { + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2124,6 +5425,114 @@ "fastq": "^1.6.0" } }, + "@sinclair/typebox": { + "version": "0.24.42", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.42.tgz", + "integrity": "sha512-d+2AtrHGyWek2u2ITF0lHRIv6Tt7X0dEHW+0rP+5aDCEjC3fiN2RBjrLD0yU0at52BcZbRGxLbAtXiR0hFCjYw==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.1.tgz", + "integrity": "sha512-FSdLaZh2UxaMuLp9lixWaHq/golWTRWOnRsAXzDTDSDOQLuZb1nsdCt6pJSPWSEQt2eFZ2YVk3oYhn+1kLMeMA==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.0.3.tgz", + "integrity": "sha512-F6ukyCTwbfsEX5F2YmVYmM5TcTHy1q9P5rWlRbrk56KyMh3v9xRGUO3aa8+SkvMi0SHXtASJv1283enXimC0Og==", + "dev": true, + "requires": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -2136,6 +5545,33 @@ "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", "dev": true }, + "@types/prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", + "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/yargs": { + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz", + "integrity": "sha512-Nz4MPhecOFArtm81gFQvQqdV7XYCrWKx5uUt6GNHredFHn1i2mtWqXTON7EPXMtNi1qjtjEM/VCHDhcHsAMLXQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.37.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.37.0.tgz", @@ -2258,12 +5694,6 @@ "eslint-visitor-keys": "^3.3.0" } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "acorn": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", @@ -2295,6 +5725,23 @@ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2332,18 +5779,82 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "babel-jest": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.0.3.tgz", + "integrity": "sha512-ApPyHSOhS/sVzwUOQIWJmdvDhBsMG01HX9z7ogtkp1TToHGGUWFlnXJUIzCgKPSfiYLn3ibipCYzsKSURHEwLg==", + "dev": true, + "requires": { + "@jest/transform": "^29.0.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.0.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.0.2.tgz", + "integrity": "sha512-eBr2ynAEFjcebVvu8Ktx580BD1QKCrBG1XwEUTXJe285p9HA/4hOhfWCFRQhTKSyBV0VzjhG7H91Eifz9s29hg==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.0.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.0.2.tgz", + "integrity": "sha512-BeVXp7rH5TK96ofyEnHjznjLMQ2nAeDJ+QzxKnHAAMs0RgrQsCywjAN8m4mOm5Di0pxU//3AoEeJJrerMH5UeA==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.0.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2363,10 +5874,40 @@ "fill-range": "^7.0.1" } }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "callsites": { @@ -2376,9 +5917,15 @@ "dev": true }, "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001409", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001409.tgz", + "integrity": "sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==", "dev": true }, "chalk": { @@ -2389,34 +5936,25 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "ci-info": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.4.0.tgz", + "integrity": "sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true }, "cliui": { "version": "7.0.4", @@ -2429,6 +5967,18 @@ "wrap-ansi": "^7.0.0" } }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2450,6 +6000,15 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2478,10 +6037,10 @@ } } }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, "deep-is": { @@ -2490,10 +6049,22 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "diff-sequences": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.0.0.tgz", + "integrity": "sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==", "dev": true }, "dir-glob": { @@ -2514,6 +6085,18 @@ "esutils": "^2.0.2" } }, + "electron-to-chromium": { + "version": "1.4.256", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", + "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==", + "dev": true + }, + "emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2529,6 +6112,15 @@ "ansi-colors": "^4.1.1" } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2642,6 +6234,12 @@ "eslint-visitor-keys": "^3.1.0" } }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", @@ -2672,6 +6270,42 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expect": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.0.3.tgz", + "integrity": "sha512-t8l5DTws3212VbmPL+tBFXhjRHLmctHB0oQbL8eUc6S7NzZtYUhycrFO9mkxA0ZUC6FAWdNi7JchJSkODtcu1Q==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.0.3", + "jest-get-type": "^29.0.0", + "jest-matcher-utils": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-util": "^29.0.3" + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2712,6 +6346,15 @@ "reusify": "^1.0.4" } }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2731,21 +6374,15 @@ } }, "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^6.0.0", + "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -2775,18 +6412,42 @@ "dev": true, "optional": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -2841,16 +6502,37 @@ } } }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, "ignore": { @@ -2869,6 +6551,16 @@ "resolve-from": "^4.0.0" } }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2891,13 +6583,19 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "dev": true, "requires": { - "binary-extensions": "^2.0.0" + "has": "^1.0.3" } }, "is-extglob": { @@ -2912,6 +6610,12 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2927,22 +6631,508 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.0.3.tgz", + "integrity": "sha512-ElgUtJBLgXM1E8L6K1RW1T96R897YY/3lRYqq9uVcPWtP2AAl/nQ16IYDh/FzQOOQ12VEuLdcPU83mbhG2C3PQ==", + "dev": true, + "requires": { + "@jest/core": "^29.0.3", + "@jest/types": "^29.0.3", + "import-local": "^3.0.2", + "jest-cli": "^29.0.3" + } + }, + "jest-changed-files": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.0.0.tgz", + "integrity": "sha512-28/iDMDrUpGoCitTURuDqUzWQoWmOmOKOFST1mi2lwh62X4BFf6khgH3uSuo1e49X/UDjuApAj3w0wLOex4VPQ==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.0.3.tgz", + "integrity": "sha512-QeGzagC6Hw5pP+df1+aoF8+FBSgkPmraC1UdkeunWh0jmrp7wC0Hr6umdUAOELBQmxtKAOMNC3KAdjmCds92Zg==", + "dev": true, + "requires": { + "@jest/environment": "^29.0.3", + "@jest/expect": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.0.3", + "jest-matcher-utils": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-runtime": "^29.0.3", + "jest-snapshot": "^29.0.3", + "jest-util": "^29.0.3", + "p-limit": "^3.1.0", + "pretty-format": "^29.0.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-cli": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.0.3.tgz", + "integrity": "sha512-aUy9Gd/Kut1z80eBzG10jAn6BgS3BoBbXyv+uXEqBJ8wnnuZ5RpNfARoskSrTIy1GY4a8f32YGuCMwibtkl9CQ==", + "dev": true, + "requires": { + "@jest/core": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/types": "^29.0.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.0.3", + "jest-util": "^29.0.3", + "jest-validate": "^29.0.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + } + }, + "jest-config": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.0.3.tgz", + "integrity": "sha512-U5qkc82HHVYe3fNu2CRXLN4g761Na26rWKf7CjM8LlZB3In1jadEkZdMwsE37rd9RSPV0NfYaCjHdk/gu3v+Ew==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.0.3", + "@jest/types": "^29.0.3", + "babel-jest": "^29.0.3", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.0.3", + "jest-environment-node": "^29.0.3", + "jest-get-type": "^29.0.0", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.3", + "jest-runner": "^29.0.3", + "jest-util": "^29.0.3", + "jest-validate": "^29.0.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.0.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.0.3.tgz", + "integrity": "sha512-+X/AIF5G/vX9fWK+Db9bi9BQas7M9oBME7egU7psbn4jlszLFCu0dW63UgeE6cs/GANq4fLaT+8sGHQQ0eCUfg==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.0.0", + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.3" + } + }, + "jest-docblock": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.0.0.tgz", + "integrity": "sha512-s5Kpra/kLzbqu9dEjov30kj1n4tfu3e7Pl8v+f8jOkeWNqM6Ds8jRaJfZow3ducoQUrf2Z4rs2N5S3zXnb83gw==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.0.3.tgz", + "integrity": "sha512-wILhZfESURHHBNvPMJ0lZlYZrvOQJxAo3wNHi+ycr90V7M+uGR9Gh4+4a/BmaZF0XTyZsk4OiYEf3GJN7Ltqzg==", + "dev": true, + "requires": { + "@jest/types": "^29.0.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.0.0", + "jest-util": "^29.0.3", + "pretty-format": "^29.0.3" + } + }, + "jest-environment-node": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.0.3.tgz", + "integrity": "sha512-cdZqRCnmIlTXC+9vtvmfiY/40Cj6s2T0czXuq1whvQdmpzAnj4sbqVYuZ4zFHk766xTTJ+Ij3uUqkk8KCfXoyg==", + "dev": true, + "requires": { + "@jest/environment": "^29.0.3", + "@jest/fake-timers": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "jest-mock": "^29.0.3", + "jest-util": "^29.0.3" + } + }, + "jest-get-type": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.0.0.tgz", + "integrity": "sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==", + "dev": true + }, + "jest-haste-map": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.3.tgz", + "integrity": "sha512-uMqR99+GuBHo0RjRhOE4iA6LmsxEwRdgiIAQgMU/wdT2XebsLDz5obIwLZm/Psj+GwSEQhw9AfAVKGYbh2G55A==", + "dev": true, + "requires": { + "@jest/types": "^29.0.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.0.0", + "jest-util": "^29.0.3", + "jest-worker": "^29.0.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.0.3.tgz", + "integrity": "sha512-YfW/G63dAuiuQ3QmQlh8hnqLDe25WFY3eQhuc/Ev1AGmkw5zREblTh7TCSKLoheyggu6G9gxO2hY8p9o6xbaRQ==", + "dev": true, + "requires": { + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.3" + } + }, + "jest-matcher-utils": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.0.3.tgz", + "integrity": "sha512-RsR1+cZ6p1hDV4GSCQTg+9qjeotQCgkaleIKLK7dm+U4V/H2bWedU3RAtLm8+mANzZ7eDV33dMar4pejd7047w==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.0.3", + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.3" + } + }, + "jest-message-util": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.0.3.tgz", + "integrity": "sha512-7T8JiUTtDfppojosORAflABfLsLKMLkBHSWkjNQrjIltGoDzNGn7wEPOSfjqYAGTYME65esQzMJxGDjuLBKdOg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.0.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.0.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.0.3.tgz", + "integrity": "sha512-ort9pYowltbcrCVR43wdlqfAiFJXBx8l4uJDsD8U72LgBcetvEp+Qxj1W9ZYgMRoeAo+ov5cnAGF2B6+Oth+ww==", + "dev": true, + "requires": { + "@jest/types": "^29.0.3", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.0.0.tgz", + "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "jest-resolve": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.0.3.tgz", + "integrity": "sha512-toVkia85Y/BPAjJasTC9zIPY6MmVXQPtrCk8SmiheC4MwVFE/CMFlOtMN6jrwPMC6TtNh8+sTMllasFeu1wMPg==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.0.3", + "jest-validate": "^29.0.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.0.3.tgz", + "integrity": "sha512-KzuBnXqNvbuCdoJpv8EanbIGObk7vUBNt/PwQPPx2aMhlv/jaXpUJsqWYRpP/0a50faMBY7WFFP8S3/CCzwfDw==", + "dev": true, + "requires": { + "jest-regex-util": "^29.0.0", + "jest-snapshot": "^29.0.3" + } + }, + "jest-runner": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.0.3.tgz", + "integrity": "sha512-Usu6VlTOZlCZoNuh3b2Tv/yzDpKqtiNAetG9t3kJuHfUyVMNW7ipCCJOUojzKkjPoaN7Bl1f7Buu6PE0sGpQxw==", + "dev": true, + "requires": { + "@jest/console": "^29.0.3", + "@jest/environment": "^29.0.3", + "@jest/test-result": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.0.0", + "jest-environment-node": "^29.0.3", + "jest-haste-map": "^29.0.3", + "jest-leak-detector": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-resolve": "^29.0.3", + "jest-runtime": "^29.0.3", + "jest-util": "^29.0.3", + "jest-watcher": "^29.0.3", + "jest-worker": "^29.0.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + } + }, + "jest-runtime": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.0.3.tgz", + "integrity": "sha512-12gZXRQ7ozEeEHKTY45a+YLqzNDR/x4c//X6AqwKwKJPpWM8FY4vwn4VQJOcLRS3Nd1fWwgP7LU4SoynhuUMHQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.0.3", + "@jest/fake-timers": "^29.0.3", + "@jest/globals": "^29.0.3", + "@jest/source-map": "^29.0.0", + "@jest/test-result": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-mock": "^29.0.3", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.3", + "jest-snapshot": "^29.0.3", + "jest-util": "^29.0.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.0.3.tgz", + "integrity": "sha512-52q6JChm04U3deq+mkQ7R/7uy7YyfVIrebMi6ZkBoDJ85yEjm/sJwdr1P0LOIEHmpyLlXrxy3QP0Zf5J2kj0ew==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.0.3", + "@jest/transform": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.0.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.0.3", + "jest-get-type": "^29.0.0", + "jest-haste-map": "^29.0.3", + "jest-matcher-utils": "^29.0.3", + "jest-message-util": "^29.0.3", + "jest-util": "^29.0.3", + "natural-compare": "^1.4.0", + "pretty-format": "^29.0.3", + "semver": "^7.3.5" + } + }, + "jest-util": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.3.tgz", + "integrity": "sha512-Q0xaG3YRG8QiTC4R6fHjHQPaPpz9pJBEi0AeOE4mQh/FuWOijFjGXMMOfQEaU9i3z76cNR7FobZZUQnL6IyfdQ==", + "dev": true, + "requires": { + "@jest/types": "^29.0.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.0.3.tgz", + "integrity": "sha512-OebiqqT6lK8cbMPtrSoS3aZP4juID762lZvpf1u+smZnwTEBCBInan0GAIIhv36MxGaJvmq5uJm7dl5gVt+Zrw==", + "dev": true, + "requires": { + "@jest/types": "^29.0.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.0.0", + "leven": "^3.1.0", + "pretty-format": "^29.0.3" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.0.3.tgz", + "integrity": "sha512-tQX9lU91A+9tyUQKUMp0Ns8xAcdhC9fo73eqA3LFxP2bSgiF49TNcc+vf3qgGYYK9qRjFpXW9+4RgF/mbxyOOw==", + "dev": true, + "requires": { + "@jest/test-result": "^29.0.3", + "@jest/types": "^29.0.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^29.0.3", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.3.tgz", + "integrity": "sha512-Tl/YWUugQOjoTYwjKdfJWkSOfhufJHO5LhXTSZC3TRoQKO+fuXnZAdoXXBlpLXKGODBL3OvdUasfDD4PcMe6ng==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { @@ -2954,6 +7144,18 @@ "argparse": "^2.0.1" } }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2966,6 +7168,24 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2976,31 +7196,33 @@ "type-check": "~0.4.0" } }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" } }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3010,6 +7232,44 @@ "yallist": "^4.0.0" } }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3026,6 +7286,12 @@ "picomatch": "^2.3.1" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -3035,80 +7301,39 @@ "brace-expansion": "^1.1.7" } }, - "mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3118,6 +7343,15 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -3142,14 +7376,31 @@ } }, "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" + }, + "dependencies": { + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + } } }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3159,6 +7410,18 @@ "callsites": "^3.0.0" } }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3177,30 +7440,86 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "pretty-format": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.0.3.tgz", + "integrity": "sha512-cHudsvQr1K5vNVLbvYF/nv3Qy/F/BcEKxGuIeMiVMRHxPOO1RxXooP8g/ZrwAp7Dx+KdMZoOc7NxLHhMrP2f9Q==", + "dev": true, + "requires": { + "@jest/schemas": "^29.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -3213,23 +7532,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true }, "regexpp": { "version": "3.2.0", @@ -3240,15 +7547,49 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3274,9 +7615,9 @@ } }, "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "semver": { @@ -3288,15 +7629,6 @@ "lru-cache": "^6.0.0" } }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3312,12 +7644,73 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -3338,6 +7731,18 @@ "ansi-regex": "^5.0.1" } }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3345,20 +7750,69 @@ "dev": true }, "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" } }, + "supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3368,6 +7822,22 @@ "is-number": "^7.0.0" } }, + "ts-jest": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.1.tgz", + "integrity": "sha512-htQOHshgvhn93QLxrmxpiQPk69+M1g7govO1g6kf6GsjCv4uvRV0znVmDrrvjUrVCnTYeY4FBxTYYYD4airyJA==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.1", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -3392,6 +7862,12 @@ "prelude-ls": "^1.2.1" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -3404,6 +7880,16 @@ "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", "dev": true }, + "update-browserslist-db": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3419,6 +7905,26 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3434,12 +7940,6 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -3457,6 +7957,16 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -3470,38 +7980,26 @@ "dev": true }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.0.0" } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 05b51de..245f52d 100644 --- a/package.json +++ b/package.json @@ -7,15 +7,18 @@ "main": "./lib/index.js", "scripts": { "lint": "eslint .", + "test": "jest", "build": "tsc --build", - "prepack": "tsc --build" + "prepack": "eslint && mocha && tsc && echo Expressionish packed successfully" }, "devDependencies": { + "@types/jest": "^29.0.3", "@types/node": "^18.7.18", "@typescript-eslint/eslint-plugin": "^5.37.0", "@typescript-eslint/parser": "^5.37.0", "eslint": "^8.4.1", - "mocha": "^10.0.0", + "jest": "^29.0.3", + "ts-jest": "^29.0.1", "typescript": "^4.8.3" }, "files": [ diff --git a/test/evaluate.js b/test/evaluate.js deleted file mode 100644 index 0f199e5..0000000 --- a/test/evaluate.js +++ /dev/null @@ -1,351 +0,0 @@ -/* global describe, it */ - -const assert = require('assert'); - -const errors = require('../src/errors.js'); - -const evaluate = require('../src/evaluate.js'); - -const expectThrow = async (fnc, type) => { - try { - await fnc(); - } catch (err) { - if (type && !(err instanceof type)) { - console.log(err); - throw new Error(`threw incorrect error: ${err.name}`); - } - return; - } - throw new Error('did not throw an error'); -}; - -const expectEqual = async (fnc, value) => { - let result = await fnc(); - assert.equal(result, value); -}; - -const vars = new Map([ - ['txt', {handle: 'txt', argsCheck: () => {}, evaluator: () => 'evaled_var_text'}], - ['ten', {handle: 'ten', argsCheck: () => {}, evaluator: () => 10}], - ['sum', {handle: 'sum', argsCheck: () => {}, evaluator: (meta, ...args) => { - return args.map(item => Number(item)).reduce((acc, cur) => acc + cur, 0); - }}], - ['inout', {handle: 'inout', argsCheck: () => {}, evaluator: (meta, ...args) => { - return args.join(''); - }}] -]); -const options = { - handlers: vars, - trigger: '' -}; - -describe('evaluate()', function () { - - describe('Throws an error when arguments\'', function () { - it('option is undefined or null', async function () { - await expectThrow(() => evaluate(), TypeError); - }); - it('variable-handlers is undefined', async function () { - await expectThrow(() => evaluate({}), TypeError); - }); - it('variable-handlers is not a Map', async function () { - await expectThrow(() => evaluate({handlers: ''}), TypeError); - }); - it('options.trigger is undefined', async function () { - await expectThrow(() => evaluate({handlers: vars}), TypeError); - }); - it('options.trigger is null', async function () { - await expectThrow(() => evaluate({handlers: vars, trigger: null}), TypeError); - }); - it('options.expression is undefined', async function () { - await expectThrow(() => evaluate({handlers: vars, trigger: ''}), TypeError); - }); - it('options.expression is not a string', async function () { - await expectThrow(() => evaluate({handlers: vars, trigger: '', expression: true}), TypeError); - }); - }); - - describe('Input is empty string', function () { - it('does not throw an error', async function () { - await evaluate({...options, expression: ''}); - }); - it('returns a string', async function () { - let result = await evaluate({...options, expression: ''}); - assert.equal(typeof(result), 'string'); - }); - it('string is empty', async function () { - await expectEqual(() => evaluate({...options, expression: ''}), ''); - }); - }); - - describe('Input is plain text', function () { - it('does not throw an error', async function () { - await evaluate({...options, expression: 'plain text'}); - }); - it('returns a string', async function () { - let result = await evaluate({...options, expression: 'plain text'}); - assert.equal(typeof(result), 'string'); - }); - it('string matches input', async function () { - await expectEqual(() => evaluate({...options, expression: 'plain text'}) , 'plain text'); - }); - it('treats root-level quotes as literals', async function () { - await expectEqual(() => evaluate({...options, expression: '"text"'}), '"text"'); - }); - it('treats root-level double backticks as literals', async function () { - await expectEqual(() => evaluate({...options, expression: '``text``'}), '``text``'); - }); - }); - - describe('Input is escape sequences', function () { - it('Escapes \\\\', async function () { - await expectEqual(() => evaluate({...options, expression: '\\\\'}), '\\'); - }); - it('Escapes \\$', async function () { - await expectEqual(() => evaluate({...options, expression: '\\$'}), '$'); - }); - it('Escapes \\"', async function () { - await expectEqual(() => evaluate({...options, expression: '\\"'}), '"'); - }); - it('Treats \\ at the end of an expression as a literal', async function () { - await expectEqual(() => evaluate({...options, expression: '\\'}), '\\'); - }); - }); - - describe('Input is a mix of text', async function () { - it('Plain-text and escape sequences', async function () { - await expectEqual(() => evaluate({...options, expression: 'text\\\\'}), 'text\\'); - await expectEqual(() => evaluate({...options, expression: '\\\\text'}), '\\text'); - await expectEqual(() => evaluate({...options, expression: 'text\\\\text'}), 'text\\text'); - await expectEqual(() => evaluate({...options, expression: '\\\\text\\\\'}), '\\text\\'); - }); - it('Treats non-escape-sequences as literal \\', async function () { - await expectEqual(() => evaluate({...options, expression: '\\a'}), '\\a'); - }); - }); - - describe('Input is variable', function () { - it('Treats naked $ as text', async function () { - await expectEqual(() => evaluate({...options, expression: '$'}), '$'); - }); - it('Treats $ as text', async function () { - await expectEqual(() => evaluate({...options, expression: '$10'}), '$10'); - }); - it('Throws an error if the variable does not exist', async function () { - await expectThrow(() => evaluate({...options, expression: '$notdefined'}), errors.ExpressionVariableError); - }); - it('Evaluates variable', async function () { - await expectEqual(() => evaluate({...options, expression: '$txt'}), 'evaled_var_text'); - }); - it('Evaluates variable with arguments', async function () { - await expectEqual(() => evaluate({...options, expression: '$sum[1,2]'}), '3'); - }); - it('Evaluates nested variables', async function () { - await expectEqual(() => evaluate({...options, expression: '$sum[$ten, 1]'}), '11'); - }); - it('Evaluates nested variable with arguments', async function () { - await expectEqual(() => evaluate({...options, expression: '$sum[$sum[$ten, 1], 1]'}), '12'); - }); - it('Evaluates quoted text in arguments to plain text', async function () { - await expectEqual(() => evaluate({...options, expression: '$inout["text"]'}), 'text'); - }); - it('Block-escapes text in double backticks', async function () { - await expectEqual(() => evaluate({...options, expression: '$inout[``"text"``]'}), '"text"'); - }); - it('Evaluates vars in block escapes', async function () { - await expectEqual(() => evaluate({...options, expression: '$inout[``"$ten"``]'}), '"10"'); - }); - }); - - describe('Input is $if', function () { - it('throws an error if no arguments', async function () { - await expectThrow(() => evaluate({...options, expression: '$if'}), errors.ExpressionSyntaxError); - }); - it('Does not throw an error for valid statement', async function () { - await evaluate({...options, expression: '$if[1 === 1, true, false]'}); - }); - }); - - describe('Input is $if with comparison operator', function () { - it('Properly evaluates ===', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[1 === 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1 === 2, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1.0 === 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a === a, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a === b, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a === A, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[ === , yes, no]'}), 'yes'); - }); - it('Properly evaluates !==', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[1 !== 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1 !== 2, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1.0 !== 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a !== a, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a !== b, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a !== A, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[ !== , yes, no]'}), 'no'); - }); - it('Properly evaluates ==', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[1 == 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1.0 == 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a == a, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a == A, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1 == 2, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a == b, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[ == , yes, no]'}), 'yes'); - }); - it('Properly evaluates !=', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[1 != 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1.0 != 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a != a, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a != A, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1 != 2, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a != b, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[ != , yes, no]'}), 'no'); - }); - it('Properly evaluates <', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[1 < 2, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1.0 < 2, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1 < 2.0, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1 < 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a < 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1 < a, yes, no]'}), 'no'); - }); - it('Properly evaluates <=', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[1 <= 2, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1.0 <= 2, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1 <= 2.0, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1 <= 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a < 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1 < a, yes, no]'}), 'no'); - }); - it('Properly evaluates >', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[2 > 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[2.0 > 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[2 > 1.0, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1 > 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a > 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1 > a, yes, no]'}), 'no'); - }); - it('Properly evaluates >=', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[2 >= 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[2.0 >= 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[2 >= 1.0, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1 >= 1, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a > 1, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1 > a, yes, no]'}), 'no'); - }); - it('Properly evaluates exists', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[a exists, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[ exists, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if["" exists, yes, no]'}), 'no'); - }); - it('Properly evaluates !exists', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[a !exists, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[ !exists, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if["" !exists, yes, no]'}), 'yes'); - }); - it('Properly evaluates isnumber', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[1 isnumber, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[.1 isnumber, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1.1 isnumber, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[-1 isnumber, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[-.1 isnumber, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[-1.1 isnumber, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a isnumber, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if["" isnumber, yes, no]'}), 'no'); - }); - it('Properly evaluates !isnumber', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[1 !isnumber, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[.1 !isnumber, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1.1 !isnumber, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[-1 !isnumber, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[-.1 !isnumber, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[-1.1 !isnumber, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a !isnumber, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if["" !isnumber, yes, no]'}), 'yes'); - }); - it('Properly evaluates isnumber range', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[0 isnumber 1-3, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1 isnumber 1-3, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[2 isnumber 1-3, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[3 isnumber 1-3, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[4 isnumber 1-3, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[1.5 isnumber 1-2, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a isnumber 1-2, yes, no]'}), 'no'); - }); - it('Properly evaluates !isnumber range', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[0 !isnumber 1-3, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1 !isnumber 1-3, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[2 !isnumber 1-3, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[3 !isnumber 1-3, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[4 !isnumber 1-3, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[1.5 !isnumber 1-2, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a !isnumber 1-2, yes, no]'}), 'yes'); - }); - it('Properly evaluates regex', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[a regex /a/, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[A regex /a/i, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a regex /b/, yes, no]'}), 'no'); - }); - it('Properly evaluates !regex', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[a !regex /a/, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[A !regex /a/i, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a !regex /b/, yes, no]'}), 'yes'); - }); - it('Properly evaluates iswcm', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[a iswcm a, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[z iswcm a, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[ab iswcm a?, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[bc iswcm a?, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[abc iswcm a?c, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[ac iswcm a?c, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[a iswcm a*, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[ab iswcm a*, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[ab iswcm *b*, yes, no]'}), 'yes'); - }); - it('Properly evaluates !iswcm', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[a !iswcm a, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[z !iswcm a, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[ab !iswcm a?, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[bc !iswcm a?, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[abc !iswcm a?c, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[ac !iswcm a?c, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[a !iswcm a*, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[ab !iswcm a*, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[ab !iswcm *b*, yes, no]'}), 'no'); - }); - it('Properly evaluates iswcmcs', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[ab iswcmcs a?, yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[Ab iswcmcs a?, yes, no]'}), 'no'); - }); - it('Properly evaluates !iswcmcs', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[ab !iswcmcs a?, yes, no]'}), 'no'); - await expectEqual(() => evaluate({...options, expression: '$if[Ab !iswcmcs a?, yes, no]'}), 'yes'); - }); - }); - - describe('Input is $if with logical operator', function () { - it('Properly evaluates $ALL', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[$ALL[1 === 1], yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[$ALL[1 === 1, 2 === 2], yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[$ALL[1 === 1, 2 === 3], yes, no]'}), 'no'); - }); - it('Properly evaluates $ANY', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[$ANY[1 === 1], yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[$ANY[1 === 2, 2 === 2], yes, no]'}), 'yes'); - await expectEqual(() => evaluate({...options, expression: '$if[$ANY[1 === 2, 2 === 3], yes, no]'}), 'no'); - }); - it('Properly evaluates $NOT', async function () { - await expectEqual(() => evaluate({...options, expression: '$if[$NOT[1 === 1], yes, no]'}), 'no'); - }); - }); - - describe('Input all the things', function () { - it('Properly evaluates all the things', async function () { - const expression = `a \\b \\$ "c \\d \\"" $ten $sum[$ten, 1] $if[$NOT[$AND[1 === 1, $ten == 9]], 12, -1]\\` - const expect = `a \\b $ "c \\d "" 10 11 12\\` - await expectEqual(() => evaluate({...options, expression}), expect); - }); - }) -}); \ No newline at end of file From b9c8fc5c404834a31665e305ea039365390ffea0 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 07:47:49 -0400 Subject: [PATCH 54/92] spec: added tests for token.ts --- jest.config.js | 3 +- src/tokens/token.spec.ts | 168 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 src/tokens/token.spec.ts diff --git a/jest.config.js b/jest.config.js index 67ba8a1..f2aa6b5 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,6 +9,5 @@ module.exports = { tsconfig: "tsconfig.test.json" } ] - }, - setupFilesAfterEnv: ["./jest/helpers.ts"] + } }; \ No newline at end of file diff --git a/src/tokens/token.spec.ts b/src/tokens/token.spec.ts new file mode 100644 index 0000000..d508a29 --- /dev/null +++ b/src/tokens/token.spec.ts @@ -0,0 +1,168 @@ +import '../../jest/helpers'; + +// import assert from 'assert'; + +import Token from './token'; + +describe('Token class', function () { + it('Exports a class as default', function () { + expect(typeof Token).toBe('function'); + }); +}); + +describe('Token#evaluate', function () { + it('is a property', function () { + expect(Token.prototype).hasProperty('evaluate'); + }); + it('is a function', function () { + expect(typeof Token.prototype.evaluate).toBe('function') + }); +}); + +describe('Token#toJSON', function () { + it('is a property', function () { + expect(Token.prototype).hasProperty('toJSON'); + }); + it('is a function', function () { + expect(typeof Token.prototype.toJSON).toBe('function'); + }); +}); + +describe('Token#toString', function () { + it('is a property', function () { + expect(Token.prototype).hasProperty('toString'); + }); + it('is a function', function () { + expect(typeof Token.prototype.toString).toBe('function'); + }); +}); + +describe('new Token()', function () { + it('doesn\'t error when constructing', function () { + new Token(); + }); +}); + +describe('Token.type', function () { + const token = new Token(); + it('is a property', function () { + expect(token).hasProperty('type'); + }); + it('is a number', function () { + expect(typeof token.type).toBe('number'); + }); + it('is finite', function () { + expect(Number.isFinite(token.type)).toBe(true); + }); + it ('defaults to 0', function () { + expect(token.type).toBe(0); + }); + it('is stored properly when specified', function () { + const token = new Token({ type: 99 }); + expect(token.type).toBe(99); + }); +}); + +describe('Token.position', function () { + const token = new Token(); + it('is a property', function () { + expect(token).hasProperty('position'); + }); + it('is a number', function () { + expect(typeof token.position).toBe('number'); + }); + it('is finite', function () { + expect(Number.isFinite(token.position)).toBe(true); + }); + it ('defaults to 0', function () { + expect(token.position).toBe(-1); + }); + it('is stored properly when specified', function () { + const token = new Token({ position: 99 }); + expect(token.position).toBe(99); + }); +}); + +describe('Token.value', function () { + const token = new Token() + it('is a property', function () { + expect(token).hasProperty('value'); + }); + it('defaults to undefined', function () { + expect(token.value).toBeUndefined(); + }); + it('is stored properly when specified', function () { + const token = new Token({ value: 'value' }); + expect(token.value).toBe('value'); + }); +}); + +describe('Token.evaluate()', function () { + it('returns null when no value is given', async function () { + const token = new Token(); + const result = await token.evaluate({}, {}); + expect(result).toBeNull(); + }); + it('returns the value when specified', async function () { + const token = new Token({ value: 'value' }); + const result = await token.evaluate({}, {}); + expect(result).toBe('value'); + }); +}); + +describe('Token.toJSON()', function () { + const token = new Token({type: 1, position: 1, value: 'value'}); + const json = token.toJSON(); + + it('returns an object', function () { + expect(typeof json).toBe('object'); + }); + it('the result is an adequite depiction', function () { + expect(json).hasProperty('type'); + expect(json.type).toBe(1); + expect(json).hasProperty('position'); + expect(json.position).toBe(1); + expect(json).hasProperty('value'); + expect(json.value).toBe('value'); + + const extraKeys : boolean = Object.keys(json).some(key => ( + key !== 'type' && + key !== 'position' && + key !== 'value' + )); + + if (extraKeys) { + throw new Error('extra properties exist'); + } + }) +}); + +describe('Token.toString()', function () { + const token = new Token({type: 1, position: 1, value: 'value'}); + it('returns a string', function () { + expect(typeof token.toString()).toBe('string'); + }); + it('the result is valid json', function () { + JSON.parse(token.toString()); + }); + it('the result is an adequit depiction of token', function () { + const json = JSON.parse(token.toString()); + + expect(json).hasProperty('type'); + expect(json.type).toBe(1); + expect(json).hasProperty('position'); + expect(json.position).toBe(1); + expect(json).hasProperty('value'); + expect(json.value).toBe('value'); + + const extraKeys : boolean = Object.keys(json).some(key => ( + key !== 'type' && + key !== 'position' && + key !== 'value' + )); + + if (extraKeys) { + throw new Error('extra properties exist'); + } + }); +}); \ No newline at end of file From ed1207701f5a92b280f0f0581c6a6324512f1d93 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 07:48:19 -0400 Subject: [PATCH 55/92] chore: removed commented assert import --- src/tokens/token.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tokens/token.spec.ts b/src/tokens/token.spec.ts index d508a29..c90e20c 100644 --- a/src/tokens/token.spec.ts +++ b/src/tokens/token.spec.ts @@ -1,7 +1,5 @@ import '../../jest/helpers'; -// import assert from 'assert'; - import Token from './token'; describe('Token class', function () { From 288e1bc3db5d0b30dabd54f36020140508588899 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 21 Sep 2022 09:46:31 -0400 Subject: [PATCH 56/92] chore: expand test coverage --- jest/helpers.ts | 17 ++++ src/tokens/token-list.ts | 2 +- src/tokens/token-operator.spec.ts | 96 ++++++++++++++++++++ src/tokens/token-operator.ts | 50 ++++++----- src/tokens/token-text.spec.ts | 64 +++++++++++++ src/tokens/token-text.ts | 16 +++- src/tokens/token.spec.ts | 143 ++++++++++++++---------------- src/tokens/token.ts | 13 +-- src/types/token-types.ts | 1 + 9 files changed, 293 insertions(+), 109 deletions(-) create mode 100644 src/tokens/token-operator.spec.ts create mode 100644 src/tokens/token-text.spec.ts diff --git a/jest/helpers.ts b/jest/helpers.ts index ab2b861..6125d14 100644 --- a/jest/helpers.ts +++ b/jest/helpers.ts @@ -3,6 +3,7 @@ export {} interface CustomMatchers { hasProperty(key: string): R; + toAsyncThrow(): R; } declare global { @@ -27,5 +28,21 @@ expect.extend({ pass: false, message: () => `expected subject to have '${key}' as a property` } + }, + + toAsyncThrow: async (subject: () => unknown) : Promise<{ pass: boolean, message: () => string}> => { + expect.assertions(1); + try { + await subject(); + return { + pass: false, + message: () => `expected subject to throw an error` + }; + } catch (err) { + return { + pass: true, + message: () => `subject did not throw an error` + } + } } }); \ No newline at end of file diff --git a/src/tokens/token-list.ts b/src/tokens/token-list.ts index 4a0f6c3..a739eb1 100644 --- a/src/tokens/token-list.ts +++ b/src/tokens/token-list.ts @@ -62,7 +62,7 @@ export default class TokenList extends Token { return res; } - toToken() : object { + toToken() : Record { return { ...(super.toJSON()), value: this.value.map(value => value.toJSON()) diff --git a/src/tokens/token-operator.spec.ts b/src/tokens/token-operator.spec.ts new file mode 100644 index 0000000..7700923 --- /dev/null +++ b/src/tokens/token-operator.spec.ts @@ -0,0 +1,96 @@ +import '../../jest/helpers'; + +import TokenType from '../types/token-types'; + +import Token from './token'; +import OperatorToken from './token-operator'; + +const stub = { + value: 'text', + toJSON() { return this.value } +}; + +describe('OperatorToken class', function () { + it('Exports a class as default', function () { + expect(typeof OperatorToken).toBe('function'); + }); +}); + +describe('new OperatorToken()', function () { + it('Doesn\'t error when construdting', function () { + new OperatorToken(); + }); + it('Is an instance of OperatorToken', function () { + const token = new OperatorToken(); + expect(token instanceof OperatorToken).toBe(true); + }); + it('Is an instance of Token', function () { + const token = new OperatorToken(); + expect(token instanceof Token).toBe(true); + }); +}); + +describe('Token#type', function () { + it('Stores type correctly', function () { + const token = new OperatorToken(); + expect(token.type).toBe(TokenType.OPERATOR); + }); +}); + +describe('OperatorToken#left', function () { + it('Stores type correctly', function () { + const token = new OperatorToken({ + left: stub + }); + expect(token.left).toBe(stub); + }); +}); + +describe('OperatorToken#right', function () { + it('Stores type correctly', function () { + const token = new OperatorToken({ + right: stub + }); + expect(token.right).toBe(stub); + }); +}); + +describe('OperatorToken#toJSON', function () { + it('Does not throw', function () { + const token = new OperatorToken(); + expect(() => token.toJSON()).not.toThrow(); + }); + + it('Does not store a value when left is not specified', function () { + const token = new OperatorToken(); + const result = token.toJSON(); + expect(result.left).toBeUndefined(); + }); + + it('Stores left when specified', function () { + const token = new OperatorToken({ left: stub }); + const result = token.toJSON(); + expect(result).hasProperty('left'); + expect(result.left).toBe('text'); + }); + + it('Does not store a value when right is not specified', function () { + const token = new OperatorToken(); + const result = token.toJSON(); + expect(result.left).toBeUndefined(); + }); + + it('Stores right when specified', function () { + const token = new OperatorToken({ right: stub }); + const result = token.toJSON(); + expect(result).hasProperty('right'); + expect(result.right).toBe('text'); + }); +}); + +describe('Operator#evaluate()', function () { + it('Throws an error', async function () { + const token = new OperatorToken(); + expect(() => token.evaluate({}, {})).toAsyncThrow(); + }); +}) \ No newline at end of file diff --git a/src/tokens/token-operator.ts b/src/tokens/token-operator.ts index bd9b654..39a6131 100644 --- a/src/tokens/token-operator.ts +++ b/src/tokens/token-operator.ts @@ -1,34 +1,35 @@ import ParserOptions from '../types/options'; import TokenType from '../types/token-types'; -import Token from './token'; +import Token, { IToken } from './token'; -export interface IOperatorToken { - type: TokenType; - position: number; - left: Token; - right?: Token; +export interface IOperatorToken extends IToken { + left?: unknown; + right?: unknown; } export default class OperatorToken extends Token { - protected left: Token; - protected right?: Token; + public left?: Token; + public right?: Token; - constructor(token: IOperatorToken) { - super(token); - this.left = token.left; - this.right = token.right; - } - - /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - async evaluate(options: ParserOptions, meta: unknown) : Promise { - return false; + constructor(token: IOperatorToken = {}) { + super({ + type: TokenType.OPERATOR, + ...token + }); + if (token.left != null) { + this.left = token.left; + } + if (token.right != null) { + this.right = token.right; + } } - toJSON() : object { - - const result : Record = { - ...(super.toJSON()), - left: this.left.toJSON() + toJSON() : Record { + const result : Record = { + ...(super.toJSON()) + }; + if (this.left != null) { + result.left = this.left.toJSON(); } if (this.right != null) { @@ -36,4 +37,9 @@ export default class OperatorToken extends Token { } return result; } + + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + async evaluate(options: ParserOptions, meta: unknown) : Promise { + throw new Error('TODO ExpressionError - Operator missing evaluator function'); + } } \ No newline at end of file diff --git a/src/tokens/token-text.spec.ts b/src/tokens/token-text.spec.ts new file mode 100644 index 0000000..46eb5b7 --- /dev/null +++ b/src/tokens/token-text.spec.ts @@ -0,0 +1,64 @@ +import '../../jest/helpers'; + +import TokenType from '../types/token-types'; + +import Token from './token'; +import TextToken from './token-text'; + +describe('TextToken class', function () { + it('Exports a class as default', function () { + expect(typeof Token).toBe('function'); + }); +}); + +describe('new TextToken()', function () { + it('Doesn\'t error when constructing', function () { + new TextToken(); + }); + it('Is an instance of TextToken', function () { + const token = new TextToken(); + expect(token instanceof TextToken).toBe(true); + }); + it('Is an instance of Token', function () { + const token = new TextToken(); + expect(token instanceof Token).toBe(true); + }); + it('Stores type correctly', function () { + const token = new TextToken(); + expect(token.type).toBe(TokenType.TEXT); + }); + it('Stores value correctly', function () { + const token = new TextToken({value: 'value'}); + expect(token.value).toBe('value'); + }); +}); + +describe('Token#type', function () { + it('Stores type correctly', function () { + const token = new TextToken(); + expect(token.type).toBe(TokenType.TEXT); + }); +}); + +describe('Token#value', function () { + it('Stores type correctly', function () { + const token = new TextToken({value: 'value'}); + expect(token.value).toBe('value'); + }); +}); + +describe('Token#evaluate()', function () { + it('resolves to no value an empty string', async function () { + expect.assertions(1); + const token = new TextToken(); + const result = await token.evaluate({}, {}); + expect(result).toBe(''); + }); + + it('resolves to a string', async function () { + expect.assertions(1); + const token = new TextToken({value: "text"}); + const result = await token.evaluate({}, {}); + expect(result).toBe('text'); + }); +}); \ No newline at end of file diff --git a/src/tokens/token-text.ts b/src/tokens/token-text.ts index 329b5cf..6b5b549 100644 --- a/src/tokens/token-text.ts +++ b/src/tokens/token-text.ts @@ -1,17 +1,25 @@ +import type ParserOptions from '../types/options'; import TokenType from '../types/token-types'; + import Token from './token'; export interface ITextToken { - position: number; - value: string; + position?: number; + value?: string; } export default class TextToken extends Token { public value: string; - constructor(token: ITextToken) { + constructor(token: ITextToken = {}) { super({ ...token, - type: TokenType.TEXT + type: TokenType.TEXT, + value: token.value == null ? '' : token.value }); } + + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + async evaluate(options: ParserOptions, meta: unknown) : Promise { + return this.value == null ? '' : this.value; + } } \ No newline at end of file diff --git a/src/tokens/token.spec.ts b/src/tokens/token.spec.ts index c90e20c..b9c46b3 100644 --- a/src/tokens/token.spec.ts +++ b/src/tokens/token.spec.ts @@ -8,114 +8,84 @@ describe('Token class', function () { }); }); -describe('Token#evaluate', function () { - it('is a property', function () { - expect(Token.prototype).hasProperty('evaluate'); - }); - it('is a function', function () { - expect(typeof Token.prototype.evaluate).toBe('function') - }); -}); - -describe('Token#toJSON', function () { - it('is a property', function () { - expect(Token.prototype).hasProperty('toJSON'); - }); - it('is a function', function () { - expect(typeof Token.prototype.toJSON).toBe('function'); - }); -}); - -describe('Token#toString', function () { - it('is a property', function () { - expect(Token.prototype).hasProperty('toString'); - }); - it('is a function', function () { - expect(typeof Token.prototype.toString).toBe('function'); - }); -}); - describe('new Token()', function () { - it('doesn\'t error when constructing', function () { + it('Doesn\'t error when constructing', function () { new Token(); }); }); -describe('Token.type', function () { +describe('Token#type', function () { const token = new Token(); - it('is a property', function () { + it('Is a property', function () { expect(token).hasProperty('type'); }); - it('is a number', function () { + it('Is a number', function () { expect(typeof token.type).toBe('number'); }); - it('is finite', function () { + it('Is finite', function () { expect(Number.isFinite(token.type)).toBe(true); }); - it ('defaults to 0', function () { + it('Defaults to 0', function () { expect(token.type).toBe(0); }); - it('is stored properly when specified', function () { + it('Is stored properly when specified', function () { const token = new Token({ type: 99 }); expect(token.type).toBe(99); }); }); -describe('Token.position', function () { +describe('Token#position', function () { const token = new Token(); - it('is a property', function () { + it('Is a property', function () { expect(token).hasProperty('position'); }); - it('is a number', function () { + it('Is a number', function () { expect(typeof token.position).toBe('number'); }); - it('is finite', function () { + it('Is finite', function () { expect(Number.isFinite(token.position)).toBe(true); }); - it ('defaults to 0', function () { + it ('Defaults to 0', function () { expect(token.position).toBe(-1); }); - it('is stored properly when specified', function () { + it('Is stored properly when specified', function () { const token = new Token({ position: 99 }); expect(token.position).toBe(99); }); }); -describe('Token.value', function () { +describe('Token#value', function () { const token = new Token() - it('is a property', function () { + it('Is a property', function () { expect(token).hasProperty('value'); }); - it('defaults to undefined', function () { + it('Defaults to undefined', function () { expect(token.value).toBeUndefined(); }); - it('is stored properly when specified', function () { + it('Is stored properly when specified', function () { const token = new Token({ value: 'value' }); expect(token.value).toBe('value'); }); }); -describe('Token.evaluate()', function () { - it('returns null when no value is given', async function () { - const token = new Token(); - const result = await token.evaluate({}, {}); - expect(result).toBeNull(); - }); - it('returns the value when specified', async function () { - const token = new Token({ value: 'value' }); - const result = await token.evaluate({}, {}); - expect(result).toBe('value'); +describe('Token#toJSON()', function () { + it('Is a property', function () { + expect(Token.prototype).hasProperty('toJSON'); }); -}); -describe('Token.toJSON()', function () { - const token = new Token({type: 1, position: 1, value: 'value'}); - const json = token.toJSON(); + it('Is a function', function () { + expect(typeof Token.prototype.toJSON).toBe('function'); + }); - it('returns an object', function () { + it('Returns an object', function () { + const token = new Token({type: 1, position: 1, value: 'value'}); + const json = token.toJSON(); expect(typeof json).toBe('object'); }); - it('the result is an adequite depiction', function () { + + it('The result is an adequite depiction', function () { + const token = new Token({type: 1, position: 1, value: 'value'}); + const json = token.toJSON(); expect(json).hasProperty('type'); expect(json.type).toBe(1); expect(json).hasProperty('position'); @@ -123,27 +93,36 @@ describe('Token.toJSON()', function () { expect(json).hasProperty('value'); expect(json.value).toBe('value'); - const extraKeys : boolean = Object.keys(json).some(key => ( + expect(Object.keys(json).some(key => ( key !== 'type' && key !== 'position' && key !== 'value' - )); - - if (extraKeys) { - throw new Error('extra properties exist'); - } + ))).toBe(false); }) }); -describe('Token.toString()', function () { - const token = new Token({type: 1, position: 1, value: 'value'}); - it('returns a string', function () { +describe('Token#toString()', function () { + + it('Is a property', function () { + expect(Token.prototype).hasProperty('toString'); + }); + + it('Is a function', function () { + expect(typeof Token.prototype.toString).toBe('function'); + }); + + it('Returns a string', function () { + const token = new Token({type: 1, position: 1, value: 'value'}); expect(typeof token.toString()).toBe('string'); }); - it('the result is valid json', function () { + + it('The result is valid json', function () { + const token = new Token({type: 1, position: 1, value: 'value'}); JSON.parse(token.toString()); }); - it('the result is an adequit depiction of token', function () { + + it('The result is an adequit depiction of token', function () { + const token = new Token({type: 1, position: 1, value: 'value'}); const json = JSON.parse(token.toString()); expect(json).hasProperty('type'); @@ -153,14 +132,26 @@ describe('Token.toString()', function () { expect(json).hasProperty('value'); expect(json.value).toBe('value'); - const extraKeys : boolean = Object.keys(json).some(key => ( + expect(Object.keys(json).some(key => ( key !== 'type' && key !== 'position' && key !== 'value' - )); + ))).toBe(false); + }); +}); - if (extraKeys) { - throw new Error('extra properties exist'); - } +describe('Token#evaluate()', function () { + + it('Is a property', function () { + expect(Token.prototype).hasProperty('evaluate'); + }); + + it('Is a function', function () { + expect(typeof Token.prototype.evaluate).toBe('function') + }); + + it('to throw an error', function () { + const token = new Token(); + expect(() => token.evaluate({}, {})).toAsyncThrow(); }); }); \ No newline at end of file diff --git a/src/tokens/token.ts b/src/tokens/token.ts index 84df6e7..d8b0986 100644 --- a/src/tokens/token.ts +++ b/src/tokens/token.ts @@ -20,20 +20,21 @@ export default class Token { this.value = token.value; } - /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - async evaluate(options: ParserOptions, meta: unknown) : Promise { - return this.value == null ? null : this.value; - } - toJSON() : Record { + return { type: this.type, position: this.position, - value: this.value == null ? null : this.value + value: this.value }; } toString() : string { return JSON.stringify(this.toJSON()); } + + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + async evaluate(options: ParserOptions, meta: unknown) : Promise { + throw new Error('TODO - ExpressionError: token does not implement evaluate function'); + } } \ No newline at end of file diff --git a/src/types/token-types.ts b/src/types/token-types.ts index b10b45b..4ad7d1d 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -6,6 +6,7 @@ const enum TokenType { FUNCTIONAL, TEXT, ARGUMENT, + OPERATOR, LOGICAL, COMPARISON, EMPTY From 0699ccf40dcf83637c7e0ad25de511bfd33a3be7 Mon Sep 17 00:00:00 2001 From: SReject Date: Fri, 23 Sep 2022 19:21:52 -0400 Subject: [PATCH 57/92] chore(config): add baseurl --- tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 9bede23..b725a18 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "noImplicitAny": true, "rootDir": "src", + "baseUrl": ".", "outDir": "lib", "incremental": true, @@ -26,5 +27,5 @@ ], "exclude": [ "node_modules/**/*" - ] + ], } \ No newline at end of file From cc0999627604bc3b56c89c6ed3c3fbf445c0898a Mon Sep 17 00:00:00 2001 From: SReject Date: Fri, 23 Sep 2022 19:22:15 -0400 Subject: [PATCH 58/92] chore(config): use local tsc in vscode --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c7c1623 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "./node_modules/typescript/lib" +} \ No newline at end of file From eb451b5dc087511333c9238015118f180403c542 Mon Sep 17 00:00:00 2001 From: SReject Date: Fri, 23 Sep 2022 19:22:48 -0400 Subject: [PATCH 59/92] rebase: alter directory structure --- src/expression.ts | 36 ---- src/helpers/get-potential-tokens.ts | 2 +- src/helpers/whitespace.ts | 2 +- src/index.ts | 116 +++-------- src/parse/argument-list/index.ts | 1 + .../argument-list/tokenize/index.ts} | 15 +- src/parse/argument/index.ts | 2 + src/parse/argument/token/index.ts | 90 ++++++++ .../argument/tokenize/index.ts} | 36 ++-- src/parse/condition/index.ts | 3 + .../condition/operators/comparison/index.ts | 58 ++++++ .../comparison/operators/contains.ts | 41 ++++ .../comparison/operators/equal-loose.ts | 41 ++++ .../comparison/operators/equal-strict.ts | 21 ++ .../operators/comparison/operators/exists.ts | 16 ++ .../operators/greater-than-equal.ts | 24 +++ .../comparison/operators/greater-than.ts | 24 +++ .../operators/comparison/operators/is-bool.ts | 46 +++++ .../operators/comparison/operators/is-null.ts | 17 ++ .../comparison/operators/less-than-equal.ts | 25 +++ .../comparison/operators/less-than.ts | 25 +++ .../comparison/operators/numerical.ts | 49 +++++ .../operators/comparison/operators/regex.ts | 37 ++++ .../comparison/operators/wildcard.ts | 124 +++++++++++ src/parse/condition/operators/index.ts | 6 + .../condition/operators/logical/index.ts | 9 + .../operators/logical/operators/and.ts | 21 ++ .../operators/logical/operators/not.ts | 16 ++ .../operators/logical/operators/or.ts | 21 ++ src/parse/condition/operators/token/index.ts | 117 +++++++++++ .../condition/tokenize/block.ts} | 8 +- .../condition/tokenize/comparison.ts} | 51 +++-- .../condition/tokenize/index.ts} | 14 +- .../condition/tokenize/logical-not.ts} | 19 +- .../condition/tokenize/logical-operator.ts | 59 ++++++ .../condition/tokenize}/operand.ts | 37 ++-- .../condition/tokenize/operator.ts} | 16 +- src/parse/expression.ts | 62 ++++++ src/parse/function/index.ts | 2 + src/parse/function/token/index.ts | 139 +++++++++++++ .../function/tokenize/index.ts} | 26 ++- src/parse/if/index.ts | 2 + .../if/token/index.ts} | 30 ++- .../if/tokenize/index.ts} | 16 +- src/parse/index.ts | 81 ++++++++ src/parse/text/index.ts | 6 + .../text/token/index.ts} | 11 +- .../text/tokenize/escape-block.ts} | 19 +- .../text/tokenize/escape-single.ts} | 4 +- .../text/tokenize/quoted.ts} | 23 ++- .../text/tokenize/special.ts} | 7 +- src/{tokens => parse}/token-list.ts | 45 ++-- src/{tokens => parse}/token.ts | 9 +- src/tokenize/comparison/comparator-map.ts | 113 ---------- src/tokenize/condition-logical.ts | 67 ------ src/tokens/comparison/base.ts | 48 ----- src/tokens/comparison/contains.ts | 72 ------- src/tokens/comparison/equal-loose.ts | 64 ------ src/tokens/comparison/equal-strict.ts | 43 ---- src/tokens/comparison/exists.ts | 33 --- src/tokens/comparison/greater-than-equal.ts | 48 ----- src/tokens/comparison/greater-than.ts | 48 ----- src/tokens/comparison/isbool.ts | 49 ----- src/tokens/comparison/isnull.ts | 34 --- src/tokens/comparison/less-than-equal.ts | 48 ----- src/tokens/comparison/less-than.ts | 48 ----- src/tokens/comparison/numerical.ts | 69 ------- src/tokens/comparison/regex.ts | 59 ------ src/tokens/comparison/wildcard.ts | 193 ------------------ src/tokens/logical/base.ts | 16 -- src/tokens/logical/logical-and.ts | 31 --- src/tokens/logical/logical-not.ts | 23 --- src/tokens/logical/logical-or.ts | 31 --- src/tokens/token-function.ts | 68 ------ src/tokens/token-operator.spec.ts | 96 --------- src/tokens/token-operator.ts | 45 ---- src/tokens/token-text.spec.ts | 64 ------ src/tokens/token.spec.ts | 157 -------------- src/types/options.ts | 3 +- src/types/tokenize-state.ts | 2 +- 80 files changed, 1435 insertions(+), 1864 deletions(-) delete mode 100644 src/expression.ts create mode 100644 src/parse/argument-list/index.ts rename src/{tokenize/argument-list.ts => parse/argument-list/tokenize/index.ts} (84%) create mode 100644 src/parse/argument/index.ts create mode 100644 src/parse/argument/token/index.ts rename src/{tokenize/argument.ts => parse/argument/tokenize/index.ts} (79%) create mode 100644 src/parse/condition/index.ts create mode 100644 src/parse/condition/operators/comparison/index.ts create mode 100644 src/parse/condition/operators/comparison/operators/contains.ts create mode 100644 src/parse/condition/operators/comparison/operators/equal-loose.ts create mode 100644 src/parse/condition/operators/comparison/operators/equal-strict.ts create mode 100644 src/parse/condition/operators/comparison/operators/exists.ts create mode 100644 src/parse/condition/operators/comparison/operators/greater-than-equal.ts create mode 100644 src/parse/condition/operators/comparison/operators/greater-than.ts create mode 100644 src/parse/condition/operators/comparison/operators/is-bool.ts create mode 100644 src/parse/condition/operators/comparison/operators/is-null.ts create mode 100644 src/parse/condition/operators/comparison/operators/less-than-equal.ts create mode 100644 src/parse/condition/operators/comparison/operators/less-than.ts create mode 100644 src/parse/condition/operators/comparison/operators/numerical.ts create mode 100644 src/parse/condition/operators/comparison/operators/regex.ts create mode 100644 src/parse/condition/operators/comparison/operators/wildcard.ts create mode 100644 src/parse/condition/operators/index.ts create mode 100644 src/parse/condition/operators/logical/index.ts create mode 100644 src/parse/condition/operators/logical/operators/and.ts create mode 100644 src/parse/condition/operators/logical/operators/not.ts create mode 100644 src/parse/condition/operators/logical/operators/or.ts create mode 100644 src/parse/condition/operators/token/index.ts rename src/{tokenize/condition-block.ts => parse/condition/tokenize/block.ts} (82%) rename src/{tokenize/comparison/index.ts => parse/condition/tokenize/comparison.ts} (66%) rename src/{tokenize/condition.ts => parse/condition/tokenize/index.ts} (76%) rename src/{tokenize/condition-not.ts => parse/condition/tokenize/logical-not.ts} (60%) create mode 100644 src/parse/condition/tokenize/logical-operator.ts rename src/{tokenize/comparison => parse/condition/tokenize}/operand.ts (80%) rename src/{tokenize/comparison/comparator.ts => parse/condition/tokenize/operator.ts} (68%) create mode 100644 src/parse/expression.ts create mode 100644 src/parse/function/index.ts create mode 100644 src/parse/function/token/index.ts rename src/{tokenize/function.ts => parse/function/tokenize/index.ts} (62%) create mode 100644 src/parse/if/index.ts rename src/{tokens/token-function-if.ts => parse/if/token/index.ts} (56%) rename src/{tokenize/function-if.ts => parse/if/tokenize/index.ts} (78%) create mode 100644 src/parse/index.ts create mode 100644 src/parse/text/index.ts rename src/{tokens/token-text.ts => parse/text/token/index.ts} (65%) rename src/{tokenize/text-escape-block.ts => parse/text/tokenize/escape-block.ts} (79%) rename src/{tokenize/text-escape-single.ts => parse/text/tokenize/escape-single.ts} (84%) rename src/{tokenize/text-quoted.ts => parse/text/tokenize/quoted.ts} (81%) rename src/{tokenize/text-special.ts => parse/text/tokenize/special.ts} (82%) rename src/{tokens => parse}/token-list.ts (57%) rename src/{tokens => parse}/token.ts (72%) delete mode 100644 src/tokenize/comparison/comparator-map.ts delete mode 100644 src/tokenize/condition-logical.ts delete mode 100644 src/tokens/comparison/base.ts delete mode 100644 src/tokens/comparison/contains.ts delete mode 100644 src/tokens/comparison/equal-loose.ts delete mode 100644 src/tokens/comparison/equal-strict.ts delete mode 100644 src/tokens/comparison/exists.ts delete mode 100644 src/tokens/comparison/greater-than-equal.ts delete mode 100644 src/tokens/comparison/greater-than.ts delete mode 100644 src/tokens/comparison/isbool.ts delete mode 100644 src/tokens/comparison/isnull.ts delete mode 100644 src/tokens/comparison/less-than-equal.ts delete mode 100644 src/tokens/comparison/less-than.ts delete mode 100644 src/tokens/comparison/numerical.ts delete mode 100644 src/tokens/comparison/regex.ts delete mode 100644 src/tokens/comparison/wildcard.ts delete mode 100644 src/tokens/logical/base.ts delete mode 100644 src/tokens/logical/logical-and.ts delete mode 100644 src/tokens/logical/logical-not.ts delete mode 100644 src/tokens/logical/logical-or.ts delete mode 100644 src/tokens/token-function.ts delete mode 100644 src/tokens/token-operator.spec.ts delete mode 100644 src/tokens/token-operator.ts delete mode 100644 src/tokens/token-text.spec.ts delete mode 100644 src/tokens/token.spec.ts diff --git a/src/expression.ts b/src/expression.ts deleted file mode 100644 index 047e1b0..0000000 --- a/src/expression.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type ParserOptions from './types/options'; -import TokenType from './types/token-types'; - -import Token from './tokens/token'; - -interface IExpression { - options: ParserOptions; - value: Token[] -} - -export default class Expression extends Token { - private options: ParserOptions; - - constructor(token: IExpression) { - super({ - position: 0, - type: TokenType.EXPRESSION, - ...token - }); - this.options = token.options; - } - - async evaluate(meta: unknown = {}) : Promise { - let index = 0; - let result = ''; - while (index < (this.value).length) { - - const value = await (this.value)[index].evaluate(this.options, meta); - if (value != null) { - result += JSON.stringify(value); - } - index += 1; - } - return result; - } -} \ No newline at end of file diff --git a/src/helpers/get-potential-tokens.ts b/src/helpers/get-potential-tokens.ts index 0766ea9..ccefcb6 100644 --- a/src/helpers/get-potential-tokens.ts +++ b/src/helpers/get-potential-tokens.ts @@ -1,5 +1,5 @@ import type IPreToken from '../types/pre-token'; -import ParserOptions from '../types/options'; +import type ParserOptions from '../types/options'; import split from './unicode-safe-split'; diff --git a/src/helpers/whitespace.ts b/src/helpers/whitespace.ts index abf47bc..92eb074 100644 --- a/src/helpers/whitespace.ts +++ b/src/helpers/whitespace.ts @@ -1,4 +1,4 @@ -import IPreToken from '../types/pre-token'; +import type IPreToken from '../types/pre-token'; export const is = (tokens: IPreToken[], cursor: number) : boolean => { return ( diff --git a/src/index.ts b/src/index.ts index e1decc1..712d201 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ import has from './helpers/has'; -import getPotentialTokens from './helpers/get-potential-tokens'; + +import IParseOptions from './types/options'; import { type default as ParserOptions, @@ -7,20 +8,8 @@ import { type IFunctionLookup } from './types/options'; -import TokenType from './types/token-types'; -import ITokenizeState from './types/tokenize-state'; - -import Token from './tokens/token'; -import TextToken from './tokens/token-text'; - -import tokenizeTextEscapeSingle from './tokenize/text-escape-single'; -import tokenizeTextEscapeBlock from './tokenize/text-escape-block'; -import tokenizeTextQuoted from './tokenize/text-quoted'; -import tokenizeTextSpecial from './tokenize/text-special'; -import tokenizeFunctionIf from './tokenize/function-if'; -import tokenizeFunction from './tokenize/function'; - -import Expression from './expression'; +import Expression from './parse/expression'; +import tokenize from './parse'; export { ExpressionError, @@ -40,6 +29,7 @@ export class Expressionish { this.functionLookups = options.functionLookups || {}; this.config = { + "if": options.if || true, eol: options.eol || 'keep', specialSequences: options.specialSequences || true }; @@ -76,86 +66,34 @@ export class Expressionish { } async tokenize(subject: string) : Promise { - if ( - subject == null || - typeof subject !== 'string' - ) { - throw new TypeError('input must be a string'); - } - - let tokens = getPotentialTokens(this.config, subject); - let cursor = 0; - - const result : Token[] = []; - - while (cursor < tokens.length) { - - const mockState : ITokenizeState = { - options: { - ...(this.config), - functionHandlers: { ...(this.functionHandlers) }, - functionLookups: { ...(this.functionLookups)} - }, - tokens: [...tokens], - cursor, - stack: [] - }; - - if ( - await tokenizeTextEscapeSingle(mockState) || - await tokenizeTextEscapeBlock(mockState) || - await tokenizeTextQuoted(mockState) || - await tokenizeTextSpecial(mockState) || - await tokenizeFunctionIf(mockState) || - await tokenizeFunction(mockState) - ) { - const lastToken : Token = result[result.length - 1]; - if ( - lastToken != null && - lastToken.type === TokenType.TEXT && - mockState.output && - (mockState.output).type === TokenType.TEXT - ) { - (lastToken.value) += (mockState.output).value; - - } else { - result.push(mockState.output); - } - - tokens = mockState.tokens; - cursor = mockState.cursor; - continue; - } - - // Assume anything else is plain text - const last : Token = result[result.length - 1]; - if (last != null && last.type === TokenType.TEXT) { - last.value += tokens[cursor].value; - - } else { - result.push(new TextToken(tokens[cursor])); - } - - cursor += 1; + const config : IParseOptions = { + functionHandlers: { ...(this.functionHandlers) }, + functionLookups: { ...(this.functionLookups) }, + ...(this.config) } - - return new Expression({ - options: { - ...(this.config), - functionHandlers: { ...(this.functionHandlers) }, - functionLookups: { ...(this.functionLookups)} - }, - value: result - }); + return tokenize(config, subject); } static async tokenize(subject: string, options?: ParserOptions) : Promise { - const parser = new Expressionish(options); - return parser.tokenize(subject); + return tokenize({ + functionHandlers: {}, + functionLookups: {}, + "if": true, + eol: 'keep', + specialSequences: true, + ...options + }, subject); } static async evaluate(subject: string, meta: unknown = {}, options?: ParserOptions) : Promise { - const tokens = await Expressionish.tokenize(subject, options); - return tokens.evaluate(meta); + const expression = await tokenize({ + functionHandlers: {}, + functionLookups: {}, + "if": true, + eol: 'keep', + specialSequences: true, + ...options + }, subject); + return expression.evaluate(meta); } } \ No newline at end of file diff --git a/src/parse/argument-list/index.ts b/src/parse/argument-list/index.ts new file mode 100644 index 0000000..7c3473d --- /dev/null +++ b/src/parse/argument-list/index.ts @@ -0,0 +1 @@ +export { default as tokenizeArgumentsList } from './tokenize'; \ No newline at end of file diff --git a/src/tokenize/argument-list.ts b/src/parse/argument-list/tokenize/index.ts similarity index 84% rename from src/tokenize/argument-list.ts rename to src/parse/argument-list/tokenize/index.ts index 9ae3bd7..9d6b27e 100644 --- a/src/tokenize/argument-list.ts +++ b/src/parse/argument-list/tokenize/index.ts @@ -1,12 +1,12 @@ -import TokenType from '../types/token-types'; -import type ITokenizeState from '../types/tokenize-state'; +import TokenType from '../../../types/token-types'; +import type ITokenizeState from '../../../types/tokenize-state'; -import Token from '../tokens/token'; +import { ExpressionSyntaxError } from '../../../errors'; -import tokenizeArgument from './argument'; -import tokenizeCondition from './condition'; +import Token from '../../token'; -import { ExpressionSyntaxError } from '../errors'; +import { tokenizeArgument } from '../../argument'; +import { tokenizeCondition } from '../../condition'; export default async (state: ITokenizeState, isCondition = false) : Promise => { const { stack, options } = state; @@ -35,8 +35,7 @@ export default async (state: ITokenizeState, isCondition = false) : Promise { + return { + ...(super.toJSON()), + value: this.value.map(value => value.toJSON()) + }; + } + async evaluate(options: IParseOptions, meta: unknown) : Promise { + if (options == null) { + options = {}; + } + if (meta == null) { + meta = {}; + } + + const parts = this.value; + + let res : unknown; + for (let idx = 0; idx < parts.length; idx += 1) { + const value = await parts[idx].evaluate(options, meta); + + if (options.verifyOnly) { + continue; + } + + if (value === undefined) { + continue; + } + + if (res == null) { + res = value; + continue; + } + + const strValue = toText(value); + if (strValue == null) { + continue; + } + + if (typeof res !== 'string') { + const strRes = toText(res); + if (strRes == null) { + res = strValue; + + } else { + res = strRes + strValue; + } + continue; + } + + res += strValue; + } + + return res; + } +} \ No newline at end of file diff --git a/src/tokenize/argument.ts b/src/parse/argument/tokenize/index.ts similarity index 79% rename from src/tokenize/argument.ts rename to src/parse/argument/tokenize/index.ts index 2472edf..cfd494a 100644 --- a/src/tokenize/argument.ts +++ b/src/parse/argument/tokenize/index.ts @@ -1,18 +1,20 @@ -import TokenType from '../types/token-types'; -import type ITokenizeState from '../types/tokenize-state'; +import TokenType from '../../../types/token-types'; +import type ITokenizeState from '../../../types/tokenize-state'; -import type Token from '../tokens/token'; -import TokenList from '../tokens/token-list'; -import TextToken from '../tokens/token-text'; +import type Token from '../../token'; +import TokenList from '../../token-list'; +import { + TextToken, + tokenizeEscape, + tokenizeEscapeBlock, + tokenizeQuoted, + tokenizeSpecial +} from '../../text'; -import tokenizeTextEscapeSingle from './text-escape-single'; -import tokenizeTextEscapeBlock from './text-escape-block'; -import tokenizeTextQuoted from './text-quoted'; -import tokenizeTextSpecial from './text-special'; -import tokenizeFunctionIf from './function-if'; -import tokenizeFunction from './function'; +import { tokenizeIf } from '../../if'; +import { tokenizeFunction } from '../../function'; -import { ExpressionSyntaxError } from '../errors'; +import { ExpressionSyntaxError } from '../../../errors'; export default async (state: ITokenizeState) : Promise => { const { stack, options } = state; @@ -38,11 +40,11 @@ export default async (state: ITokenizeState) : Promise => { }; if ( - await tokenizeTextEscapeSingle(mockState, ['"', '$', '\\', ',', ']',]) || - await tokenizeTextEscapeBlock(mockState) || - await tokenizeTextQuoted(mockState) || - await tokenizeTextSpecial(mockState) || - await tokenizeFunctionIf(mockState) || + await tokenizeEscape(mockState, ['"', '$', '\\', ',', ']',]) || + await tokenizeEscapeBlock(mockState) || + await tokenizeQuoted(mockState) || + await tokenizeSpecial(mockState) || + await tokenizeIf(mockState) || await tokenizeFunction(mockState) ) { if (mockState.output) { diff --git a/src/parse/condition/index.ts b/src/parse/condition/index.ts new file mode 100644 index 0000000..a0bb6fb --- /dev/null +++ b/src/parse/condition/index.ts @@ -0,0 +1,3 @@ +export { default as tokenizeCondition } from './tokenize'; + +export { OperatorToken, type IOperatorToken, type IOperator, ArgumentsQuantifier } from './operators'; \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/index.ts b/src/parse/condition/operators/comparison/index.ts new file mode 100644 index 0000000..b231459 --- /dev/null +++ b/src/parse/condition/operators/comparison/index.ts @@ -0,0 +1,58 @@ +import { type default as IParseOptions } from '../../../../types/options'; + +import { type IOperator, type IHandleState } from '../token'; + +import operatorContains from './operators/contains'; +import operatorEqualLoose from './operators/equal-loose'; +import operatorEqualStrict from './operators/equal-strict'; +import operatorExists from './operators/exists'; +import operatorGreaterThanOrEqual from './operators/greater-than-equal'; +import operatorGreaterThan from './operators/greater-than'; +import operatorIsBool from './operators/is-bool'; +import operatorIsNull from './operators/is-null'; +import operatorLessThanOrEqual from './operators/less-than-equal'; +import operatorLessThan from './operators/less-than'; +import operatorNumerical from './operators/numerical'; +import operatorRegex from './operators/regex'; +import operatorWildcard from './operators/wildcard'; + +const operators : Map = new Map(); +[ + operatorContains, + operatorEqualLoose, + operatorEqualStrict, + operatorExists, + operatorGreaterThan, + operatorGreaterThanOrEqual, + operatorIsBool, + operatorIsNull, + operatorLessThan, + operatorLessThanOrEqual, + operatorNumerical, + operatorRegex, + operatorWildcard +].forEach((operator : IOperator) => { + operator.alias.forEach(alias => { + operators.set(alias, operator); + }); + + if (operator.inverse) { + const opin = operator.inverse; + opin.alias.forEach(alias => { + const base : IOperator = { ...operator, inverse: undefined, ...opin }; + + if (!opin.handle) { + base.handle = async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const result = await operator.handle.call(this, options, meta, state); + if (result != null) { + return !result; + } + }; + } + operators.set(alias, base); + }); + } +}); + + +export default operators; \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/contains.ts b/src/parse/condition/operators/comparison/operators/contains.ts new file mode 100644 index 0000000..bf391c9 --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/contains.ts @@ -0,0 +1,41 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; + +import { type default as IParseOptions } from '../../../../../types/options'; + +export default { + name: 'contains', + description: "Checks if the left operand contains the right operand", + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + cased: true, + alias: ['contains'], + inverse: { + description: "Checks if operands are not loosely equal", + alias: ['!contains'] + }, + handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + + const { left, right, caseSensitive = false } = state; + + if (Array.isArray(left)) { + return left.some((left: unknown) => { + if (typeof left === 'string') { + if (typeof right !== 'string') { + return false; + } + if (caseSensitive) { + return left === right; + } + return left.toLowerCase() === right.toLowerCase(); + } + return left === right; + }); + } + + if (typeof left === 'string' && typeof right === 'string') { + if (caseSensitive) { + return left.includes(right); + } + return left.toLowerCase().includes(right.toLowerCase()); + } + } +}; \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/equal-loose.ts b/src/parse/condition/operators/comparison/operators/equal-loose.ts new file mode 100644 index 0000000..253ed45 --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/equal-loose.ts @@ -0,0 +1,41 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; + +import isPrimitive from '../../../../../helpers/is-primitive'; +import toNumber from '../../../../../helpers/to-number'; + +export default { + name: 'equals-loose', + description: "Checks if operands are loosely equal", + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + alias: ['=='], + inverse: { + description: "Checks if operands are not loosely equal", + alias: ['!='] + }, + handle: async function(options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right } = state; + + if ( + left === right || + (left == null && right == null) || + (Number.isNaN(left) && Number.isNaN(right)) + ) { + return true; + } + + if (isPrimitive(left) && isPrimitive(right)) { + if (String(left).toLowerCase() === String(right).toLowerCase()) { + return true; + } + + const leftNum = toNumber(left); + const rightNum = toNumber(right); + + if (leftNum != null && rightNum != null) { + return leftNum === rightNum; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/equal-strict.ts b/src/parse/condition/operators/comparison/operators/equal-strict.ts new file mode 100644 index 0000000..95cb33d --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/equal-strict.ts @@ -0,0 +1,21 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; + +export default { + name: 'equal-strict', + description: "Checks if operands are strictly equal", + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + alias: ['==='], + inverse: { + description: "Checks if operands are not strictly equal", + alias: ['!=='] + }, + handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right } = state; + return ( + left === right || + (left == null && right == null) || + (Number.isNaN(left) && Number.isNaN(right)) + ); + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/exists.ts b/src/parse/condition/operators/comparison/operators/exists.ts new file mode 100644 index 0000000..ff2a6a3 --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/exists.ts @@ -0,0 +1,16 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; + +export default { + name: 'exists', + description: "Checks if operands are strictly equal", + arguments: ArgumentsQuantifier.LEFTONLY, + alias: ['exists'], + inverse: { + description: "Checks if operands are not strictly equal", + alias: ['!exists'] + }, + handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + return state.left != null && state.left !== '' && state.left !== false; + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/greater-than-equal.ts b/src/parse/condition/operators/comparison/operators/greater-than-equal.ts new file mode 100644 index 0000000..a4dcf97 --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/greater-than-equal.ts @@ -0,0 +1,24 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; +import toNumber from '../../../../../helpers/to-number'; + +export default { + name: "greater-than-or-equal", + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is numerical and greater than or equal to the right operand", + alias: ['>='], + handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right } = state; + const leftNum = toNumber(left); + if (leftNum == null) { + return; + } + + const rightNum = toNumber(right); + if (rightNum == null) { + return; + } + + return leftNum >= rightNum; + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/greater-than.ts b/src/parse/condition/operators/comparison/operators/greater-than.ts new file mode 100644 index 0000000..cd5ee1c --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/greater-than.ts @@ -0,0 +1,24 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; +import toNumber from '../../../../../helpers/to-number'; + +export default { + name: "greater-than", + description: "Checks if the left operand is numerical and greater than to the right operand", + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + alias: ['>='], + handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right } = state; + const leftNum = toNumber(left); + if (leftNum == null) { + return; + } + + const rightNum = toNumber(right); + if (rightNum == null) { + return; + } + + return leftNum > rightNum; + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/is-bool.ts b/src/parse/condition/operators/comparison/operators/is-bool.ts new file mode 100644 index 0000000..737456e --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/is-bool.ts @@ -0,0 +1,46 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; + +const toBool = (subject: unknown) => { + if (subject === true || subject === false) { + return subject; + } + + if (typeof subject === 'string') { + const subjectStr = subject.toLowerCase(); + if (subjectStr === 'true') { + return true; + } + if (subjectStr === 'false') { + return true; + } + } +} + +export default { + name: "isbool", + arguments: ArgumentsQuantifier.LEFTONLY, + description: "Checks if the left operand is boolean and if specified matches the right operand", + alias: ['isbool'], + inverse: { + description: "Checks if the left operand is not a boolean or if specified does not match right operand", + alias: ['!isbool'] + }, + handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right } = state; + + const leftBool = toBool(left); + if (leftBool == null) { + return false; + } + + if (right == null) { + return true; + } + + const rightBool = toBool(right); + if (right != null) { + return leftBool === rightBool; + } + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/is-null.ts b/src/parse/condition/operators/comparison/operators/is-null.ts new file mode 100644 index 0000000..c58441f --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/is-null.ts @@ -0,0 +1,17 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; + +export default { + name: 'isnull', + arguments: ArgumentsQuantifier.LEFTONLY, + description: "Checks if the left operand is null or undefined", + alias: ['isnull'], + inverse: { + description: "Checks if the left operand is not null or undefined", + alias: ['!isnull'] + }, + handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left } = state; + return left == null; + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/less-than-equal.ts b/src/parse/condition/operators/comparison/operators/less-than-equal.ts new file mode 100644 index 0000000..aca4706 --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/less-than-equal.ts @@ -0,0 +1,25 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; +import toNumber from '../../../../../helpers/to-number'; + +export default { + name: 'less-than-or-equal', + description: "Checks if the left operand is numerical and less than or equal to the right operand", + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + alias: ['<='], + handle: async function(options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right } = state; + + const leftNum = toNumber(left); + if (leftNum == null) { + return; + } + + const rightNum = toNumber(right); + if (rightNum == null) { + return; + } + + return leftNum <= rightNum; + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/less-than.ts b/src/parse/condition/operators/comparison/operators/less-than.ts new file mode 100644 index 0000000..299c071 --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/less-than.ts @@ -0,0 +1,25 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; +import toNumber from '../../../../../helpers/to-number'; + +export default { + name: 'less-than', + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is numerical and less than the right operand", + alias: ['<'], + handle: async function(options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right } = state; + + const leftNum = toNumber(left); + if (leftNum == null) { + return; + } + + const rightNum = toNumber(right); + if (rightNum == null) { + return; + } + + return leftNum <= rightNum; + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/numerical.ts b/src/parse/condition/operators/comparison/operators/numerical.ts new file mode 100644 index 0000000..8d1d0b4 --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/numerical.ts @@ -0,0 +1,49 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; +import toNumber from '../../../../../helpers/to-number'; + +const isRange = /^((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))-((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))$/; + +export default { + name: 'numerical', + arguments: ArgumentsQuantifier.RIGHTOPTIONAL, + description: "Checks if the left operand is numerical and if specified within the range of the right operand (inclusive)", + alias: ['isnum', 'isnumber'], + inverse: { + description: "Checks if the left operand is not numerical and if specified not within the range of the right operand (inclusive)", + alias: ['!isnum', '!isnumber'] + }, + handle: async function(options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right } = state; + + const v1 = toNumber(left); + if (v1 == null) { + return false; + } + if (right == null) { + return true; + } + + if (right == null || right === '') { + return true; + } + + if (typeof right != 'string') { + return v1 === right; + } + + const range = isRange.exec(right); + if (!range) { + return; + } + + const r1 = Number(range[1]); + const r2 = Number(range[2]); + + if (r1 > r2) { + return r2 <= v1 && v1 <= r1; + } + + return r1 <= v1 && v1 <= r2; + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/regex.ts b/src/parse/condition/operators/comparison/operators/regex.ts new file mode 100644 index 0000000..a45a97f --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/regex.ts @@ -0,0 +1,37 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; +import toText from '../../../../../helpers/to-text'; + +export default { + name: 'regex', + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is a match of the right operand regex", + alias: ['regex'], + inverse: { + description: "Checks if the left operand is not a match of the right operand regex", + alias: ['!regex'] + }, + handle: async function(options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right } = state; + + if (left == null || typeof right !== 'string') { + return; + } + + const leftText = toText(left); + if (leftText == null) { + return false; + } + + const rightText = toText(right); + if (rightText == null) { + return false; + } + + const parts = /^\/(.*)\/([a-z]*)$/i.exec(rightText); + if (parts) { + return (new RegExp(parts[1], parts[2])).test(leftText); + } + return (new RegExp(rightText)).test(leftText); + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/wildcard.ts b/src/parse/condition/operators/comparison/operators/wildcard.ts new file mode 100644 index 0000000..fa4bc7e --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/wildcard.ts @@ -0,0 +1,124 @@ +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type default as IParseOptions } from '../../../../../types/options'; +import split from '../../../../../helpers/unicode-safe-split'; +import toText from '../../../../../helpers/to-text'; + +const toRegExp = (subject: string, caseSensitive: boolean) : RegExp => { + const wc = split(subject); + let pattern = ''; + let anchorStart = true; + let anchorEnd = true; + let idx = 0; + const len = wc.length; + while (idx < len) { + + const atStart = idx === 0; + + let hasTokens = false; + let zeroOrMore = false; + let anyOneChar = 0; + + let char = wc[idx]; + while (char === '?' || char === '*') { + hasTokens = true; + if (wc[idx] === '?') { + anyOneChar += 1; + } else { + zeroOrMore = true; + } + idx += 1; + char = wc[idx]; + } + + if (hasTokens) { + pattern += '.'.repeat(anyOneChar); + if (zeroOrMore) { + const atEnd = idx === len; + + if (atStart) { + anchorStart = false; + } + if (atEnd) { + anchorEnd = false; + } + + if (!atStart && !atEnd) { + if (!anyOneChar) { + pattern += '.*'; + } else { + pattern += '+'; + } + } + } + continue; + } + + // Char needs to be escaped + if ( + char === '^' || + char === '.' || + char === '-' || + char === '+' || + char === '\\' || + char === '/' || + char === '|' || + char === '(' || + char === ')' || + char === '[' || + char === ']' || + char === '{' || + char === '}' || + char === '$' + ) { + pattern += `\\${char}`; + idx += 1; + continue; + } + + pattern += char; + idx += 1; + } + + if (anchorStart) { + pattern = '^' + pattern; + } + if (anchorEnd) { + pattern += '$'; + } + return new RegExp( + pattern, + 'u' + (!caseSensitive ? 'i' : '') + ); +}; + +export default { + name: 'wildcard', + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + description: "Checks if the left operand is a match of the right operand wildcard", + alias: ['iswm'], + cased: true, + inverse: { + description: "Checks if the left operand is not a match of the right operand wildcard", + alias: ['!iswm'] + }, + handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { + const { left, right, caseSensitive = false } = state; + + const leftText = toText(left); + if (leftText == null) { + return; + } + + const rightText = toText(right); + if (rightText == null) { + return; + } + + const v2RegExp = toRegExp(rightText, caseSensitive); + if (v2RegExp == null) { + return; + } + + return v2RegExp.test(leftText); + } +} \ No newline at end of file diff --git a/src/parse/condition/operators/index.ts b/src/parse/condition/operators/index.ts new file mode 100644 index 0000000..6063a5f --- /dev/null +++ b/src/parse/condition/operators/index.ts @@ -0,0 +1,6 @@ +export { default as comparisonOperators } from './comparison'; +export { default as logicalOperators } from './logical'; + +export { notOperator } from './logical' + +export { default as OperatorToken, type IOperatorToken, type IOperator, ArgumentsQuantifier } from './token'; \ No newline at end of file diff --git a/src/parse/condition/operators/logical/index.ts b/src/parse/condition/operators/logical/index.ts new file mode 100644 index 0000000..2a19aea --- /dev/null +++ b/src/parse/condition/operators/logical/index.ts @@ -0,0 +1,9 @@ +import operatorAnd from './operators/and'; +import operatorOr from './operators/or'; + +export { default as notOperator } from './operators/not'; + +export default new Map([ + ['&&', operatorAnd], + ['||', operatorOr] +]); \ No newline at end of file diff --git a/src/parse/condition/operators/logical/operators/and.ts b/src/parse/condition/operators/logical/operators/and.ts new file mode 100644 index 0000000..bb482bc --- /dev/null +++ b/src/parse/condition/operators/logical/operators/and.ts @@ -0,0 +1,21 @@ +import { type default as IParseOptions } from '../../../../../types/options'; +import { type IOperator, type IHandleStateDeferred, ArgumentsQuantifier } from '../../token'; + +export default { + name: 'logical-and', + description: "Checks if two conditions are truthy", + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + defer: true, + alias: ['&&'], + handle: async function (options: IParseOptions, meta: unknown, state: IHandleStateDeferred) : Promise { + const { arguments: args } = state; + + const left = await args[0].evaluate(options, meta); + if (left == null || left === false || left === 0 || left === '') { + return false; + } + + const right = await args[1].evaluate(options, meta); + return (right != null && right !== false && right !== 0 && right !== ''); + } +}; \ No newline at end of file diff --git a/src/parse/condition/operators/logical/operators/not.ts b/src/parse/condition/operators/logical/operators/not.ts new file mode 100644 index 0000000..f79b564 --- /dev/null +++ b/src/parse/condition/operators/logical/operators/not.ts @@ -0,0 +1,16 @@ +import { type default as IParseOptions } from '../../../../../types/options'; +import { type IOperator, type IHandleStateDeferred, ArgumentsQuantifier } from '../../token'; + +export default { + name: 'logical-not', + description: "Checks if two conditions are truthy", + arguments: ArgumentsQuantifier.LEFTONLY, + defer: true, + alias: [], + handle: async function (options: IParseOptions, meta: unknown, state: IHandleStateDeferred) : Promise { + const { arguments: args } = state; + + const left = await args[0].evaluate(options, meta); + return (left == null || left === false || left === 0 || left === ''); + } +}; \ No newline at end of file diff --git a/src/parse/condition/operators/logical/operators/or.ts b/src/parse/condition/operators/logical/operators/or.ts new file mode 100644 index 0000000..2cc8ff3 --- /dev/null +++ b/src/parse/condition/operators/logical/operators/or.ts @@ -0,0 +1,21 @@ +import { type default as IParseOptions } from '../../../../../types/options'; +import { type IOperator, type IHandleStateDeferred, ArgumentsQuantifier } from '../../token'; + +export default { + name: 'logical-or', + description: "Checks if two conditions are truthy", + arguments: ArgumentsQuantifier.RIGHTREQUIRED, + defer: true, + alias: ['||'], + handle: async function (options: IParseOptions, meta: unknown, state: IHandleStateDeferred) : Promise { + const { arguments: args } = state; + + const left = await args[0].evaluate(options, meta); + if (left != null && left !== false && left !== 0 && left !== '') { + return true; + } + + const right = await args[1].evaluate(options, meta); + return (right != null && right !== false && right !== 0 && right !== ''); + } +}; \ No newline at end of file diff --git a/src/parse/condition/operators/token/index.ts b/src/parse/condition/operators/token/index.ts new file mode 100644 index 0000000..a1c30b5 --- /dev/null +++ b/src/parse/condition/operators/token/index.ts @@ -0,0 +1,117 @@ +import type IParseOptions from '../../../../types/options'; +import TokenType from '../../../../types/token-types'; + +import { default as Token, type IToken } from '../../../token'; + +interface IHandleStateBase { + caseSensitive: boolean; +} +interface IHandleStateNotDeferred extends IHandleStateBase { + left: unknown; + right?: unknown; + arguments?: never; +} +export interface IHandleStateDeferred extends IHandleStateBase { + left?: never; + right?: never; + arguments: Token[] +} +export type IHandleState = IHandleStateNotDeferred | IHandleStateDeferred; + +export const enum ArgumentsQuantifier { + LEFTONLY, + RIGHTOPTIONAL, + RIGHTREQUIRED +} + +export type IHandleFn = (options: IParseOptions, meta: unknown, state: IHandleState) => Promise; + +export interface IOperator { + name: string; + description: string; + arguments: ArgumentsQuantifier; + defer: boolean; + cased?: boolean, + alias: string[]; + inverse?: { + description: string; + alias: string[]; + handle: IHandleFn; + }; + handle: IHandleFn; +} + +export interface IOperatorToken extends IToken { + caseSensitive?: boolean; + argumentsQuantifier: ArgumentsQuantifier; + arguments: Token[]; + defer?: boolean; + handle: IHandleFn; +} + +export default class OperatorToken extends Token { + public caseSensitive : boolean; + public arguments: Token[]; + public defer: boolean; + public handle : IHandleFn; + + constructor(token: IOperatorToken) { + if (token.arguments == null || !Array.isArray(token.arguments)) { + throw new Error('TODO - ExpressionError - invalid arguments list'); + } + if (token.arguments.length === 0) { + throw new Error('TODO - ExpressionError - at least one argument is required'); + } + if (token.arguments.length > 2) { + throw new Error('TODO - ExpressionError - too many arguments specified') + } + + if (token.arguments.length !== 2 && token.argumentsQuantifier === ArgumentsQuantifier.RIGHTREQUIRED) { + throw new Error('TODO - ExpressionError - right argument required'); + } + + super({ + ...token, + type: TokenType.OPERATOR + }); + + this.caseSensitive = token.caseSensitive === true; + this.defer = token.defer === true; + this.handle = token.handle; + } + + toJSON() : Record { + return { + ...(super.toJSON()), + caseSensitive: this.caseSensitive, + arguments: this.arguments.map(token => token.toJSON()) + }; + } + + async evaluate(options: IParseOptions, meta: unknown): Promise { + if (this.defer) { + if (options.verifyOnly) { + await this.arguments[0].evaluate(options, meta); + if (this.arguments.length > 1) { + await this.arguments[1].evaluate(options, meta); + } + return false; + } + + return await this.handle.call(this, options, meta, {caseSensitive: this.caseSensitive, arguments: this.arguments}); + } + + const left = await this.arguments[0].evaluate(options, meta); + + let right: unknown; + if (this.arguments.length > 1) { + right = await this.arguments[1].evaluate(options, meta); + } + + if (options.verifyOnly) { + return false; + } + + return true === await this.handle.call(this, options, meta, {left, right, caseSensitive: this.caseSensitive}); + } +} \ No newline at end of file diff --git a/src/tokenize/condition-block.ts b/src/parse/condition/tokenize/block.ts similarity index 82% rename from src/tokenize/condition-block.ts rename to src/parse/condition/tokenize/block.ts index 5368591..b28b0cc 100644 --- a/src/tokenize/condition-block.ts +++ b/src/parse/condition/tokenize/block.ts @@ -1,9 +1,9 @@ -import { consume as consumeWS } from '../helpers/whitespace'; +import { consume as consumeWS } from '../../../helpers/whitespace'; +import type ITokenizeState from '../../../types/tokenize-state'; -import type Token from '../tokens/token'; -import type ITokenizeState from '../types/tokenize-state'; +import type Token from '../../token'; -import tokenizeCondition from './condition'; +import tokenizeCondition from '.'; export default async (state: ITokenizeState) : Promise => { const { options, stack, tokens } = state; diff --git a/src/tokenize/comparison/index.ts b/src/parse/condition/tokenize/comparison.ts similarity index 66% rename from src/tokenize/comparison/index.ts rename to src/parse/condition/tokenize/comparison.ts index a25eb3d..c6978f5 100644 --- a/src/tokenize/comparison/index.ts +++ b/src/parse/condition/tokenize/comparison.ts @@ -1,14 +1,19 @@ -import type ITokenizeState from '../../types/tokenize-state'; -import { ArgumentQuantifier } from '../../types/manifest-comparison'; +import { consume as consumeWS } from '../../../helpers/whitespace'; -import { consume as consumeWS } from '../../helpers/whitespace'; +import type ITokenizeState from '../../../types/tokenize-state'; -import tokenizeOperand, { IOperandState } from './operand'; -import tokenizeComparator, { IComparatorState } from './comparator'; +import { + ArgumentsQuantifier, + type IOperator, + comparisonOperators, + OperatorToken, +} from '../operators' -import comparatorMap, { IComparator } from './comparator-map'; +import Token from '../../token'; -import Token from '../../tokens/token'; +import tokenizeOperand, { type IOperandState } from './operand'; + +import tokenizeOperator, { type IOperatorState } from './operator'; export default async (state: ITokenizeState, asArgument = true) : Promise => { const { options, stack } = state; @@ -19,7 +24,7 @@ export default async (state: ITokenizeState, asArgument = true) : Promise= tokens.length || tokens[cursor].value === ']' || - (asArgument && tokens[cursor].value) === ',' || + (asArgument && tokens[cursor].value === ',') || tokens[cursor].value === '&&' || tokens[cursor].value === '||' ) { @@ -35,7 +40,7 @@ export default async (state: ITokenizeState, asArgument = true) : Promise => { const { options, stack, operand, tokens, cursor } = state; - const mockState : IComparatorState = { + const mockState : IOperatorState = { options: { ...options }, stack: [ ...stack ], tokens: [ ...tokens ], @@ -53,12 +58,12 @@ export default async (state: ITokenizeState, asArgument = true) : Promisecomparator).arguments; - if (argQuant !== ArgumentQuantifier.LEFTONLY) { + const argQuant = (operator).arguments; + if (argQuant !== ArgumentsQuantifier.LEFTONLY) { const mockState : ITokenizeState = { options: { ...options }, stack: [ ...stack ], @@ -96,10 +101,14 @@ export default async (state: ITokenizeState, asArgument = true) : PromisemockState.output; - } else if (argQuant === ArgumentQuantifier.RIGHTREQUIRED) { + } else if (argQuant === ArgumentsQuantifier.RIGHTREQUIRED) { throw new Error('TODO - SyntaxError: Right hand expression expected'); } } + const args : Token[] = [left]; + if (right) { + args.push(right); + } if ( cursor >= tokens.length || @@ -108,10 +117,12 @@ export default async (state: ITokenizeState, asArgument = true) : Promisecomparator).tokenClass({ + state.output = new OperatorToken({ position: startPosition, - left, - right + value: (operator).name, + argumentsQuantifier: (operator).arguments, + arguments: args, + handle: (operator).handle }); return true; } diff --git a/src/tokenize/condition.ts b/src/parse/condition/tokenize/index.ts similarity index 76% rename from src/tokenize/condition.ts rename to src/parse/condition/tokenize/index.ts index feef757..af31912 100644 --- a/src/tokenize/condition.ts +++ b/src/parse/condition/tokenize/index.ts @@ -1,11 +1,11 @@ -import type ITokenizeState from '../types/tokenize-state'; +import type ITokenizeState from '../../../types/tokenize-state'; -import type Token from '../tokens/token'; +import type Token from '../../token'; +import tokenizeLogicalNot from './logical-not'; +import tokenizeBlock from './block'; import tokenizeComparison from './comparison'; -import tokenizeConditionBlock from './condition-block'; -import tokenizeConditionNot from './condition-not'; -import tokenizeLogicalOperator from './condition-logical'; +import tokenizeLogicalOperator from './logical-operator'; export default async (state: ITokenizeState, asArgument = false) : Promise => { @@ -19,8 +19,8 @@ export default async (state: ITokenizeState, asArgument = false) : PromisemockState.output; diff --git a/src/tokenize/condition-not.ts b/src/parse/condition/tokenize/logical-not.ts similarity index 60% rename from src/tokenize/condition-not.ts rename to src/parse/condition/tokenize/logical-not.ts index b76b172..cc4389d 100644 --- a/src/tokenize/condition-not.ts +++ b/src/parse/condition/tokenize/logical-not.ts @@ -1,9 +1,10 @@ -import type ITokenizeState from '../types/tokenize-state'; +import type ITokenizeState from '../../../types/tokenize-state'; -import type Token from '../tokens/token'; -import NotToken from '../tokens/logical/logical-not'; +import type Token from '../../token'; -import tokenizeConditionBlock from './condition-block'; +import { OperatorToken, notOperator } from '../operators'; + +import tokenizeBlock from './block'; export default async (state: ITokenizeState) : Promise => { @@ -29,14 +30,16 @@ export default async (state: ITokenizeState) : Promise => { cursor }; - if (await tokenizeConditionBlock(mockState)) { + if (await tokenizeBlock(mockState)) { state.tokens = mockState.tokens; state.cursor = mockState.cursor; - state.output = new NotToken({ + state.output = new OperatorToken({ position: startPosition, - left: mockState.output + value: notOperator.name, + argumentsQuantifier: notOperator.arguments, + arguments: [mockState.output], + handle: notOperator.handle }); - return true; } diff --git a/src/parse/condition/tokenize/logical-operator.ts b/src/parse/condition/tokenize/logical-operator.ts new file mode 100644 index 0000000..2af584f --- /dev/null +++ b/src/parse/condition/tokenize/logical-operator.ts @@ -0,0 +1,59 @@ +import { consume as consumeWS } from '../../../helpers/whitespace'; + +import type ITokenizeState from '../../../types/tokenize-state'; + +import type Token from '../../token'; +import { OperatorToken, logicalOperators, type IOperator } from '../operators'; + +import tokenizeLogicalNot from './logical-not'; +import tokenizeBlock from './block'; +import tokenizeComparison from './comparison'; + +export default async (state: ITokenizeState, leftCondition: Token, asArgument = false) : Promise => { + + const { options, stack } = state; + let { tokens, cursor } = state; + + cursor = consumeWS(tokens, cursor); + + const startPosition = tokens[cursor].position; + + if (!logicalOperators.has(tokens[cursor].value)) { + return false; + } + const operator = logicalOperators.get(tokens[cursor].value); + + cursor = consumeWS(tokens, cursor + 1); + + const mockState : ITokenizeState = { + options: { ...options }, + stack: [ ...stack ], + tokens: [ ...tokens ], + cursor + }; + + let rightCondition: Token; + if ( + await tokenizeLogicalNot(mockState) || + await tokenizeBlock(mockState) || + await tokenizeComparison(mockState, asArgument) + ) { + tokens = mockState.tokens; + cursor = mockState.cursor; + rightCondition = mockState.output; + + } else { + throw new Error('TODO - SyntaxError: invalid right handle conditional'); + } + + state.tokens = tokens; + state.cursor = cursor; + state.output = new OperatorToken({ + position: startPosition, + value: operator.name, + arguments: [leftCondition, rightCondition], + argumentsQuantifier: operator.arguments, + handle: operator.handle + }); + return true; +} \ No newline at end of file diff --git a/src/tokenize/comparison/operand.ts b/src/parse/condition/tokenize/operand.ts similarity index 80% rename from src/tokenize/comparison/operand.ts rename to src/parse/condition/tokenize/operand.ts index 66e270c..86baee3 100644 --- a/src/tokenize/comparison/operand.ts +++ b/src/parse/condition/tokenize/operand.ts @@ -1,18 +1,21 @@ -import TokenType from '../../types/token-types'; -import ITokenizeState from '../../types/tokenize-state'; +import TokenType from '../../../types/token-types'; +import type ITokenizeState from '../../../types/tokenize-state'; -import { consume as consumeWS, is as isWS } from '../../helpers/whitespace'; +import { consume as consumeWS, is as isWS } from '../../../helpers/whitespace'; -import tokenizeTextEscapeSingle from '../text-escape-single'; -import tokenizeTextEscapeBlock from '../text-escape-block'; -import tokenizeTextQuoted from '../text-quoted'; -import tokenizeTextSpecial from '../text-special'; -import tokenizeFunctionIf from '../function-if'; -import tokenizeFunction from '../function'; +import { + TextToken, + tokenizeEscape, + tokenizeEscapeBlock, + tokenizeQuoted, + tokenizeSpecial, +} from '../../text'; -import Token from '../../tokens/token'; -import TextToken from '../../tokens/token-text'; -import TokenList from '../../tokens/token-list'; +import { tokenizeIf } from '../../if'; +import { tokenizeFunction } from '../../function'; + +import type Token from '../../token'; +import TokenList from '../../token-list'; export interface IOperandState extends ITokenizeState { endOfOperand?: boolean; @@ -66,11 +69,11 @@ export default async ( cursor } if ( - await tokenizeTextEscapeSingle(mockState, ['"', '$', '\\', ',', ']',]) || - await tokenizeTextEscapeBlock(mockState) || - await tokenizeTextQuoted(mockState) || - await tokenizeTextSpecial(mockState) || - await tokenizeFunctionIf(mockState) || + await tokenizeEscape(mockState, ['"', '$', '\\', ',', ']',]) || + await tokenizeEscapeBlock(mockState) || + await tokenizeQuoted(mockState) || + await tokenizeSpecial(mockState) || + await tokenizeIf(mockState) || await tokenizeFunction(mockState) ) { const last = operand[operand.length - 1]; diff --git a/src/tokenize/comparison/comparator.ts b/src/parse/condition/tokenize/operator.ts similarity index 68% rename from src/tokenize/comparison/comparator.ts rename to src/parse/condition/tokenize/operator.ts index b9fa710..e05859b 100644 --- a/src/tokenize/comparison/comparator.ts +++ b/src/parse/condition/tokenize/operator.ts @@ -1,14 +1,14 @@ -import ITokenizeState from '../../types/tokenize-state'; +import type ITokenizeState from '../../../types/tokenize-state'; -import { consume as consumeWS, is as isWS } from '../../helpers/whitespace'; +import { consume as consumeWS, is as isWS } from '../../../helpers/whitespace'; -import comparatorMap, { IComparator } from './comparator-map'; +import { comparisonOperators, type IOperator } from '../operators'; -export interface IComparatorState extends Omit { - output?: IComparator +export interface IOperatorState extends Omit { + output?: IOperator } -export default async (state: IComparatorState, asArgument = true) : Promise => { +export default async (state: IOperatorState, asArgument = true) : Promise => { const { tokens } = state; let { cursor } = state; @@ -47,7 +47,7 @@ export default async (state: IComparatorState, asArgument = true) : Promise= tokens.length || tokens[cursor].value === ']' || @@ -57,7 +57,7 @@ export default async (state: IComparatorState, asArgument = true) : PromisecomparatorMap.get(compname); + state.output = comparisonOperators.get(compname); return true; } diff --git a/src/parse/expression.ts b/src/parse/expression.ts new file mode 100644 index 0000000..249d0b7 --- /dev/null +++ b/src/parse/expression.ts @@ -0,0 +1,62 @@ +import type IParseOptions from '../types/options'; +import type Token from './token'; + +export interface IExpression { + options: IParseOptions, + input: string, + tokens: Token[]; +} + +export default class Expression { + public options: IParseOptions; + public input : string; + public tokens: Token[]; + + constructor(expression: IExpression) { + this.options = expression.options; + this.input = expression.input; + this.tokens = expression.tokens; + } + + toJSON() : Record { + return { + options: { ...(this.options) }, + input: this.input, + value: this.tokens.forEach(value => value.toJSON()) + }; + } + + async evaluate(options: IParseOptions = {}, meta: unknown = {}) : Promise { + const config = { + ...(this.options), + ...options + }; + + let result = ''; + let index = 0; + while (index < this.tokens.length) { + let value = await this.tokens[index].evaluate(config, meta); + index += 1; + + if ( + options.verifyOnly || + value == null || + (typeof value === 'number' && !Number.isFinite(value)) + ) { + continue; + } + + if (typeof value !== 'string') { + value = JSON.stringify(value); + } + + result += value; + } + + if (config.verifyOnly) { + return ''; + } + + return result; + } +} \ No newline at end of file diff --git a/src/parse/function/index.ts b/src/parse/function/index.ts new file mode 100644 index 0000000..f0c9abb --- /dev/null +++ b/src/parse/function/index.ts @@ -0,0 +1,2 @@ +export { default as tokenizeFunction } from './tokenize'; +export { default as FunctionToken } from './token'; \ No newline at end of file diff --git a/src/parse/function/token/index.ts b/src/parse/function/token/index.ts new file mode 100644 index 0000000..9f778d5 --- /dev/null +++ b/src/parse/function/token/index.ts @@ -0,0 +1,139 @@ +import TokenType from '../../../types/token-types'; +import { type default as IParserOptions, type IFunctionLookup, type IFunctionHandler } from '../../../types/options'; +import Token, { type IToken } from '../../token'; + +interface IFunctionBaseToken extends IToken { + prefix: string; + value: string; + arguments: Token[]; +} + +export interface IFunctionHandlerToken extends IFunctionBaseToken { + handler: IFunctionHandler; + lookupFn?: never; +} + +interface IFunctionLookupToken extends IFunctionBaseToken { + handler?: never; + lookupFn: IFunctionLookup; +} + +export type IFunctionToken = IFunctionHandlerToken | IFunctionLookupToken; + +export default class FunctionToken extends Token { + public prefix: string; + public value: string; + public arguments: Token[]; + public handler: IFunctionHandler; + public lookupFn: IFunctionLookup; + + constructor(token: IFunctionToken) { + + if (token == null) { + throw new Error('TODO - ExpressionError: token info missing') + } + + if (typeof token.prefix !== 'string') { + throw new Error('TODO - ExpressionError: token contains invalid prefix'); + } + + if (typeof token.value !== 'string') { + throw new Error('TODO - ExpressionError: token.value must be a string'); + } + + if (!Array.isArray(token.arguments)) { + throw new Error('TODO - ExpressionError: token info contains invalid arguments') + } + + if (!token.arguments.length) { + throw new Error('TODO - ExpressionError: token info arguments is unpopulated array') + } + + if (token.handler != null) { + if (token.lookupFn != null) { + throw new Error('TODO - ExpressionError: token contains both lookupFn and handlerFn'); + } + if (typeof token.handler !== 'object') { + throw new Error('TODO - ExpressionError: specified handler must be an object'); + } + if (token.handler.evaluator == null) { + throw new Error('TODO - ExpressionError: specified handler does not have .evaluator property'); + } + if (typeof token.handler.evaluator !== 'function') { + throw new Error('TODO - ExpressionError: specified handler.evaluator property not a function'); + } + + } else if (token.lookupFn == null) { + throw new Error('TODO - ExpressionError: token info not contain lookupFn or handlerFn'); + + } else if (typeof token.lookupFn !== 'function') { + throw new Error('TODO - ExpressionError: specified lookupFn must be a function'); + } + + super({ + type: TokenType.FUNCTIONAL, + ...token + }); + this.prefix = '' + token.prefix; + this.arguments = token.arguments; + + if (token.handler != null) { + this.handler = token.handler; + + } else { + this.lookupFn = token.lookupFn; + } + } + + toJSON() : Record { + return { + ...(super.toJSON()), + prefix: this.prefix, + arguments: this.arguments.map(value => value.toJSON()) + } + } + + async evaluate(options: IParserOptions, meta: unknown) : Promise { + if (options == null) { + options = {}; + } + if (meta == null) { + meta = {}; + } + + let handler : IFunctionHandler; + if (this.handler) { + handler = this.handler; + + } else { + handler = await this.lookupFn(this.value); + } + + if (handler == null) { + throw new Error(`TODO - ExpressionError: No handler for ${this.prefix}${this.value}`); + } + + const args : unknown[] = []; + if (this.arguments != null) { + const argList = this.arguments; + for (let idx = 0; idx < argList.length; idx += 1) { + const arg = await argList[idx].evaluate(options, meta); + args.push(arg); + } + } + + if (options.verifyOnly) { + return; + } + + if (!options.skipArgumentsCheck && handler.argsCheck != null) { + try { + await handler.argsCheck.call(this, meta, args); + } catch (err) { + throw new Error(`TODO - ArgumentsError: ${err.message}`); + } + } + + return handler.evaluator.call(this, meta, ...args); + } +} \ No newline at end of file diff --git a/src/tokenize/function.ts b/src/parse/function/tokenize/index.ts similarity index 62% rename from src/tokenize/function.ts rename to src/parse/function/tokenize/index.ts index 833341a..4f4ab93 100644 --- a/src/tokenize/function.ts +++ b/src/parse/function/tokenize/index.ts @@ -1,18 +1,16 @@ -import has from '../helpers/has'; +import has from '../../../helpers/has'; -import { - type IFunctionLookup -} from '../types/options'; +import { type IFunctionLookup, type IFunctionHandler } from '../../../types/options'; -import type ITokenizeState from '../types/tokenize-state'; +import type ITokenizeState from '../../../types/tokenize-state'; -import type Token from '../tokens/token'; -import FunctionalToken from '../tokens/token-function'; +import type Token from '../../token'; +import FunctionalToken from '../token'; -import tokenizeArgumentList from './argument-list'; +import { tokenizeArgumentsList } from '../../argument-list'; export default async (state: ITokenizeState) : Promise => { - const { tokens, stack, options} = state; + const { tokens, stack, options } = state; let cursor = state.cursor; if (tokens[cursor]?.value !== '$' || tokens[cursor + 1] == null) { @@ -29,14 +27,14 @@ export default async (state: ITokenizeState) : Promise => { cursor += 1; if (has(options.functionHandlers, varname)) { - const handler = options.functionHandlers[varname]; - lookupFn = () => handler; + const handler : IFunctionHandler = (>options.functionHandlers)[varname]; + lookupFn = async () => handler; } else if ( cursor >= tokens.length && has(options.functionLookups, varname) ) { - lookupFn = options.functionLookups[varname] + lookupFn = (>options.functionLookups)[varname] prefix += varname; varname = tokens[cursor].value.toLowerCase(); cursor += 1; @@ -52,7 +50,7 @@ export default async (state: ITokenizeState) : Promise => { stack: [ ...stack, `${prefix}${varname}`] } - await tokenizeArgumentList(mockState); + await tokenizeArgumentsList(mockState); state.tokens = mockState.tokens; state.output = new FunctionalToken({ @@ -60,7 +58,7 @@ export default async (state: ITokenizeState) : Promise => { prefix, value: varname, arguments: (mockState.output || []), - lookupFn: lookupFn + lookupFn }); state.cursor = mockState.cursor; diff --git a/src/parse/if/index.ts b/src/parse/if/index.ts new file mode 100644 index 0000000..0520a30 --- /dev/null +++ b/src/parse/if/index.ts @@ -0,0 +1,2 @@ +export { default as tokenizeIf } from './tokenize'; +export { default as IfToken } from './token'; \ No newline at end of file diff --git a/src/tokens/token-function-if.ts b/src/parse/if/token/index.ts similarity index 56% rename from src/tokens/token-function-if.ts rename to src/parse/if/token/index.ts index 10993e8..3456264 100644 --- a/src/tokens/token-function-if.ts +++ b/src/parse/if/token/index.ts @@ -1,8 +1,9 @@ -import TokenType from '../types/token-types'; -import ParserOptions from '../types/options'; +import TokenType from '../../../types/token-types'; +import type IParserOptions from '../../../types/options'; -import Token from './token'; -import OperatorToken from './token-operator'; +import Token from '../../token'; + +import { type OperatorToken } from '../../condition'; export interface IIfStatementToken { position: number; @@ -27,23 +28,32 @@ export default class IfStatementToken extends Token { this.whenFalse = token.whenFalse; } - async evaluate(options: ParserOptions, meta: unknown) : Promise { - const res = await this.condition.evaluate(options, meta); + async evaluate(options: IParserOptions, meta: unknown) : Promise { + if (options == null) { + options = {}; + } + + if (meta == null) { + meta = {}; + } + + const res = await this.condition.evaluate(options, {...(meta) }); if (options.verifyOnly) { - await this.whenTrue.evaluate(options, meta); + await this.whenTrue.evaluate(options, {...(meta) }); + if (this.whenFalse != null) { - await this.whenFalse.evaluate(options, meta); + await this.whenFalse.evaluate(options, {...(meta) }); } return; } if (res != null && res !== false) { - return this.whenTrue.evaluate(options, meta); + return this.whenTrue.evaluate(options, {...(meta) }); } if (this.whenFalse != null) { - return this.whenFalse.evaluate(options, meta); + return this.whenFalse.evaluate(options, {...(meta) }); } } diff --git a/src/tokenize/function-if.ts b/src/parse/if/tokenize/index.ts similarity index 78% rename from src/tokenize/function-if.ts rename to src/parse/if/tokenize/index.ts index 764a137..4ce2542 100644 --- a/src/tokenize/function-if.ts +++ b/src/parse/if/tokenize/index.ts @@ -1,13 +1,13 @@ -import TokenType from '../types/token-types'; +import TokenType from '../../../types/token-types'; +import type ITokenizeState from '../../../types/tokenize-state'; +import { ExpressionSyntaxError } from '../../../errors'; -import Token from '../tokens/token'; -import IfToken from '../tokens/token-function-if'; -import type OperatorToken from '../tokens/token-operator'; +import Token from '../../token'; +import IfToken from '../token'; -import type ITokenizeState from '../types/tokenize-state'; -import tokenizeArgumentList from './argument-list'; +import { type OperatorToken } from '../../condition'; -import { ExpressionSyntaxError } from '../errors'; +import { tokenizeArgumentsList } from '../../argument-list'; export default async (state: ITokenizeState) : Promise => { const { tokens, stack, options } = state; @@ -30,7 +30,7 @@ export default async (state: ITokenizeState) : Promise => { cursor, stack: [...stack, '$if'] } - await tokenizeArgumentList(mockState, true); + await tokenizeArgumentsList(mockState, true); const args = mockState.output; diff --git a/src/parse/index.ts b/src/parse/index.ts new file mode 100644 index 0000000..561a9ec --- /dev/null +++ b/src/parse/index.ts @@ -0,0 +1,81 @@ +import type IParseOptions from '../types/options'; +import type ITokenizeState from '../types/tokenize-state'; +import TokenType from '../types/token-types'; +import getPotentialTokens from '../helpers/get-potential-tokens'; + +import type Token from './token'; +import Expression from './expression'; + +import { + TextToken, + tokenizeEscape, + tokenizeEscapeBlock, + tokenizeQuoted, + tokenizeSpecial +} from './text'; + +import { tokenizeIf } from './if' +import { tokenizeFunction } from './function'; + +export default async (options: IParseOptions, subject: string) : Promise => { + if (subject == null || typeof subject !== 'string') { + throw new TypeError('input must be a string'); + } + + let tokens = getPotentialTokens(options, subject); + let cursor = 0; + + const result : Token[] = []; + + while (cursor < tokens.length) { + const mockState : ITokenizeState = { + options: { ...options }, + stack: [], + tokens: [ ...tokens ], + cursor + }; + + if ( + await tokenizeEscape(mockState) || + await tokenizeEscapeBlock(mockState) || + await tokenizeQuoted(mockState) || + await tokenizeSpecial(mockState) || + await tokenizeIf(mockState) || + await tokenizeFunction(mockState) + ) { + const lastToken : Token = result[result.length - 1]; + if ( + lastToken != null && + lastToken.type === TokenType.TEXT && + mockState.output && + (mockState.output).type === TokenType.TEXT + ) { + (lastToken.value) += (mockState.output).value; + + } else { + result.push(mockState.output); + } + + tokens = mockState.tokens; + cursor = mockState.cursor; + continue; + } + + // Assume anything else is plain text + const last : Token = result[result.length - 1]; + if (last != null && last.type === TokenType.TEXT) { + last.value += tokens[cursor].value; + + } else { + result.push(new TextToken(tokens[cursor])); + } + + cursor += 1; + } + + return new Expression({ + options: options, + input: subject, + tokens: result + }); +} \ No newline at end of file diff --git a/src/parse/text/index.ts b/src/parse/text/index.ts new file mode 100644 index 0000000..a4cc728 --- /dev/null +++ b/src/parse/text/index.ts @@ -0,0 +1,6 @@ +export { default as TextToken, type ITextToken } from './token'; + +export { default as tokenizeEscape } from './tokenize/escape-single'; +export { default as tokenizeEscapeBlock } from './tokenize/escape-block'; +export { default as tokenizeQuoted } from './tokenize/quoted'; +export { default as tokenizeSpecial } from './tokenize/special'; \ No newline at end of file diff --git a/src/tokens/token-text.ts b/src/parse/text/token/index.ts similarity index 65% rename from src/tokens/token-text.ts rename to src/parse/text/token/index.ts index 6b5b549..6bda7e6 100644 --- a/src/tokens/token-text.ts +++ b/src/parse/text/token/index.ts @@ -1,10 +1,9 @@ -import type ParserOptions from '../types/options'; -import TokenType from '../types/token-types'; +import type ParserOptions from '../../../types/options'; +import TokenType from '../../../types/token-types'; -import Token from './token'; +import Token, { type IToken } from '../../token'; -export interface ITextToken { - position?: number; +export interface ITextToken extends IToken { value?: string; } @@ -20,6 +19,6 @@ export default class TextToken extends Token { /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ async evaluate(options: ParserOptions, meta: unknown) : Promise { - return this.value == null ? '' : this.value; + return this.value; } } \ No newline at end of file diff --git a/src/tokenize/text-escape-block.ts b/src/parse/text/tokenize/escape-block.ts similarity index 79% rename from src/tokenize/text-escape-block.ts rename to src/parse/text/tokenize/escape-block.ts index 4ede000..1dd047d 100644 --- a/src/tokenize/text-escape-block.ts +++ b/src/parse/text/tokenize/escape-block.ts @@ -1,14 +1,15 @@ -import TokenType from '../types/token-types'; +import TokenType from '../../../types/token-types'; +import type ITokenizeState from '../../../types/tokenize-state'; -import type Token from '../tokens/token'; -import TokenList from '../tokens/token-list'; -import TextToken from '../tokens/token-text'; +import type Token from '../../token'; +import TokenList from '../../token-list'; -import tokenizeFunctionIf from './function-if'; -import tokenizeFunction from './function'; +import TextToken from '../token'; -import type ITokenizeState from '../types/tokenize-state'; -import { ExpressionSyntaxError } from '../errors'; +import { tokenizeIf } from '../../if'; +import { tokenizeFunction } from '../../function'; + +import { ExpressionSyntaxError } from '../../../errors'; export default async (state: ITokenizeState) : Promise => { let { tokens, cursor } = state; @@ -37,7 +38,7 @@ export default async (state: ITokenizeState) : Promise => { }; if ( - await tokenizeFunctionIf(mockState) || + await tokenizeIf(mockState) || await tokenizeFunction(mockState) ) { if (mockState.output) { diff --git a/src/tokenize/text-escape-single.ts b/src/parse/text/tokenize/escape-single.ts similarity index 84% rename from src/tokenize/text-escape-single.ts rename to src/parse/text/tokenize/escape-single.ts index acf1075..cbf3c34 100644 --- a/src/tokenize/text-escape-single.ts +++ b/src/parse/text/tokenize/escape-single.ts @@ -1,6 +1,6 @@ -import TextToken from '../tokens/token-text'; +import type ITokenizeState from '../../../types/tokenize-state'; -import type ITokenizeState from '../types/tokenize-state'; +import TextToken from '../token'; export default async (state: ITokenizeState, characters?: string[]) : Promise => { const { tokens, cursor } = state; diff --git a/src/tokenize/text-quoted.ts b/src/parse/text/tokenize/quoted.ts similarity index 81% rename from src/tokenize/text-quoted.ts rename to src/parse/text/tokenize/quoted.ts index 0d3a445..70065b7 100644 --- a/src/tokenize/text-quoted.ts +++ b/src/parse/text/tokenize/quoted.ts @@ -1,17 +1,18 @@ -import TokenType from '../types/token-types'; +import TokenType from '../../../types/token-types'; +import type ITokenizeState from '../../../types/tokenize-state'; -import type Token from '../tokens/token'; -import TokenList from '../tokens/token-list'; -import TextToken from '../tokens/token-text'; +import { ExpressionSyntaxError } from '../../../errors'; -import tokenizeEscapeSingle from './text-escape-single'; -import tokenizeTextSpecial from './text-special'; -import tokenizeFunctionIf from './function-if'; -import tokenizeFunction from './function'; +import type Token from '../../token'; +import TokenList from '../../token-list'; -import type ITokenizeState from '../types/tokenize-state'; +import TextToken from '../token'; -import { ExpressionSyntaxError } from '../errors'; +import tokenizeEscapeSingle from './escape-single'; +import tokenizeTextSpecial from './special'; + +import { tokenizeIf } from '../../if'; +import { tokenizeFunction } from '../../function'; export default async (state: ITokenizeState) : Promise => { @@ -47,7 +48,7 @@ export default async (state: ITokenizeState) : Promise => { if ( await tokenizeEscapeSingle(mockState, ['\\', '"']) || await tokenizeTextSpecial(mockState) || - await tokenizeFunctionIf(mockState) || + await tokenizeIf(mockState) || await tokenizeFunction(mockState) ) { diff --git a/src/tokenize/text-special.ts b/src/parse/text/tokenize/special.ts similarity index 82% rename from src/tokenize/text-special.ts rename to src/parse/text/tokenize/special.ts index eb309eb..788e4e0 100644 --- a/src/tokenize/text-special.ts +++ b/src/parse/text/tokenize/special.ts @@ -1,7 +1,8 @@ -import has from '../helpers/has'; -import type ITokenizeState from '../types/tokenize-state'; +import has from '../../../helpers/has'; -import TextToken from '../tokens/token-text'; +import type ITokenizeState from '../../../types/tokenize-state'; + +import TextToken from '../token'; export default async (state: ITokenizeState) : Promise => { if (!state.options.specialSequences) { diff --git a/src/tokens/token-list.ts b/src/parse/token-list.ts similarity index 57% rename from src/tokens/token-list.ts rename to src/parse/token-list.ts index a739eb1..d2bcbd5 100644 --- a/src/tokens/token-list.ts +++ b/src/parse/token-list.ts @@ -1,12 +1,11 @@ import TokenType from '../types/token-types'; -import ParserOptions from '../types/options'; +import type IParserOptions from '../types/options'; import toText from '../helpers/to-text'; -import Token from './token'; +import Token, { type IToken } from './token'; -export interface ITokenList { - position: number; +export interface ITokenList extends IToken { value: Token[]; } @@ -14,18 +13,45 @@ export default class TokenList extends Token { public value : Token[]; constructor(token: ITokenList) { + if (token == null) { + throw new Error('TODO - ExpressionError: token not specified'); + } + if (typeof token !== 'object') { + throw new Error('TODO - ExpressionError: token must be an object'); + } + if (token.value == null) { + throw new Error('TODO - ExpressionError: token list not specified'); + } + if (!Array.isArray(token.value)) { + throw new Error('TODO - ExpressionError: token list must be an array') + } + super({ ...token, type: TokenType.TOKENLIST }); } - async evaluate(options: ParserOptions, meta: unknown): Promise { + toJSON() : Record { + return { + ...(super.toJSON()), + value: this.value.map(value => value.toJSON()) + }; + } + + async evaluate(options: IParserOptions, meta: unknown): Promise { + if (options == null) { + options = {}; + } + if (meta == null) { + meta = {}; + } + const parts = this.value; let res : unknown; for (let idx = 0; idx < parts.length; idx += 1) { - const value = await parts[idx].evaluate(options, meta); + const value = await parts[idx].evaluate(options, {...(meta) }); if (options.verifyOnly) { continue; @@ -61,11 +87,4 @@ export default class TokenList extends Token { return res; } - - toToken() : Record { - return { - ...(super.toJSON()), - value: this.value.map(value => value.toJSON()) - }; - } } \ No newline at end of file diff --git a/src/tokens/token.ts b/src/parse/token.ts similarity index 72% rename from src/tokens/token.ts rename to src/parse/token.ts index d8b0986..43960b7 100644 --- a/src/tokens/token.ts +++ b/src/parse/token.ts @@ -1,4 +1,4 @@ -import type ParserOptions from '../types/options'; +import type IParserOptions from '../types/options'; import TokenType from '../types/token-types'; @@ -34,7 +34,10 @@ export default class Token { } /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - async evaluate(options: ParserOptions, meta: unknown) : Promise { - throw new Error('TODO - ExpressionError: token does not implement evaluate function'); + async evaluate(options: IParserOptions, meta: unknown) : Promise { + if (this.type !== TokenType.EMPTY) { + throw new Error('TODO - ExpressionError: token does not implement evaluate function'); + } + return; } } \ No newline at end of file diff --git a/src/tokenize/comparison/comparator-map.ts b/src/tokenize/comparison/comparator-map.ts deleted file mode 100644 index 7bf2d97..0000000 --- a/src/tokenize/comparison/comparator-map.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { ArgumentQuantifier, type default as Manifest } from '../../types/manifest-comparison'; - -import ComparisonToken from '../../tokens/comparison/base'; -import { default as ContainsToken, manifest as containsManifest } from '../../tokens/comparison/contains'; -import { default as EqualLooseToken, manifest as equalLooseManifest } from '../../tokens/comparison/equal-loose'; -import { default as EqualStrictToken, manifest as equalStrictManifest } from '../../tokens/comparison/equal-strict'; -import { default as ExistsToken, manifest as existsManifest } from '../../tokens/comparison/exists'; -import { default as GreaterThanEqualToken, manifest as greaterThanEqualManifest } from '../../tokens/comparison/greater-than-equal'; -import { default as GreaterThanToken, manifest as greaterThanManifest } from '../../tokens/comparison/greater-than'; -import { default as IsBoolToken, manifest as isBoolManifest } from '../../tokens/comparison/isbool'; -import { default as IsNullToken, manifest as isNullManifest } from '../../tokens/comparison/isnull'; -import { default as LessThanEqualToken, manifest as lessThanEqualManifest } from '../../tokens/comparison/less-than-equal'; -import { default as LessThanToken, manifest as lessThanManifest } from '../../tokens/comparison/less-than'; -import { default as NumericalToken, manifest as numericalManifest } from '../../tokens/comparison/numerical'; -import { default as RegexToken, manifest as regexManifest } from '../../tokens/comparison/regex'; -import { default as WildcardToken, manifest as wildcardManifest } from '../../tokens/comparison/wildcard'; - -interface Type extends Function { - //eslint-disable-next-line @typescript-eslint/no-explicit-any - new (...args: any[]): T; -} - -interface ITokenBase { - invert?: boolean; - caseSensitive?: void | boolean; -} - -export interface IComparator { - tokenClass: Type; - arguments: ArgumentQuantifier; - description: string; - tokenBase?: ITokenBase; -} - -const comparatorMap : Map = new Map(); - -((...args: Array<[Type, Manifest]>) => { - args.forEach((arg) => { - - const [TokenClass, manifest] = arg; - - manifest.alias.forEach(alias => { - alias = alias.toLowerCase(); - - comparatorMap.set( - alias, - { - description: manifest.description, - arguments: manifest.arguments, - tokenClass: TokenClass, - tokenBase: { invert: false } - } - ); - - if (manifest.casing) { - comparatorMap.set( - alias + 'cs', - { - description: manifest.description, - arguments: manifest.arguments, - tokenClass: TokenClass, - tokenBase: { invert: false, caseSensitive: true } - } - ); - } - }); - - if (manifest.inverse !== false) { - const { alias, description } = manifest.inverse; - - alias.forEach(alias => { - alias = alias.toLowerCase(); - comparatorMap.set( - alias, - { - description: description, - arguments: manifest.arguments, - tokenClass: TokenClass, - tokenBase: { invert: true } - } - ); - - if (manifest.casing) { - comparatorMap.set( - alias + 'cs', - { - description: description, - arguments: manifest.arguments, - tokenClass: TokenClass, - tokenBase: { invert: false, caseSensitive: true } - } - ); - } - }); - } - }); -})( - [ContainsToken, containsManifest], - [EqualLooseToken, equalLooseManifest], - [EqualStrictToken, equalStrictManifest], - [ExistsToken, existsManifest], - [GreaterThanEqualToken, greaterThanEqualManifest], - [GreaterThanToken, greaterThanManifest], - [IsBoolToken, isBoolManifest], - [IsNullToken, isNullManifest], - [LessThanEqualToken, lessThanEqualManifest], - [LessThanToken, lessThanManifest], - [NumericalToken, numericalManifest], - [RegexToken, regexManifest], - [WildcardToken, wildcardManifest] -); - -export default comparatorMap; \ No newline at end of file diff --git a/src/tokenize/condition-logical.ts b/src/tokenize/condition-logical.ts deleted file mode 100644 index 3241a17..0000000 --- a/src/tokenize/condition-logical.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { consume as consumeWS } from '../helpers/whitespace'; - -import type ITokenizeState from '../types/tokenize-state'; - -import type Token from '../tokens/token'; -import type LogicalToken from '../tokens/logical/base'; -import AndLogicalToken from '../tokens/logical/logical-and'; -import OrLogicalToken from '../tokens/logical/logical-or'; - -import tokenizeConditionalNot from './condition-not'; -import tokenizeConditionBlock from './condition-block'; -import tokenizeComparison from './comparison/index'; - -interface Type extends Function { - //eslint-disable-next-line @typescript-eslint/no-explicit-any - new (...args: any[]): T; -} - -export default async (state: ITokenizeState, leftCondition: Token, asArgument = false) : Promise => { - - const { options, stack } = state; - let { tokens, cursor } = state; - - cursor = consumeWS(tokens, cursor); - - const startPosition = tokens[cursor].position; - - if ( - tokens[cursor].value !== '&&' && - tokens[cursor].value !== '||' - ) { - return false; - } - const TokenClass : Type = (tokens[cursor].value === '||' ? OrLogicalToken : AndLogicalToken); - - cursor = consumeWS(tokens, cursor + 1); - - const mockState : ITokenizeState = { - options: { ...options }, - stack: [ ...stack ], - tokens: [ ...tokens ], - cursor - }; - - let rightCondition: Token; - if ( - await tokenizeConditionalNot(mockState) || - await tokenizeConditionBlock(mockState) || - await tokenizeComparison(mockState, asArgument) - ) { - tokens = mockState.tokens; - cursor = mockState.cursor; - rightCondition = mockState.output; - - } else { - throw new Error('TODO - SyntaxError: invalid right handle conditional'); - } - - state.tokens = tokens; - state.cursor = cursor; - state.output = new TokenClass({ - position: startPosition, - left: leftCondition, - right: rightCondition - }); - return true; -} \ No newline at end of file diff --git a/src/tokens/comparison/base.ts b/src/tokens/comparison/base.ts deleted file mode 100644 index dfb47ba..0000000 --- a/src/tokens/comparison/base.ts +++ /dev/null @@ -1,48 +0,0 @@ -import ParserOptions from '../../types/options'; -import TokenType from '../../types/token-types'; - -import OperatorToken, { IOperatorToken } from '../token-operator'; - -export interface IComparisonToken extends IOperatorToken { - invert?: boolean; - value: unknown; -} - -export default class ComparisonToken extends OperatorToken { - protected invert = false; - - constructor(token: IComparisonToken) { - super({ - ...token, - type: TokenType.COMPARISON - }); - - if (token.invert) { - this.invert = true; - } - } - - /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - async handle(options: ParserOptions, meta: unknown) : Promise { - return false; - } - - async handleInverse(options: ParserOptions, meta: unknown) : Promise { - const result = await this.handle(options, meta); - return !result; - } - - async evaluate(options: ParserOptions, meta: unknown) : Promise { - if (this.invert) { - return this.handleInverse(options, meta); - } - return this.handle(options, meta); - } - - toToken() : object { - return { - ...(super.toJSON()), - invert: this.invert - } - } -} \ No newline at end of file diff --git a/src/tokens/comparison/contains.ts b/src/tokens/comparison/contains.ts deleted file mode 100644 index 45cc8c9..0000000 --- a/src/tokens/comparison/contains.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTREQUIRED, - description: "Checks if the left operand contains the right operand", - casing: true, - alias: ['contains'], - inverse: { - description: "Checks if operands are not loosely equal", - alias: ['!contains'] - } -}; - -export interface IContainsToken extends IComparisonToken { - caseSensitive: void | boolean; -} - -export default class ContainsToken extends ComparisonToken { - private caseSensitive: void | boolean; - - constructor(token: IContainsToken) { - super({ - ...token, - value: 'contains' - }); - this.caseSensitive = token.caseSensitive; - } - - async handle(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom error - throw new Error('TODO'); - } - - const v1 = await this.left.evaluate(options, meta); - const v2 = await this.right.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - if (typeof v1 === 'string') { - if (typeof v2 !== 'string') { - return false; - } - if (this.caseSensitive) { - return v1.includes(v2); - } - return v1.toLowerCase().includes(v2.toLowerCase()) - } - - if (!Array.isArray(v1)) { - return false; - } - - return v1.some((value: unknown) => { - if (typeof value === 'string') { - if (typeof v2 !== 'string') { - return false; - } - if (this.caseSensitive) { - return value === v2; - } - return value.toLowerCase() === v2.toLowerCase(); - } - return v1 === v2; - }); - } -} \ No newline at end of file diff --git a/src/tokens/comparison/equal-loose.ts b/src/tokens/comparison/equal-loose.ts deleted file mode 100644 index f749266..0000000 --- a/src/tokens/comparison/equal-loose.ts +++ /dev/null @@ -1,64 +0,0 @@ -import toNumber from '../../helpers/to-number'; -import isPrimitive from '../../helpers/is-primitive'; - -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTREQUIRED, - description: "Checks if operands are loosely equal", - alias: ['=='], - inverse: { - description: "Checks if operands are not loosely equal", - alias: ['!='] - } -}; - -export default class EqualLooseToken extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'equal-loose' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - - if (this.right == null) { - // TODO - custom error - throw new Error('TODO'); - } - - const v1 = await this.left.evaluate(options, meta); - const v2 = await this.right.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - if ( - v1 === v2 || - (v1 == null && v2 == null) || - (Number.isNaN(v1) && Number.isNaN(v2)) - ) { - return true; - } - - if (isPrimitive(v1) && isPrimitive(v2)) { - if (String(v1).toLowerCase() === String(v2).toLowerCase()) { - return true; - } - - const v1Num = toNumber(v1); - const v2Num = toNumber(v2); - - if (v1Num != null && v2Num != null) { - return v1Num === v2Num; - } - } - return false; - } -} \ No newline at end of file diff --git a/src/tokens/comparison/equal-strict.ts b/src/tokens/comparison/equal-strict.ts deleted file mode 100644 index fde048d..0000000 --- a/src/tokens/comparison/equal-strict.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTREQUIRED, - description: "Checks if operands are strictly equal", - alias: ['==='], - inverse: { - description: "Checks if operands are not strictly equal", - alias: ['!=='] - } -}; - -export default class EqualStrictToken extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'equal' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom error - throw new Error('TODO - Evaluation Error: Right hand argument missing'); - } - - const v1 = await this.left.evaluate(options, meta); - const v2 = await this.right.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - return ( - v1 === v2 || - (v1 == null && v2 == null) || - (Number.isNaN(v1) && Number.isNaN(v2)) - ); - } -} \ No newline at end of file diff --git a/src/tokens/comparison/exists.ts b/src/tokens/comparison/exists.ts deleted file mode 100644 index 4967715..0000000 --- a/src/tokens/comparison/exists.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.LEFTONLY, - description: "Checks if operand is not null, false or empty string", - alias: ['exists'], - inverse: { - description: "Checks if operand is null, false or empty string", - alias: ['!exists'] - } -}; - -export default class ExistsToken extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'exists' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - const v1 = await this.left.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - return v1 != null && v1 !== ''; - } -} \ No newline at end of file diff --git a/src/tokens/comparison/greater-than-equal.ts b/src/tokens/comparison/greater-than-equal.ts deleted file mode 100644 index c38f19b..0000000 --- a/src/tokens/comparison/greater-than-equal.ts +++ /dev/null @@ -1,48 +0,0 @@ -import toNumber from '../../helpers/to-number'; - -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTREQUIRED, - description: "Checks if the left operand is numerical and greater than or equal to the right operand", - alias: ['>='], - inverse: false -}; - -export default class GreaterThanEqualToken extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'greater-than-or-equal' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom error - throw new Error('TODO - Evaluation Error: Right hand argument missing'); - } - - let v1 = await this.left.evaluate(options, meta); - let v2 = await this.right.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - v1 = toNumber(v1); - if (v1 == null) { - return false; - } - - v2 = toNumber(v2); - if (v2 == null) { - return false; - } - - return v1 >= v2; - } -} \ No newline at end of file diff --git a/src/tokens/comparison/greater-than.ts b/src/tokens/comparison/greater-than.ts deleted file mode 100644 index 669b628..0000000 --- a/src/tokens/comparison/greater-than.ts +++ /dev/null @@ -1,48 +0,0 @@ -import toNumber from '../../helpers/to-number'; - -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTREQUIRED, - description: "Checks if the left operand is numerical and greater than the right operand", - alias: ['>='], - inverse: false -}; - -export default class GreaterThanToken extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'greater-than' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom error - throw new Error('TODO - Evaluation Error: Right hand argument missing'); - } - - let v1 = await this.left.evaluate(options, meta); - let v2 = await this.right.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - v1 = toNumber(v1); - if (v1 == null) { - return false; - } - - v2 = toNumber(v2); - if (v2 == null) { - return false; - } - - return v1 > v2; - } -} \ No newline at end of file diff --git a/src/tokens/comparison/isbool.ts b/src/tokens/comparison/isbool.ts deleted file mode 100644 index dc0dd3b..0000000 --- a/src/tokens/comparison/isbool.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export interface IIsToken extends IComparisonToken { - against: null | boolean; -} - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.LEFTONLY, - description: "Checks if the left operand is boolean", - alias: ['isbool'], - inverse: { - description: "Checks if the left operand is not a boolean", - alias: ['!isbool'] - } -}; - - -export default class IsToken extends ComparisonToken { - public against : null | boolean; - - constructor(token: IIsToken) { - super({ - ...token, - value: 'isbool' - }); - - this.against = token.against; - } - - async handle(options: ParserOptions, meta: unknown): Promise { - const v1 = await this.left.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - return v1 === this.against; - } - - toToken(): object { - return { - ...(super.toToken()), - against: this.against - } - } -} \ No newline at end of file diff --git a/src/tokens/comparison/isnull.ts b/src/tokens/comparison/isnull.ts deleted file mode 100644 index aeaf07f..0000000 --- a/src/tokens/comparison/isnull.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.LEFTONLY, - description: "Checks if the left operand is null or undefined", - alias: ['isnull'], - inverse: { - description: "Checks if the left operand is not null or undefined", - alias: ['!isnull'] - } -}; - - -export default class IsNull extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'isnull' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - const v1 = await this.left.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - return v1 == null; - } -} \ No newline at end of file diff --git a/src/tokens/comparison/less-than-equal.ts b/src/tokens/comparison/less-than-equal.ts deleted file mode 100644 index 7eab84a..0000000 --- a/src/tokens/comparison/less-than-equal.ts +++ /dev/null @@ -1,48 +0,0 @@ -import toNumber from '../../helpers/to-number'; - -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTREQUIRED, - description: "Checks if the left operand is numerical and less than or equal to the right operand", - alias: ['<='], - inverse: false -}; - -export default class LessThanEqualToken extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'less-than-or-equal' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom error - throw new Error('TODO - Evaluation Error: Right hand argument missing'); - } - - let v1 = await this.left.evaluate(options, meta); - let v2 = await this.right.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - v1 = toNumber(v1); - if (v1 == null) { - return false; - } - - v2 = toNumber(v2); - if (v2 == null) { - return false; - } - - return v1 <= v2; - } -} \ No newline at end of file diff --git a/src/tokens/comparison/less-than.ts b/src/tokens/comparison/less-than.ts deleted file mode 100644 index 6e07750..0000000 --- a/src/tokens/comparison/less-than.ts +++ /dev/null @@ -1,48 +0,0 @@ -import toNumber from '../../helpers/to-number'; - -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTREQUIRED, - description: "Checks if the left operand is numerical and less than the right operand", - alias: ['<'], - inverse: false -}; - -export default class LessThanToken extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'less-than' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom error - throw new Error('TODO - Evaluation Error: Right hand argument missing'); - } - - let v1 = await this.left.evaluate(options, meta); - let v2 = await this.right.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - v1 = toNumber(v1); - if (v1 == null) { - return false; - } - - v2 = toNumber(v2); - if (v2 == null) { - return false; - } - - return v1 < v2; - } -} \ No newline at end of file diff --git a/src/tokens/comparison/numerical.ts b/src/tokens/comparison/numerical.ts deleted file mode 100644 index 70a15cf..0000000 --- a/src/tokens/comparison/numerical.ts +++ /dev/null @@ -1,69 +0,0 @@ -import toNumber from '../../helpers/to-number'; - -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -const isRange = /^((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))-((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))$/; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTOPTIONAL, - description: "Checks if the left operand is numerical and if specified within the range of the right operand (inclusive)", - alias: ['isnum', 'isnumber'], - inverse: { - description: "Checks if the left operand is not numerical and if specified not within the range of the right operand (inclusive)", - alias: ['!isnum', '!isnumber'] - } -}; - -export default class LessThanToken extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'numerical' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - let v1 = await this.left.evaluate(options, meta); - - if (options.verifyOnly) { - if (this.right != null) { - await this.right.evaluate(options, meta); - } - return false; - } - - v1 = toNumber(v1); - if (v1 == null) { - return false; - } - if (this.right == null) { - return true; - } - - const v2 = await this.right.evaluate(options, meta); - if (v2 == null || v2 === '') { - return true; - } - - if (typeof v2 != 'string') { - return v1 === v2; - } - - const range = isRange.exec(v2); - if (!range) { - return false; - } - - const r1 = Number(range[1]); - const r2 = Number(range[2]); - - if (r1 > r2) { - return r2 <= v1 && v1 <= r1; - } - - return r1 <= v1 && v1 <= v2; - } -} \ No newline at end of file diff --git a/src/tokens/comparison/regex.ts b/src/tokens/comparison/regex.ts deleted file mode 100644 index a00d0bf..0000000 --- a/src/tokens/comparison/regex.ts +++ /dev/null @@ -1,59 +0,0 @@ -import toText from '../../helpers/to-text'; - -import type ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTREQUIRED, - description: "Checks if the left operand is a match of the right operand regex", - alias: ['regex'], - inverse: { - description: "Checks if the left operand is not a match of the right operand regex", - alias: ['!regex'] - } -}; - -export default class LessThanToken extends ComparisonToken { - constructor(token: IComparisonToken) { - super({ - ...token, - value: 'regex' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom error - throw new Error('TODO - Evaluation Error: Right hand argument missing'); - } - - let v1 = await this.left.evaluate(options, meta); - let v2 = await this.right.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - if (v1 == null || typeof v2 !== 'string') { - return false; - } - - v1 = toText(v1); - if (v1 == null) { - return false; - } - - v2 = toText(v2); - if (v2 == null) { - return false; - } - - const parts = /^\/(.*)\/([a-z]*)$/i.exec(v2); - if (parts) { - return (new RegExp(parts[1], parts[2])).test(v1); - } - return (new RegExp(v2)).test(v1); - } -} \ No newline at end of file diff --git a/src/tokens/comparison/wildcard.ts b/src/tokens/comparison/wildcard.ts deleted file mode 100644 index aed0ed2..0000000 --- a/src/tokens/comparison/wildcard.ts +++ /dev/null @@ -1,193 +0,0 @@ -import split from '../../helpers/unicode-safe-split'; -import toText from '../../helpers/to-text'; - -import ParserOptions from '../../types/options'; -import { type default as Manifest, ArgumentQuantifier } from '../../types/manifest-comparison'; - -import ComparisonToken, { IComparisonToken } from './base'; - -const toRegExp = (subject: string, caseSensitive = false) : RegExp => { - const wc = split(subject); - let pattern = ''; - let anchorStart = true; - let anchorEnd = true; - let idx = 0; - const len = wc.length; - while (idx < len) { - - const atStart = idx === 0; - - let hasTokens = false; - let zeroOrMore = false; - let anyOneChar = 0; - - let char = wc[idx]; - while (char === '?' || char === '*') { - hasTokens = true; - if (wc[idx] === '?') { - anyOneChar += 1; - } else { - zeroOrMore = true; - } - idx += 1; - char = wc[idx]; - } - - if (hasTokens) { - pattern += '.'.repeat(anyOneChar); - if (zeroOrMore) { - const atEnd = idx === len; - - if (atStart) { - anchorStart = false; - } - if (atEnd) { - anchorEnd = false; - } - - if (!atStart && !atEnd) { - if (!anyOneChar) { - pattern += '.*'; - } else { - pattern += '+'; - } - } - } - continue; - } - - // Char needs to be escaped - if ( - char === '^' || - char === '.' || - char === '-' || - char === '+' || - char === '\\' || - char === '/' || - char === '|' || - char === '(' || - char === ')' || - char === '[' || - char === ']' || - char === '{' || - char === '}' || - char === '$' - ) { - pattern += `\\${char}`; - idx += 1; - continue; - } - - pattern += char; - idx += 1; - } - - if (anchorStart) { - pattern = '^' + pattern; - } - if (anchorEnd) { - pattern += '$'; - } - return new RegExp( - pattern, - 'u' + (!caseSensitive ? 'i' : '') - ) -}; - -interface IWildcardToken extends IComparisonToken { - caseSensitive: boolean; -} - -export const manifest : Manifest = { - arguments: ArgumentQuantifier.RIGHTREQUIRED, - description: "Checks if the left operand is a match of the right operand wildcard", - alias: ['iswm'], - casing: true, - inverse: { - description: "Checks if the left operand is not a match of the right operand wildcard", - alias: ['!iswm'] - } -}; - -export default class WildcardToken extends ComparisonToken { - readonly caseSensitive: boolean - - constructor(token: IWildcardToken) { - super({ - ...token, - value: 'wildcard' - }); - } - - async handle(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom error - throw new Error('TODO - Evaluation Error: Right hand argument missing'); - } - - const v1 = await this.left.evaluate(options, meta); - - if (options.verifyOnly) { - await this.right.evaluate(options, meta); - return false; - } - - const v1Text = toText(v1); - if (v1Text == null) { - return false; - } - - const v2 = await this.right.evaluate(options, meta); - const v2Text = toText(v2); - if (v2Text == null) { - return false; - } - - const v2RegExp = toRegExp(v2Text); - if (v2RegExp == null) { - return false; - } - - return v2RegExp.test(v1Text); - } - - async handleInverse(options: ParserOptions, meta: unknown): Promise { - - if (this.right == null) { - // TODO - custom error - throw new Error('TODO - Evaluation Error: Right hand argument missing'); - } - - const v1 = await this.left.evaluate(options, meta); - - if (options.verifyOnly) { - await this.right.evaluate(options, meta); - return false; - } - - const v1Text = toText(v1); - if (v1Text == null) { - return false; - } - - const v2 = await this.right.evaluate(options, meta); - const v2Text = toText(v2); - if (v2Text == null) { - return false; - } - - const v2RegExp = toRegExp(v2Text); - if (v2RegExp == null) { - return false; - } - - return !v2RegExp.test(v1Text); - } - - toToken() : object { - return { - ...(super.toToken()), - caseSensitive: this.caseSensitive - }; - } -} \ No newline at end of file diff --git a/src/tokens/logical/base.ts b/src/tokens/logical/base.ts deleted file mode 100644 index c06d838..0000000 --- a/src/tokens/logical/base.ts +++ /dev/null @@ -1,16 +0,0 @@ -import TokenType from '../../types/token-types'; - -import { default as OperatorToken, IOperatorToken} from '../token-operator'; - -export interface ILogicalToken extends Omit { - value: unknown; -} - -export default class LogicalToken extends OperatorToken { - constructor(token: ILogicalToken) { - super({ - ...token, - type: TokenType.LOGICAL - }); - } -} \ No newline at end of file diff --git a/src/tokens/logical/logical-and.ts b/src/tokens/logical/logical-and.ts deleted file mode 100644 index 4de3c9c..0000000 --- a/src/tokens/logical/logical-and.ts +++ /dev/null @@ -1,31 +0,0 @@ -import ParserOptions from '../../types/options'; -import { default as LogicalToken, ILogicalToken } from './base'; - -export default class AndOperator extends LogicalToken { - constructor(token: ILogicalToken) { - super({ - ...token, - value: 'and' - }); - } - - async evaluate(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom errors - throw new Error('TODO'); - } - - const left = await this.left.evaluate(options, meta); - if (options.verifyOnly) { - await this.right.evaluate(options, meta); - return false; - } - if (left == null || left === false || left === '' || left === 0) { - return false; - } - - - const right = await this.right.evaluate(options, meta); - return right != null && right != false && right !== '' && right !== 0; - } -} \ No newline at end of file diff --git a/src/tokens/logical/logical-not.ts b/src/tokens/logical/logical-not.ts deleted file mode 100644 index cf16be6..0000000 --- a/src/tokens/logical/logical-not.ts +++ /dev/null @@ -1,23 +0,0 @@ -import ParserOptions from '../../types/options'; -import { default as LogicalToken, ILogicalToken } from './base'; - -export type INotOperator = Omit; - -export default class NotOperator extends LogicalToken { - constructor(token: INotOperator) { - super({ - ...token, - value: 'not' - }); - } - - async evaluate(options: ParserOptions, meta: unknown): Promise { - const value = await this.left.evaluate(options, meta); - - if (options.verifyOnly) { - return false; - } - - return value != null && value !== false && value !== '' && value !== 0; - } -} \ No newline at end of file diff --git a/src/tokens/logical/logical-or.ts b/src/tokens/logical/logical-or.ts deleted file mode 100644 index 25c8644..0000000 --- a/src/tokens/logical/logical-or.ts +++ /dev/null @@ -1,31 +0,0 @@ -import ParserOptions from '../../types/options'; -import { default as LogicalToken, ILogicalToken } from './base'; - -export default class OrOperator extends LogicalToken { - constructor(token: ILogicalToken) { - super({ - ...token, - value: 'or' - }); - } - - async evaluate(options: ParserOptions, meta: unknown): Promise { - if (this.right == null) { - // TODO - custom errors - throw new Error('TODO'); - } - - const left = await this.left.evaluate(options, meta); - if (options.verifyOnly) { - await this.right.evaluate(options, meta); - return false; - } - - if (left != null && left !== false && left !== '' && left !== 0) { - return true; - } - - const right = await this.right.evaluate(options, meta); - return right != null && right != false && right !== '' && right !== 0; - } -} \ No newline at end of file diff --git a/src/tokens/token-function.ts b/src/tokens/token-function.ts deleted file mode 100644 index 436c3a8..0000000 --- a/src/tokens/token-function.ts +++ /dev/null @@ -1,68 +0,0 @@ -import TokenType from '../types/token-types'; -import ParserOptions, { type IFunctionLookup } from '../types/options'; -import Token from './token'; - -export interface IFunctionalToken { - position: number; - prefix: string; - value: string; - arguments: Token[]; - lookupFn: IFunctionLookup; -} - -export default class FunctionalToken extends Token { - public prefix: string; - public arguments: Token[]; - public lookupFn : IFunctionLookup; - - constructor(token: IFunctionalToken) { - super({ - ...token, - type: TokenType.FUNCTIONAL - }); - this.prefix = token.prefix; - this.arguments = token.arguments; - this.lookupFn = token.lookupFn; - } - - async evaluate(options: ParserOptions, meta: unknown) : Promise { - const handler = this.lookupFn(this.value); - if (handler == null) { - // TODO: custom errors - throw new Error(`TODO - No handler for ${this.prefix}${this.value}`); - } - - const args : unknown[] = []; - if (this.arguments != null) { - const argList = this.arguments; - for (let idx = 0; idx < argList.length; idx += 1) { - const arg = await argList[idx].evaluate(options, meta); - args.push(arg); - } - } - - if (options.verifyOnly) { - return; - } - - if (!options.skipArgumentsCheck && handler.argsCheck != null) { - try { - await handler.argsCheck(meta, args); - } catch (err) { - // TODO - Custom errors - throw new Error(`TODO - ArgumentsError: ${err.message}`); - } - } - - const res = handler.evaluator(meta, ...args); - return res; - } - - toToken() : object { - return { - ...(super.toJSON()), - prefix: this.prefix, - arguments: this.arguments.map(value => value.toJSON()) - } - } -} \ No newline at end of file diff --git a/src/tokens/token-operator.spec.ts b/src/tokens/token-operator.spec.ts deleted file mode 100644 index 7700923..0000000 --- a/src/tokens/token-operator.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import '../../jest/helpers'; - -import TokenType from '../types/token-types'; - -import Token from './token'; -import OperatorToken from './token-operator'; - -const stub = { - value: 'text', - toJSON() { return this.value } -}; - -describe('OperatorToken class', function () { - it('Exports a class as default', function () { - expect(typeof OperatorToken).toBe('function'); - }); -}); - -describe('new OperatorToken()', function () { - it('Doesn\'t error when construdting', function () { - new OperatorToken(); - }); - it('Is an instance of OperatorToken', function () { - const token = new OperatorToken(); - expect(token instanceof OperatorToken).toBe(true); - }); - it('Is an instance of Token', function () { - const token = new OperatorToken(); - expect(token instanceof Token).toBe(true); - }); -}); - -describe('Token#type', function () { - it('Stores type correctly', function () { - const token = new OperatorToken(); - expect(token.type).toBe(TokenType.OPERATOR); - }); -}); - -describe('OperatorToken#left', function () { - it('Stores type correctly', function () { - const token = new OperatorToken({ - left: stub - }); - expect(token.left).toBe(stub); - }); -}); - -describe('OperatorToken#right', function () { - it('Stores type correctly', function () { - const token = new OperatorToken({ - right: stub - }); - expect(token.right).toBe(stub); - }); -}); - -describe('OperatorToken#toJSON', function () { - it('Does not throw', function () { - const token = new OperatorToken(); - expect(() => token.toJSON()).not.toThrow(); - }); - - it('Does not store a value when left is not specified', function () { - const token = new OperatorToken(); - const result = token.toJSON(); - expect(result.left).toBeUndefined(); - }); - - it('Stores left when specified', function () { - const token = new OperatorToken({ left: stub }); - const result = token.toJSON(); - expect(result).hasProperty('left'); - expect(result.left).toBe('text'); - }); - - it('Does not store a value when right is not specified', function () { - const token = new OperatorToken(); - const result = token.toJSON(); - expect(result.left).toBeUndefined(); - }); - - it('Stores right when specified', function () { - const token = new OperatorToken({ right: stub }); - const result = token.toJSON(); - expect(result).hasProperty('right'); - expect(result.right).toBe('text'); - }); -}); - -describe('Operator#evaluate()', function () { - it('Throws an error', async function () { - const token = new OperatorToken(); - expect(() => token.evaluate({}, {})).toAsyncThrow(); - }); -}) \ No newline at end of file diff --git a/src/tokens/token-operator.ts b/src/tokens/token-operator.ts deleted file mode 100644 index 39a6131..0000000 --- a/src/tokens/token-operator.ts +++ /dev/null @@ -1,45 +0,0 @@ -import ParserOptions from '../types/options'; -import TokenType from '../types/token-types'; -import Token, { IToken } from './token'; - -export interface IOperatorToken extends IToken { - left?: unknown; - right?: unknown; -} - -export default class OperatorToken extends Token { - public left?: Token; - public right?: Token; - - constructor(token: IOperatorToken = {}) { - super({ - type: TokenType.OPERATOR, - ...token - }); - if (token.left != null) { - this.left = token.left; - } - if (token.right != null) { - this.right = token.right; - } - } - - toJSON() : Record { - const result : Record = { - ...(super.toJSON()) - }; - if (this.left != null) { - result.left = this.left.toJSON(); - } - - if (this.right != null) { - result.right = this.right.toJSON(); - } - return result; - } - - /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - async evaluate(options: ParserOptions, meta: unknown) : Promise { - throw new Error('TODO ExpressionError - Operator missing evaluator function'); - } -} \ No newline at end of file diff --git a/src/tokens/token-text.spec.ts b/src/tokens/token-text.spec.ts deleted file mode 100644 index 46eb5b7..0000000 --- a/src/tokens/token-text.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import '../../jest/helpers'; - -import TokenType from '../types/token-types'; - -import Token from './token'; -import TextToken from './token-text'; - -describe('TextToken class', function () { - it('Exports a class as default', function () { - expect(typeof Token).toBe('function'); - }); -}); - -describe('new TextToken()', function () { - it('Doesn\'t error when constructing', function () { - new TextToken(); - }); - it('Is an instance of TextToken', function () { - const token = new TextToken(); - expect(token instanceof TextToken).toBe(true); - }); - it('Is an instance of Token', function () { - const token = new TextToken(); - expect(token instanceof Token).toBe(true); - }); - it('Stores type correctly', function () { - const token = new TextToken(); - expect(token.type).toBe(TokenType.TEXT); - }); - it('Stores value correctly', function () { - const token = new TextToken({value: 'value'}); - expect(token.value).toBe('value'); - }); -}); - -describe('Token#type', function () { - it('Stores type correctly', function () { - const token = new TextToken(); - expect(token.type).toBe(TokenType.TEXT); - }); -}); - -describe('Token#value', function () { - it('Stores type correctly', function () { - const token = new TextToken({value: 'value'}); - expect(token.value).toBe('value'); - }); -}); - -describe('Token#evaluate()', function () { - it('resolves to no value an empty string', async function () { - expect.assertions(1); - const token = new TextToken(); - const result = await token.evaluate({}, {}); - expect(result).toBe(''); - }); - - it('resolves to a string', async function () { - expect.assertions(1); - const token = new TextToken({value: "text"}); - const result = await token.evaluate({}, {}); - expect(result).toBe('text'); - }); -}); \ No newline at end of file diff --git a/src/tokens/token.spec.ts b/src/tokens/token.spec.ts deleted file mode 100644 index b9c46b3..0000000 --- a/src/tokens/token.spec.ts +++ /dev/null @@ -1,157 +0,0 @@ -import '../../jest/helpers'; - -import Token from './token'; - -describe('Token class', function () { - it('Exports a class as default', function () { - expect(typeof Token).toBe('function'); - }); -}); - -describe('new Token()', function () { - it('Doesn\'t error when constructing', function () { - new Token(); - }); -}); - -describe('Token#type', function () { - const token = new Token(); - it('Is a property', function () { - expect(token).hasProperty('type'); - }); - it('Is a number', function () { - expect(typeof token.type).toBe('number'); - }); - it('Is finite', function () { - expect(Number.isFinite(token.type)).toBe(true); - }); - it('Defaults to 0', function () { - expect(token.type).toBe(0); - }); - it('Is stored properly when specified', function () { - const token = new Token({ type: 99 }); - expect(token.type).toBe(99); - }); -}); - -describe('Token#position', function () { - const token = new Token(); - it('Is a property', function () { - expect(token).hasProperty('position'); - }); - it('Is a number', function () { - expect(typeof token.position).toBe('number'); - }); - it('Is finite', function () { - expect(Number.isFinite(token.position)).toBe(true); - }); - it ('Defaults to 0', function () { - expect(token.position).toBe(-1); - }); - it('Is stored properly when specified', function () { - const token = new Token({ position: 99 }); - expect(token.position).toBe(99); - }); -}); - -describe('Token#value', function () { - const token = new Token() - it('Is a property', function () { - expect(token).hasProperty('value'); - }); - it('Defaults to undefined', function () { - expect(token.value).toBeUndefined(); - }); - it('Is stored properly when specified', function () { - const token = new Token({ value: 'value' }); - expect(token.value).toBe('value'); - }); -}); - -describe('Token#toJSON()', function () { - it('Is a property', function () { - expect(Token.prototype).hasProperty('toJSON'); - }); - - it('Is a function', function () { - expect(typeof Token.prototype.toJSON).toBe('function'); - }); - - it('Returns an object', function () { - const token = new Token({type: 1, position: 1, value: 'value'}); - const json = token.toJSON(); - expect(typeof json).toBe('object'); - }); - - it('The result is an adequite depiction', function () { - const token = new Token({type: 1, position: 1, value: 'value'}); - const json = token.toJSON(); - expect(json).hasProperty('type'); - expect(json.type).toBe(1); - expect(json).hasProperty('position'); - expect(json.position).toBe(1); - expect(json).hasProperty('value'); - expect(json.value).toBe('value'); - - expect(Object.keys(json).some(key => ( - key !== 'type' && - key !== 'position' && - key !== 'value' - ))).toBe(false); - }) -}); - -describe('Token#toString()', function () { - - it('Is a property', function () { - expect(Token.prototype).hasProperty('toString'); - }); - - it('Is a function', function () { - expect(typeof Token.prototype.toString).toBe('function'); - }); - - it('Returns a string', function () { - const token = new Token({type: 1, position: 1, value: 'value'}); - expect(typeof token.toString()).toBe('string'); - }); - - it('The result is valid json', function () { - const token = new Token({type: 1, position: 1, value: 'value'}); - JSON.parse(token.toString()); - }); - - it('The result is an adequit depiction of token', function () { - const token = new Token({type: 1, position: 1, value: 'value'}); - const json = JSON.parse(token.toString()); - - expect(json).hasProperty('type'); - expect(json.type).toBe(1); - expect(json).hasProperty('position'); - expect(json.position).toBe(1); - expect(json).hasProperty('value'); - expect(json.value).toBe('value'); - - expect(Object.keys(json).some(key => ( - key !== 'type' && - key !== 'position' && - key !== 'value' - ))).toBe(false); - }); -}); - -describe('Token#evaluate()', function () { - - it('Is a property', function () { - expect(Token.prototype).hasProperty('evaluate'); - }); - - it('Is a function', function () { - expect(typeof Token.prototype.evaluate).toBe('function') - }); - - it('to throw an error', function () { - const token = new Token(); - expect(() => token.evaluate({}, {})).toAsyncThrow(); - }); -}); \ No newline at end of file diff --git a/src/types/options.ts b/src/types/options.ts index 36a5b40..0665186 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -4,12 +4,13 @@ export interface IFunctionHandler { evaluator: (meta: unknown, ...args: unknown[]) => Promise; } -export type IFunctionLookup = (name: string, stack?: string[], meta?: unknown) => IFunctionHandler; +export type IFunctionLookup = (name: string, stack?: string[], meta?: unknown) => Promise; export default interface ParserOptions { functionHandlers?: Record; functionLookups?: Record; + "if"?: boolean; eol?: 'error' | 'remove' | 'space' | 'keep'; specialSequences?: boolean; diff --git a/src/types/tokenize-state.ts b/src/types/tokenize-state.ts index cfc61aa..8a8b31d 100644 --- a/src/types/tokenize-state.ts +++ b/src/types/tokenize-state.ts @@ -1,6 +1,6 @@ import type ParserOptions from './options'; import type IPreToken from './pre-token'; -import type Token from '../tokens/token'; +import type Token from '../parse/token'; export default interface ITokenizeState { options: ParserOptions, From fc7894c7665282e6d5f09b0a981eddf0f153ddf7 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 04:56:49 -0400 Subject: [PATCH 60/92] feat: optimized --- src/helpers/is-primitive.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/helpers/is-primitive.ts b/src/helpers/is-primitive.ts index 2e560aa..a953463 100644 --- a/src/helpers/is-primitive.ts +++ b/src/helpers/is-primitive.ts @@ -1,7 +1,5 @@ -export default (subject: unknown) : boolean => { - return ( - typeof subject === 'boolean' || - (typeof subject === 'number' && Number.isFinite(subject)) || - typeof subject === 'string' - ); -} \ No newline at end of file +export default (subject: unknown) : boolean => ( + typeof subject === 'boolean' || + (typeof subject === 'number' && Number.isFinite(subject)) || + typeof subject === 'string' +); \ No newline at end of file From f1bc127d8bcb677c1aaf03029c90b843945ff87b Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 04:57:18 -0400 Subject: [PATCH 61/92] feat: make subject typed as any --- src/helpers/has.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helpers/has.ts b/src/helpers/has.ts index 1506b81..d1256e1 100644 --- a/src/helpers/has.ts +++ b/src/helpers/has.ts @@ -1,3 +1,4 @@ const hasOwnProperty = Object.prototype.hasOwnProperty; -export default (subject: unknown, key: string) => hasOwnProperty.call(subject, key); \ No newline at end of file +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +export default (subject: any, key: string) => (hasOwnProperty.call(subject, key) && subject[key] !== undefined); \ No newline at end of file From 05cb450dc23aa774eebf30d93033488f3677b3ff Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 04:57:42 -0400 Subject: [PATCH 62/92] feat: update jest helpers --- jest/helpers.ts | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/jest/helpers.ts b/jest/helpers.ts index 6125d14..72a87ec 100644 --- a/jest/helpers.ts +++ b/jest/helpers.ts @@ -2,7 +2,7 @@ export {} interface CustomMatchers { - hasProperty(key: string): R; + toHaveOwnProperty(key: string, value?: unknown): R; toAsyncThrow(): R; } @@ -14,23 +14,44 @@ declare global { } } +interface IResult { + pass: boolean; + message: () => string; +} + const hasOwnProperty = Object.prototype.hasOwnProperty expect.extend({ - hasProperty: (subject: unknown, key: string) : {pass: boolean, message: () => string} => { - if (hasOwnProperty.call(subject, key)) { + + //eslint-disable-next-line @typescript-eslint/no-explicit-any + toHaveOwnProperty: (subject: any, key: string, value?: unknown) : IResult => { + if (!hasOwnProperty.call(subject, key)) { + return { + pass: false, + message: () => `expected subject to have '${key}'` + }; + + // @ts-expect-error: checking arguments count + } else if (arguments.length < 3) { return { pass: true, message: () => `expected subject not to have '${key}' as a property` }; - } - return { - pass: false, - message: () => `expected subject to have '${key}' as a property` + + } else if (subject[key] === value) { + return { + pass: true, + message: () => `expected subject '${key}' not to equal ${value}` + }; + } else { + return { + pass: false, + message: () => `expected subject '${key}' to equal ${value}` + }; } }, - toAsyncThrow: async (subject: () => unknown) : Promise<{ pass: boolean, message: () => string}> => { + toAsyncThrow: async (subject: () => unknown) : Promise => { expect.assertions(1); try { await subject(); From 43d7b9591304578646ee37627e2171feef65e204 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 04:58:13 -0400 Subject: [PATCH 63/92] chore: update jest config to use proper tsconfig --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index f2aa6b5..a7d3176 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,7 +6,7 @@ module.exports = { '.ts$': [ 'ts-jest', { - tsconfig: "tsconfig.test.json" + tsconfig: "./tsconfig.json" } ] } From dc54d8b0aad33a3ba808f2c6a769d6c23fdd32ff Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 04:58:40 -0400 Subject: [PATCH 64/92] chore: formatting --- src/parse/text/tokenize/quoted.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/parse/text/tokenize/quoted.ts b/src/parse/text/tokenize/quoted.ts index 70065b7..c49a464 100644 --- a/src/parse/text/tokenize/quoted.ts +++ b/src/parse/text/tokenize/quoted.ts @@ -32,10 +32,7 @@ export default async (state: ITokenizeState) : Promise => { } const quoteTokens : Token[] = []; - while ( - cursor < tokens.length && - tokens[cursor].value !== '"' - ) { + while (cursor < tokens.length && tokens[cursor].value !== '"') { const lastToken : Token = quoteTokens[quoteTokens.length - 1]; From 0c83c95d6f772dbe368de6101fab79a59c0c68c7 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 04:59:01 -0400 Subject: [PATCH 65/92] feat: add value check --- src/parse/text/token/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parse/text/token/index.ts b/src/parse/text/token/index.ts index 6bda7e6..fe39783 100644 --- a/src/parse/text/token/index.ts +++ b/src/parse/text/token/index.ts @@ -10,6 +10,9 @@ export interface ITextToken extends IToken { export default class TextToken extends Token { public value: string; constructor(token: ITextToken = {}) { + if (token.value != null && typeof token.value !== 'string') { + throw new Error('TODO - ExpressionError: value must be a string'); + } super({ ...token, type: TokenType.TEXT, From 1abb9db9ebfa6a1adc92b89545b9121578e0a4bb Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 04:59:27 -0400 Subject: [PATCH 66/92] chore: optimize check --- src/parse/text/tokenize/escape-single.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parse/text/tokenize/escape-single.ts b/src/parse/text/tokenize/escape-single.ts index cbf3c34..a3f37ed 100644 --- a/src/parse/text/tokenize/escape-single.ts +++ b/src/parse/text/tokenize/escape-single.ts @@ -10,8 +10,8 @@ export default async (state: ITokenizeState, characters?: string[]) : Promise= tokens.length || + tokens[cursor].value !== '\\' || !characters.includes(tokens[cursor + 1].value) ) { return false; From fee34229413a793d769ba81ae30f3ab3de2da101 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 05:00:01 -0400 Subject: [PATCH 67/92] fix: not returning correct value --- src/parse/text/tokenize/special.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parse/text/tokenize/special.ts b/src/parse/text/tokenize/special.ts index 788e4e0..cf4d07c 100644 --- a/src/parse/text/tokenize/special.ts +++ b/src/parse/text/tokenize/special.ts @@ -5,7 +5,7 @@ import type ITokenizeState from '../../../types/tokenize-state'; import TextToken from '../token'; export default async (state: ITokenizeState) : Promise => { - if (!state.options.specialSequences) { + if (state.options.specialSequences === false) { return false; } @@ -17,8 +17,8 @@ export default async (state: ITokenizeState) : Promise => { const { tokens, cursor } = state; if ( - tokens[cursor]?.value !== '\\' || - tokens[cursor + 1] == null || + (cursor + 1) >= tokens.length || + tokens[cursor].value !== '\\' || !has(characters, tokens[cursor + 1].value) ) { return false; @@ -26,7 +26,7 @@ export default async (state: ITokenizeState) : Promise => { state.output = new TextToken({ position: tokens[cursor].position, - value: tokens[cursor + 1].value + value: characters[tokens[cursor + 1].value] }); state.cursor += 2; From 33fcc6b086a517ce89ad3a16ee8d979f2bbe978b Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 05:00:19 -0400 Subject: [PATCH 68/92] chore: write tests --- src/parse/text/token/index.spec.ts | 42 +++++ src/parse/text/tokenize/escape-single.spec.ts | 148 ++++++++++++++++++ src/parse/text/tokenize/special.spec.ts | 134 ++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 src/parse/text/token/index.spec.ts create mode 100644 src/parse/text/tokenize/escape-single.spec.ts create mode 100644 src/parse/text/tokenize/special.spec.ts diff --git a/src/parse/text/token/index.spec.ts b/src/parse/text/token/index.spec.ts new file mode 100644 index 0000000..fd52841 --- /dev/null +++ b/src/parse/text/token/index.spec.ts @@ -0,0 +1,42 @@ +import '../../../../jest/helpers'; + +import Token from '../../token'; +import TextToken from './index'; + +test('Exports a function', () => { + expect(typeof TextToken).toBe('function'); + expect(TextToken.prototype).toBeDefined(); +}); + +test('Throws an error if specified input is not string', () => { + expect(() => new TextToken({ + // @ts-expect-error: Testing non-string values + value: false + })).toThrow(); +}); + +test('Constructs when inputs are valid', () => { + expect(() => new TextToken()).not.toThrow(); +}); + +test('Instances derive from Token', () => { + expect(new TextToken()).toBeInstanceOf(Token); +}); + +test('Defaults value to empty string', () => { + expect(new TextToken()).toHaveOwnProperty('value', ''); +}); + +test('Stores value properly', () => { + expect(new TextToken({value: 'test'})).toHaveOwnProperty('value', 'test'); +}); + +test('Evaluates to value property', async () => { + expect.assertions(2); + + const result = await (new TextToken()).evaluate({}, {}); + expect(result).toBe(''); + + const result2 = await (new TextToken({value: 'test'})).evaluate({}, {}); + expect(result2).toBe('test'); +}); \ No newline at end of file diff --git a/src/parse/text/tokenize/escape-single.spec.ts b/src/parse/text/tokenize/escape-single.spec.ts new file mode 100644 index 0000000..45a2e6c --- /dev/null +++ b/src/parse/text/tokenize/escape-single.spec.ts @@ -0,0 +1,148 @@ +import type ITokenizeState from '../../../types/tokenize-state'; + +import type Token from '../../token'; +import TextToken from '../token'; +import tokenizeEscape from './escape-single'; + +const tokenBase : ITokenizeState = { + options: {}, + stack: [], + tokens: [], + cursor: 0 +}; + +test('Exports a function', () => { + expect(typeof tokenizeEscape).toBe('function'); +}); + +test('Returns false when theres no tokens', async () => { + expect.assertions(1); + const result = await tokenizeEscape(tokenBase); + expect(result).toBe(false); +}); + +test('Returns false when theres only one token', async () => { + expect.assertions(1); + const result = await tokenizeEscape({ + ...tokenBase, + tokens: [{position: 0, value: ''}] + }); + expect(result).toBe(false); +}); + +test('Returns false when there\'s only one token left', async () => { + expect.assertions(1); + const result = await tokenizeEscape({ + ...tokenBase, + tokens: [ + {position: 0, value: ''}, + {position: 0, value: ''} + ], + cursor: 1 + }); + expect(result).toBe(false); +}); + +test('Returns false when token at cursor is not \\', async () => { + expect.assertions(1); + const result = await tokenizeEscape({ + ...tokenBase, + tokens: [ + {position: 0, value: 'a'}, + {position: 0, value: 'n'} + ], + cursor: 0 + }); + expect(result).toBe(false); +}); + +test('Returns false when token isn\'t an escapable sequence', async () => { + expect.assertions(1); + const result = await tokenizeEscape({ + ...tokenBase, + tokens: [{position: 0, value: '\\'}, {position: 0, value: 'n'}], + cursor: 0 + }); + expect(result).toBe(false); +}); + +test('Returns true for default: \\\\', async () => { + expect.assertions(4); + const state : ITokenizeState = { + ...tokenBase, + tokens: [{position: 0, value: '\\'}, {position: 0, value: '\\'}], + cursor: 0 + }; + const result = await tokenizeEscape(state); + expect(result).toBe(true); + expect(state.output).toBeDefined(); + expect(state.output instanceof TextToken).toBe(true); + expect((state.output).value).toBe('\\'); +}); + +test('Returns true for default: \\$', async () => { + expect.assertions(4); + const state : ITokenizeState = { + ...tokenBase, + tokens: [{position: 0, value: '\\'}, {position: 0, value: '$'}], + cursor: 0 + }; + const result = await tokenizeEscape(state); + expect(result).toBe(true); + expect(state.output).toBeDefined(); + expect(state.output instanceof TextToken).toBe(true); + expect((state.output).value).toBe('$'); +}); + +test('Returns true for default: \\"', async () => { + expect.assertions(4); + const state : ITokenizeState = { + ...tokenBase, + tokens: [{position: 0, value: '\\'}, {position: 0, value: '"'}], + cursor: 0 + }; + const result = await tokenizeEscape(state); + expect(result).toBe(true); + expect(state.output).toBeDefined(); + expect(state.output instanceof TextToken).toBe(true); + expect((state.output).value).toBe('"'); +}); + +test('Returns true for default: \\`', async () => { + expect.assertions(4); + const state : ITokenizeState = { + ...tokenBase, + tokens: [{position: 0, value: '\\'}, {position: 0, value: '`'}], + cursor: 0 + }; + const result = await tokenizeEscape(state); + expect(result).toBe(true); + expect(state.output).toBeDefined(); + expect(state.output instanceof TextToken).toBe(true); + expect((state.output).value).toBe('`'); +}); + +test('Returns false for defaults when alternative list is specified', async () => { + expect.assertions(1); + const state : ITokenizeState = { + ...tokenBase, + tokens: [{position: 0, value: '\\'}, {position: 0, value: '$'}], + cursor: 0 + }; + const result = await tokenizeEscape(state, ['@']); + expect(result).toBe(false); +}); + +test('Uses alternative list of escapables when specified', async () => { + expect.assertions(4); + const state : ITokenizeState = { + ...tokenBase, + tokens: [{position: 0, value: '\\'}, {position: 0, value: '@'}], + cursor: 0 + }; + const result = await tokenizeEscape(state, ['@']); + expect(result).toBe(true); + expect(state.output).toBeDefined(); + expect(state.output instanceof TextToken).toBe(true); + expect((state.output).value).toBe('@'); +}); \ No newline at end of file diff --git a/src/parse/text/tokenize/special.spec.ts b/src/parse/text/tokenize/special.spec.ts new file mode 100644 index 0000000..12a2c6c --- /dev/null +++ b/src/parse/text/tokenize/special.spec.ts @@ -0,0 +1,134 @@ +import type ITokenizeState from '../../../types/tokenize-state'; + +import type Token from '../../token'; +import TextToken from '../token'; + +import tokenizeSpecial from './special'; + +const tokenBase : ITokenizeState = { + options: {}, + tokens: [], + cursor: 0, + stack: [] +}; + +test('It exports a function', () => { + expect(typeof tokenizeSpecial).toBe('function'); +}); + +test('Returns false when options.specialSequences is false', async () => { + expect.assertions(1); + const result = await tokenizeSpecial({ + ...tokenBase, + options: { specialSequences: false } + }); + expect(result).toBe(false); +}); + +test('Returns false when theres no tokens', async () => { + expect.assertions(1); + const result = await tokenizeSpecial(tokenBase); + expect(result).toBe(false); +}); + +test('Returns false when theres only one token', async () => { + expect.assertions(1); + const result = await tokenizeSpecial({ + ...tokenBase, + tokens: [{position: 0, value: ''}] + }); + expect(result).toBe(false); +}); + +test('Returns false when there\'s only one token left', async () => { + expect.assertions(1); + const result = await tokenizeSpecial({ + ...tokenBase, + tokens: [ + {position: 0, value: ''}, + {position: 0, value: ''} + ], + cursor: 1 + }); + expect(result).toBe(false); +}); + +test('Returns false when token at cursor is not \\', async () => { + expect.assertions(1); + const result = await tokenizeSpecial({ + ...tokenBase, + tokens: [ + {position: 0, value: 'a'}, + {position: 0, value: 'n'} + ], + cursor: 0 + }); + expect(result).toBe(false); +}); + +test('Returns false when there isn\'t a special sequence', async () => { + expect.assertions(1); + const result = await tokenizeSpecial({ + ...tokenBase, + tokens: [ + {position: 0, value: '\\'}, + {position: 0, value: 'v'} + ], + cursor: 0 + }); + expect(result).toBe(false); +}); + +test('Parses \\n', async () => { + expect.assertions(4); + + const state : ITokenizeState = { + ...tokenBase, + tokens: [ + {position: 0, value: '\\'}, + {position: 1, value: 'n'} + ] + }; + const result = await tokenizeSpecial(state); + + expect(result).toBe(true); + expect(state.output).toBeDefined(); + expect(state.output instanceof TextToken).toBe(true); + expect((state.output).value).toBe('\n'); +}); + +test('Parses \\r', async () => { + expect.assertions(4); + + const state : ITokenizeState = { + ...tokenBase, + tokens: [ + {position: 0, value: '\\'}, + {position: 1, value: 'r'} + ] + }; + const result = await tokenizeSpecial(state); + + expect(result).toBe(true); + expect(state.output).toBeDefined(); + expect(state.output instanceof TextToken).toBe(true); + expect((state.output).value).toBe('\r'); +}); + +test('Parses \\t', async () => { + expect.assertions(4); + + const state : ITokenizeState = { + ...tokenBase, + tokens: [ + {position: 0, value: '\\'}, + {position: 1, value: 't'} + ] + }; + const result = await tokenizeSpecial(state); + + expect(result).toBe(true); + expect(state.output).toBeDefined(); + expect(state.output instanceof TextToken).toBe(true); + expect((state.output).value).toBe('\t'); +}); \ No newline at end of file From 16ccac5fd9e6ac09cc9a7e80793a7c2ee73afc68 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 05:47:21 -0400 Subject: [PATCH 69/92] feat: value defaults to null when undefined --- src/parse/token.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/parse/token.ts b/src/parse/token.ts index 43960b7..c8112bd 100644 --- a/src/parse/token.ts +++ b/src/parse/token.ts @@ -21,11 +21,10 @@ export default class Token { } toJSON() : Record { - return { type: this.type, position: this.position, - value: this.value + value: this.value == null ? null : this.value }; } From 64a99df3e983e65f1aa922e20ff5ae5539d9fb1a Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 05:47:57 -0400 Subject: [PATCH 70/92] fix(jest/helpers): invalid arguments check --- jest/helpers.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jest/helpers.ts b/jest/helpers.ts index 72a87ec..f6b6db4 100644 --- a/jest/helpers.ts +++ b/jest/helpers.ts @@ -24,15 +24,17 @@ const hasOwnProperty = Object.prototype.hasOwnProperty expect.extend({ //eslint-disable-next-line @typescript-eslint/no-explicit-any - toHaveOwnProperty: (subject: any, key: string, value?: unknown) : IResult => { + toHaveOwnProperty: (...args: any[]) : IResult => { + + const [subject, key, value] = args; + if (!hasOwnProperty.call(subject, key)) { return { pass: false, message: () => `expected subject to have '${key}'` }; - // @ts-expect-error: checking arguments count - } else if (arguments.length < 3) { + } else if (args.length < 3) { return { pass: true, message: () => `expected subject not to have '${key}' as a property` From 5deb1e487285960b076045468dba473b424c4d8e Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 05:48:30 -0400 Subject: [PATCH 71/92] chore(tests): implement Token class tests --- src/parse/token.spec.ts | 94 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/parse/token.spec.ts diff --git a/src/parse/token.spec.ts b/src/parse/token.spec.ts new file mode 100644 index 0000000..c4cb4ef --- /dev/null +++ b/src/parse/token.spec.ts @@ -0,0 +1,94 @@ +import '../../jest/helpers'; + +import TokenType from '../types/token-types'; +import Token from './token'; + +test('Exports a constructor', () => { + expect(typeof Token).toBe('function'); + expect(Token.prototype).toBeDefined(); +}); + +test('Has .toJSON() function', () => { + expect(Token.prototype).toHaveOwnProperty('toJSON'); + expect(typeof Token.prototype.toJSON).toBe('function'); +}); + +test('Has .toString() function', () => { + expect(Token.prototype).toHaveOwnProperty('toString'); + expect(typeof Token.prototype.toString).toBe('function'); +}); + +test('Has .evaluate() function', () => { + expect(Token.prototype).toHaveOwnProperty('evaluate'); + expect(typeof Token.prototype.evaluate).toBe('function'); +}); + +test('Constructs without error', () => { + expect(() => new Token()).not.toThrow(); +}); + +test('Stores type correctly', () => { + const result1 = new Token(); + expect(result1).toHaveOwnProperty('type', TokenType.UNKNOWN); + + const result2 = new Token({ type: TokenType.TEXT }) + expect(result2).toHaveOwnProperty('type', TokenType.TEXT); +}); + +test('Stores position correctly', () => { + const result1 = new Token(); + expect(result1).toHaveOwnProperty('position', -1); + + const result2 = new Token({ position: 10 }) + expect(result2).toHaveOwnProperty('position', 10); +}); + +test('Stores value correctly', () => { + const result1 = new Token(); + expect(result1.value).toBeUndefined() + + const result2 = new Token({ value: 'test' }) + expect(result2).toHaveOwnProperty('value', 'test'); +}); + +test('.toJSON() returns proper representation', () => { + const result1 = (new Token()).toJSON(); + expect(result1).toHaveOwnProperty('type', TokenType.UNKNOWN); + expect(result1).toHaveOwnProperty('position', -1); + expect(result1).toHaveOwnProperty('value', null); + + const result2 = (new Token({ type: TokenType.TEXT, position: 0, value: 'test' })).toJSON(); + expect(result2).toHaveOwnProperty('type', TokenType.TEXT); + expect(result2).toHaveOwnProperty('position', 0); + expect(result2).toHaveOwnProperty('value', 'test'); +}); + +test('.toString() returns proper representation', () => { + const json = (new Token({ type: TokenType.TEXT, position: 0, value: 'test' })).toString(); + const result = JSON.parse(json); + + expect(result).toHaveOwnProperty('type', TokenType.TEXT); + expect(result).toHaveOwnProperty('position', 0); + expect(result).toHaveOwnProperty('value', 'test'); +}); + +test('.evaluate() throws unless token is empty', () => { + expect(() => { + const token = new Token(); + return token.evaluate({}, {}) + }).toAsyncThrow(); +}); + +test('.evaluate() does not throw if token is empty', () => { + expect(() => { + const token = new Token({type: TokenType.EMPTY}); + return token.evaluate({}, {}); + }).not.toAsyncThrow(); +}); + +test('.evaluate() returns undefined for empty token', async () => { + expect.assertions(1); + const token = new Token({type: TokenType.EMPTY}); + const result = await token.evaluate({}, {}); + expect(result).toBeUndefined(); +}); \ No newline at end of file From 54218a68b117a46cb520671124e2348473079bee Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 07:27:02 -0400 Subject: [PATCH 72/92] ignore coverage dir --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index af98353..77e8817 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ lib/ -/debug-playground.js \ No newline at end of file +coverage/ +/debug-playground.js From 9f8688e4aadbd06c4de9f470b7d0ef9797231fd7 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 07:27:26 -0400 Subject: [PATCH 73/92] tests: check that instance is Token --- src/parse/token.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parse/token.spec.ts b/src/parse/token.spec.ts index c4cb4ef..4c8ce18 100644 --- a/src/parse/token.spec.ts +++ b/src/parse/token.spec.ts @@ -25,6 +25,7 @@ test('Has .evaluate() function', () => { test('Constructs without error', () => { expect(() => new Token()).not.toThrow(); + expect(new Token()).toBeInstanceOf(Token); }); test('Stores type correctly', () => { From 03b081f99bea35502e8a926791492ff47646eef8 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 07:27:46 -0400 Subject: [PATCH 74/92] feat: validate values list --- src/parse/token-list.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/parse/token-list.ts b/src/parse/token-list.ts index d2bcbd5..9bf29a4 100644 --- a/src/parse/token-list.ts +++ b/src/parse/token-list.ts @@ -26,6 +26,12 @@ export default class TokenList extends Token { throw new Error('TODO - ExpressionError: token list must be an array') } + for (let idx = 0; idx < token.value.length; idx += 1) { + if (!(token.value[idx] instanceof Token)) { + throw new Error('value list must contain only tokens'); + } + } + super({ ...token, type: TokenType.TOKENLIST @@ -51,7 +57,7 @@ export default class TokenList extends Token { let res : unknown; for (let idx = 0; idx < parts.length; idx += 1) { - const value = await parts[idx].evaluate(options, {...(meta) }); + const value = await parts[idx].evaluate(options, meta); if (options.verifyOnly) { continue; From feae5afd334acfa8ea4740b7c7591c7584df5993 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 07:28:04 -0400 Subject: [PATCH 75/92] tests: extend coverage of token-list --- src/parse/token-list.spec.ts | 199 +++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 src/parse/token-list.spec.ts diff --git a/src/parse/token-list.spec.ts b/src/parse/token-list.spec.ts new file mode 100644 index 0000000..b371b1a --- /dev/null +++ b/src/parse/token-list.spec.ts @@ -0,0 +1,199 @@ +import '../../jest/helpers'; + +import TokenType from '../types/token-types'; +import IParseOptions from '../types/options'; + +import Token from './token'; +import TokenList from './token-list'; + +test('Exports a constructor', () => { + expect(typeof TokenList).toBe('function'); + expect(TokenList.prototype).toBeDefined(); +}); + +test('Has .toJSON() function', () => { + expect(TokenList.prototype).toHaveOwnProperty('toJSON'); + expect(typeof TokenList.prototype.toJSON).toBe('function'); +}); + +test('Has .evaluate() function', () => { + expect(TokenList.prototype).toHaveOwnProperty('evaluate'); + expect(typeof TokenList.prototype.evaluate).toBe('function'); +}); + +test('Constructor errors when token is nullish', () => { + expect(() => { + // @ts-expect-error: Testing nullish token + new TokenList(); + }).toThrow(); +}); + +test('Constructor errors when token is not an object', () => { + expect(() => { + // @ts-expect-error: Testing invalid token + new TokenList(true); + }).toThrow(); +}); + +test('Constructor errors when token.value is nullish', () => { + expect(() => { + // @ts-expect-error: Testing nullish token.value + new TokenList({}); + }).toThrow(); +}); + +test('Constructor errors when token.value is not an array', () => { + expect(() => { + // @ts-expect-error: Testing invalid token.value + new TokenList({value: ''}); + }).toThrow(); +}); + +test('Constructor errors when token.value contains a non-token instance', () => { + expect(() => { + // @ts-expect-error: Testing invalid token.value + new TokenList({value: ['']}); + }).toThrow(); +}); + +test('Constructs without error', () => { + expect(() => new TokenList({value: []})).not.toThrow(); +}); + +test('Instance to be instance of TokenList and Token', () => { + const token = new TokenList({value: []}); + expect(token).toBeInstanceOf(TokenList); + expect(token).toBeInstanceOf(Token); +}); + +test('.type is set to TOKENLIST', () => { + const token = new TokenList({value: []}); + expect(token).toHaveOwnProperty('type', TokenType.TOKENLIST); +}); + +test('.value is set to input', () => { + const value : Token[] = []; + const token = new TokenList({ value }); + expect(token).toHaveOwnProperty('value', value); +}); + +test('.toJSON() calls super.toJSON()', () => { + const spy = jest.spyOn(Token.prototype, 'toJSON'); + const token = new TokenList({ value: [] }); + expect(() => token.toJSON()).not.toThrow(); + expect(spy).toHaveBeenCalledTimes(1); + jest.clearAllMocks(); +}); + +test('.toJSON() returns valid representation', () => { + const value = new Token(); + let calls = 0; + value.toJSON = function(this: Token) : Record { + calls += 1; + return Token.prototype.toJSON.call(this); + }; + + const token = new TokenList({value: [value]}); + const json = token.toJSON(); + expect(calls).toBe(1); + + expect(json).toHaveOwnProperty('value'); + expect(Array.isArray(json.value)).toBeTruthy(); + expect((json.value).length).toBe(1); +}); + +test('.evaluate() calls evaluate for values/falls back to defaults for inputs', async () => { + const value = new Token(); + + let receivedOptions : void | IParseOptions = undefined; + let receivedMeta : void | unknown = undefined; + value.evaluate = async function(options: IParseOptions, meta: unknown) : Promise { + receivedOptions = options; + receivedMeta = meta; + return; + }; + const token = new TokenList({value: [value]}); + + // @ts-expect-error : Testing nulled inputs + await token.evaluate(); + expect(receivedOptions).toBeDefined(); + expect(receivedMeta).toBeDefined(); +}); + +test('.evaluate() returns undefined when options.verifyOnly is specified', async () => { + const value = new Token(); + value.evaluate = async function() : Promise { + return 'text'; + } + const token = new TokenList({value: [value]}); + + const result = await token.evaluate({verifyOnly: true}, {}); + expect(result).toBeUndefined(); +}); + +test('.evaluate() sets result to value when result is not set', async () => { + const value = new Token(); + value.evaluate = async function() : Promise { + return 'test'; + } + const token = new TokenList({value: [value]}); + + const result = await token.evaluate({}, {}); + expect(result).toBe('test'); +}); + +test('.evaluate() returns null if only a single value is given and its null', async () => { + const value = new Token(); + value.evaluate = async function() : Promise { + return null; + } + const token = new TokenList({value: [value]}); + + const result = await token.evaluate({}, {}); + expect(result).toBeNull(); +}); + +test('.evaluate() does not append non-textable values', async () => { + const value1 = new Token(); + value1.evaluate = async function() : Promise { return 'a'; } + + const value2 = new Token(); + value2.evaluate = async function() : Promise { return; } + + const value3 = new Token(); + value3.evaluate = async function() : Promise { return null; } + + const value4 = new Token(); + value4.evaluate = async function() : Promise { return 'b'; } + + const token = new TokenList({value: [value1, value2, value3, value4]}); + + const result = await token.evaluate({}, {}); + expect(result).toBe('ab'); +}); + +test('.evaluate() converts to text when appending', async () => { + const value1 = new Token(); + value1.evaluate = async function() : Promise { return {}; } + + const value2 = new Token(); + value2.evaluate = async function() : Promise { return 'a'; } + + const token = new TokenList({value: [value1, value2]}); + + const result = await token.evaluate({}, {}); + expect(result).toBe('{}a'); +}); + +test('.evaluate() ignores functions', async () => { + const value1 = new Token(); + value1.evaluate = async function() : Promise { return (()=>1); } + + const value2 = new Token(); + value2.evaluate = async function() : Promise { return 'a'; } + + const token = new TokenList({value: [value1, value2]}); + + const result = await token.evaluate({}, {}); + expect(result).toBe('a'); +}); \ No newline at end of file From e15fe267efd7bad26bea4141dbd7494cbfada56b Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 08:06:56 -0400 Subject: [PATCH 76/92] fix: add null check --- src/helpers/has.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/has.ts b/src/helpers/has.ts index d1256e1..7943413 100644 --- a/src/helpers/has.ts +++ b/src/helpers/has.ts @@ -1,4 +1,4 @@ const hasOwnProperty = Object.prototype.hasOwnProperty; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -export default (subject: any, key: string) => (hasOwnProperty.call(subject, key) && subject[key] !== undefined); \ No newline at end of file +export default (subject: any, key: string) => (subject != null && hasOwnProperty.call(subject, key) && subject[key] !== undefined); \ No newline at end of file From 3b386ce5c3fd2b1834a4201dfa4192d875a07d5b Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 08:07:12 -0400 Subject: [PATCH 77/92] fix: add object check --- src/helpers/to-number.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/helpers/to-number.ts b/src/helpers/to-number.ts index be2efbe..ea3913b 100644 --- a/src/helpers/to-number.ts +++ b/src/helpers/to-number.ts @@ -1,5 +1,9 @@ export default (subject: unknown) : null | number => { - if (subject != null && subject !== '') { + if ( + subject != null && + typeof subject !== 'object' && + subject !== '' + ) { subject = Number(subject); if (Number.isFinite(subject)) { return subject; From 8ef7d8ea6aacd5f5880268bcaf20d6cdd4ccfdbf Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 08:07:26 -0400 Subject: [PATCH 78/92] tests: extend overage --- src/helpers/has.spec.ts | 20 ++++++++++++++++++++ src/helpers/is-primitive.spec.ts | 27 +++++++++++++++++++++++++++ src/helpers/to-number.spec.ts | 19 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/helpers/has.spec.ts create mode 100644 src/helpers/is-primitive.spec.ts create mode 100644 src/helpers/to-number.spec.ts diff --git a/src/helpers/has.spec.ts b/src/helpers/has.spec.ts new file mode 100644 index 0000000..19d7df9 --- /dev/null +++ b/src/helpers/has.spec.ts @@ -0,0 +1,20 @@ +import has from './has'; + +test('Should return false for nullish values', () => { + // @ts-expect-error: Testing no value + expect(has()).toBe(false); + + // @ts-expect-error: Testing undefined value + expect(has(undefined)).toBe(false); + + // @ts-expect-error: Testing null value + expect(has(null)).toBe(false); +}); + +test('It should return false for prototype members', () => { + expect(has([], 'split')).toBe(false); +}); + +test('It should return true for own members', () => { + expect(has({key: 'value'}, 'key')).toBe(true); +}); \ No newline at end of file diff --git a/src/helpers/is-primitive.spec.ts b/src/helpers/is-primitive.spec.ts new file mode 100644 index 0000000..2f32dd7 --- /dev/null +++ b/src/helpers/is-primitive.spec.ts @@ -0,0 +1,27 @@ +import isPrimitive from './is-primitive'; + +test('Returns true for boolean', () => { + expect(isPrimitive(true)).toBe(true); + expect(isPrimitive(false)).toBe(true); +}); + +test('Returns true for finite numbers, and false for other numbers', () => { + expect(isPrimitive(1)).toBe(true); + expect(isPrimitive(NaN)).toBe(false); + expect(isPrimitive(Infinity)).toBe(false); +}); + +test('Returns true for literal strings, false for instances', () => { + expect(isPrimitive('')).toBe(true); + expect(isPrimitive(new String(''))).toBe(false); +}); + +test('All others should be false', () => { + // @ts-expect-error: Testing empty input + expect(isPrimitive()).toBe(false); + expect(isPrimitive(undefined)).toBe(false); + expect(isPrimitive(null)).toBe(false); + expect(isPrimitive([])).toBe(false); + expect(isPrimitive({})).toBe(false); + expect(isPrimitive(()=>1)).toBe(false); +}); diff --git a/src/helpers/to-number.spec.ts b/src/helpers/to-number.spec.ts new file mode 100644 index 0000000..ec7ecd1 --- /dev/null +++ b/src/helpers/to-number.spec.ts @@ -0,0 +1,19 @@ +import toNumber from './to-number'; + +test('Returns null for non-numerics', () => { + //@ts-expect-error: Testing empty input + expect(toNumber()).toBe(null); + expect(toNumber(undefined)).toBe(null); + expect(toNumber(null)).toBe(null); + expect(toNumber('')).toBe(null); + expect(toNumber([])).toBe(null); + expect(toNumber({})).toBe(null); + expect(toNumber(()=>1)).toBe(null); + expect(toNumber(NaN)).toBe(null); + expect(toNumber(Infinity)).toBe(null); +}); + +test('Returns number for numeric', () => { + expect(toNumber(1)).toBe(1); + expect(toNumber('1')).toBe(1); +}) \ No newline at end of file From 7f3df02fefcd75f816c7695f15a185f8be5c33e2 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 08:07:47 -0400 Subject: [PATCH 79/92] chore(tests): only include src/ in coverage report --- jest.config.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index a7d3176..4d75ca9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,5 +9,8 @@ module.exports = { tsconfig: "./tsconfig.json" } ] - } + }, + collectCoverageFrom: [ + "src/**/*{!(.spec),}.ts" + ] }; \ No newline at end of file From 14f242cf26d4861079c4202c6493c98c61e19b9a Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 08:31:19 -0400 Subject: [PATCH 80/92] fix: add function check --- src/helpers/to-text.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/helpers/to-text.ts b/src/helpers/to-text.ts index 6ff8bc9..501f748 100644 --- a/src/helpers/to-text.ts +++ b/src/helpers/to-text.ts @@ -1,7 +1,10 @@ import isPrimitive from "./is-primitive"; export default (subject: unknown) : string | void => { - if (subject != null) { + if ( + subject != null && + typeof subject !== 'function' + ) { if (isPrimitive(subject)) { return String(subject); From 7ab00aa8e1e8c3fc8be99f63e2e19c24447167b4 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 08:31:36 -0400 Subject: [PATCH 81/92] chore(tests): expand coverage --- src/helpers/to-text.spec.ts | 20 +++++++++++++++++ src/helpers/whitespace.spec.ts | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/helpers/to-text.spec.ts create mode 100644 src/helpers/whitespace.spec.ts diff --git a/src/helpers/to-text.spec.ts b/src/helpers/to-text.spec.ts new file mode 100644 index 0000000..ad64264 --- /dev/null +++ b/src/helpers/to-text.spec.ts @@ -0,0 +1,20 @@ +import toText from './to-text'; + +test('Nullish values return undefined', () => { + // @ts-expect-error: Testing no input + expect(toText()).toBeUndefined(); + expect(toText(undefined)).toBeUndefined(); + expect(toText(null)).toBeUndefined(); + expect(toText(()=>1)).toBeUndefined(); +}); + +test('Primitive values should get converted to simple text', () => { + expect(toText(true)).toBe('true'); + expect(toText(false)).toBe('false'); + expect(toText(10)).toBe('10'); + expect(toText('text')).toBe('text'); +}); + +test('Object should be converted to json', () => { + expect(toText({})).toBe('{}'); +}); \ No newline at end of file diff --git a/src/helpers/whitespace.spec.ts b/src/helpers/whitespace.spec.ts new file mode 100644 index 0000000..7a65fa1 --- /dev/null +++ b/src/helpers/whitespace.spec.ts @@ -0,0 +1,40 @@ +import { + is, + consume +} from './whitespace'; + +test('Export \'is\' as a function', () => { + expect(typeof is).toBe('function'); +}); + +test('is() returns undefined if token is not whitespace', () => { + expect(is([], 0)).toBe(false) + expect(is([{position: 0, value: ''}], 0)).toBe(false); +}); + +test('is() returns true for whitespace tokens', () => { + expect(is([{position: 0, value: ' '}], 0)).toBe(true); + expect(is([{position: 0, value: '\t'}], 0)).toBe(true); + expect(is([{position: 0, value: '\n'}], 0)).toBe(true); + expect(is([{position: 0, value: '\t'}], 0)).toBe(true); +}); + +test('Export \'consume\' as a function', () => { + expect(typeof consume).toBe('function'); +}); + +test('consume() does not progress cursor for non whitespace', () => { + expect(consume([ + {position: 0, value: 'a'} + ],0)).toBe(0); +}); + +test('consume() progresses beyond whitespace', () => { + expect(consume([ + {position: 0, value: ' '}, + {position: 1, value: '\t'}, + {position: 2, value: '\n'}, + {position: 3, value: '\r'}, + {position: 4, value: 'a'} + ],0)).toBe(4); +}); \ No newline at end of file From 5483f5a85c258899033e5cdaccd85b053aae2fe0 Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 19:28:02 -0400 Subject: [PATCH 82/92] rebase: change argumentsQuantifier to quantifier --- .../comparison/operators/contains.ts | 2 +- .../comparison/operators/equal-loose.ts | 2 +- .../comparison/operators/equal-strict.ts | 2 +- .../operators/comparison/operators/exists.ts | 2 +- .../operators/greater-than-equal.ts | 2 +- .../comparison/operators/greater-than.ts | 2 +- .../operators/comparison/operators/is-bool.ts | 2 +- .../operators/comparison/operators/is-null.ts | 2 +- .../comparison/operators/less-than-equal.ts | 2 +- .../comparison/operators/less-than.ts | 2 +- .../comparison/operators/numerical.ts | 2 +- .../operators/comparison/operators/regex.ts | 2 +- .../comparison/operators/wildcard.ts | 2 +- .../operators/logical/operators/and.ts | 2 +- .../operators/logical/operators/not.ts | 2 +- .../operators/logical/operators/or.ts | 2 +- src/parse/condition/operators/token/index.ts | 55 +++++++++++++++---- src/parse/condition/tokenize/comparison.ts | 4 +- src/parse/condition/tokenize/logical-not.ts | 2 +- .../condition/tokenize/logical-operator.ts | 2 +- 20 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/parse/condition/operators/comparison/operators/contains.ts b/src/parse/condition/operators/comparison/operators/contains.ts index bf391c9..5c584de 100644 --- a/src/parse/condition/operators/comparison/operators/contains.ts +++ b/src/parse/condition/operators/comparison/operators/contains.ts @@ -5,7 +5,7 @@ import { type default as IParseOptions } from '../../../../../types/options'; export default { name: 'contains', description: "Checks if the left operand contains the right operand", - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, cased: true, alias: ['contains'], inverse: { diff --git a/src/parse/condition/operators/comparison/operators/equal-loose.ts b/src/parse/condition/operators/comparison/operators/equal-loose.ts index 253ed45..fc8a0ee 100644 --- a/src/parse/condition/operators/comparison/operators/equal-loose.ts +++ b/src/parse/condition/operators/comparison/operators/equal-loose.ts @@ -7,7 +7,7 @@ import toNumber from '../../../../../helpers/to-number'; export default { name: 'equals-loose', description: "Checks if operands are loosely equal", - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, alias: ['=='], inverse: { description: "Checks if operands are not loosely equal", diff --git a/src/parse/condition/operators/comparison/operators/equal-strict.ts b/src/parse/condition/operators/comparison/operators/equal-strict.ts index 95cb33d..5516b2a 100644 --- a/src/parse/condition/operators/comparison/operators/equal-strict.ts +++ b/src/parse/condition/operators/comparison/operators/equal-strict.ts @@ -4,7 +4,7 @@ import { type default as IParseOptions } from '../../../../../types/options'; export default { name: 'equal-strict', description: "Checks if operands are strictly equal", - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, alias: ['==='], inverse: { description: "Checks if operands are not strictly equal", diff --git a/src/parse/condition/operators/comparison/operators/exists.ts b/src/parse/condition/operators/comparison/operators/exists.ts index ff2a6a3..4eaecbb 100644 --- a/src/parse/condition/operators/comparison/operators/exists.ts +++ b/src/parse/condition/operators/comparison/operators/exists.ts @@ -4,7 +4,7 @@ import { type default as IParseOptions } from '../../../../../types/options'; export default { name: 'exists', description: "Checks if operands are strictly equal", - arguments: ArgumentsQuantifier.LEFTONLY, + quantifier: ArgumentsQuantifier.LEFTONLY, alias: ['exists'], inverse: { description: "Checks if operands are not strictly equal", diff --git a/src/parse/condition/operators/comparison/operators/greater-than-equal.ts b/src/parse/condition/operators/comparison/operators/greater-than-equal.ts index a4dcf97..2131dc9 100644 --- a/src/parse/condition/operators/comparison/operators/greater-than-equal.ts +++ b/src/parse/condition/operators/comparison/operators/greater-than-equal.ts @@ -4,7 +4,7 @@ import toNumber from '../../../../../helpers/to-number'; export default { name: "greater-than-or-equal", - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, description: "Checks if the left operand is numerical and greater than or equal to the right operand", alias: ['>='], handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { diff --git a/src/parse/condition/operators/comparison/operators/greater-than.ts b/src/parse/condition/operators/comparison/operators/greater-than.ts index cd5ee1c..d93a886 100644 --- a/src/parse/condition/operators/comparison/operators/greater-than.ts +++ b/src/parse/condition/operators/comparison/operators/greater-than.ts @@ -5,7 +5,7 @@ import toNumber from '../../../../../helpers/to-number'; export default { name: "greater-than", description: "Checks if the left operand is numerical and greater than to the right operand", - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, alias: ['>='], handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { const { left, right } = state; diff --git a/src/parse/condition/operators/comparison/operators/is-bool.ts b/src/parse/condition/operators/comparison/operators/is-bool.ts index 737456e..6c8458f 100644 --- a/src/parse/condition/operators/comparison/operators/is-bool.ts +++ b/src/parse/condition/operators/comparison/operators/is-bool.ts @@ -19,7 +19,7 @@ const toBool = (subject: unknown) => { export default { name: "isbool", - arguments: ArgumentsQuantifier.LEFTONLY, + quantifier: ArgumentsQuantifier.LEFTONLY, description: "Checks if the left operand is boolean and if specified matches the right operand", alias: ['isbool'], inverse: { diff --git a/src/parse/condition/operators/comparison/operators/is-null.ts b/src/parse/condition/operators/comparison/operators/is-null.ts index c58441f..f1abf60 100644 --- a/src/parse/condition/operators/comparison/operators/is-null.ts +++ b/src/parse/condition/operators/comparison/operators/is-null.ts @@ -3,8 +3,8 @@ import { type default as IParseOptions } from '../../../../../types/options'; export default { name: 'isnull', - arguments: ArgumentsQuantifier.LEFTONLY, description: "Checks if the left operand is null or undefined", + quantifier: ArgumentsQuantifier.LEFTONLY, alias: ['isnull'], inverse: { description: "Checks if the left operand is not null or undefined", diff --git a/src/parse/condition/operators/comparison/operators/less-than-equal.ts b/src/parse/condition/operators/comparison/operators/less-than-equal.ts index aca4706..88b856a 100644 --- a/src/parse/condition/operators/comparison/operators/less-than-equal.ts +++ b/src/parse/condition/operators/comparison/operators/less-than-equal.ts @@ -5,7 +5,7 @@ import toNumber from '../../../../../helpers/to-number'; export default { name: 'less-than-or-equal', description: "Checks if the left operand is numerical and less than or equal to the right operand", - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, alias: ['<='], handle: async function(options: IParseOptions, meta: unknown, state: IHandleState) : Promise { const { left, right } = state; diff --git a/src/parse/condition/operators/comparison/operators/less-than.ts b/src/parse/condition/operators/comparison/operators/less-than.ts index 299c071..e4aa926 100644 --- a/src/parse/condition/operators/comparison/operators/less-than.ts +++ b/src/parse/condition/operators/comparison/operators/less-than.ts @@ -4,8 +4,8 @@ import toNumber from '../../../../../helpers/to-number'; export default { name: 'less-than', - arguments: ArgumentsQuantifier.RIGHTREQUIRED, description: "Checks if the left operand is numerical and less than the right operand", + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, alias: ['<'], handle: async function(options: IParseOptions, meta: unknown, state: IHandleState) : Promise { const { left, right } = state; diff --git a/src/parse/condition/operators/comparison/operators/numerical.ts b/src/parse/condition/operators/comparison/operators/numerical.ts index 8d1d0b4..2ef2ef1 100644 --- a/src/parse/condition/operators/comparison/operators/numerical.ts +++ b/src/parse/condition/operators/comparison/operators/numerical.ts @@ -6,7 +6,7 @@ const isRange = /^((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))-((?:[+-]?\d+(?:\.\d+)? export default { name: 'numerical', - arguments: ArgumentsQuantifier.RIGHTOPTIONAL, + quantifier: ArgumentsQuantifier.RIGHTOPTIONAL, description: "Checks if the left operand is numerical and if specified within the range of the right operand (inclusive)", alias: ['isnum', 'isnumber'], inverse: { diff --git a/src/parse/condition/operators/comparison/operators/regex.ts b/src/parse/condition/operators/comparison/operators/regex.ts index a45a97f..3871b65 100644 --- a/src/parse/condition/operators/comparison/operators/regex.ts +++ b/src/parse/condition/operators/comparison/operators/regex.ts @@ -4,7 +4,7 @@ import toText from '../../../../../helpers/to-text'; export default { name: 'regex', - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, description: "Checks if the left operand is a match of the right operand regex", alias: ['regex'], inverse: { diff --git a/src/parse/condition/operators/comparison/operators/wildcard.ts b/src/parse/condition/operators/comparison/operators/wildcard.ts index fa4bc7e..9a8a233 100644 --- a/src/parse/condition/operators/comparison/operators/wildcard.ts +++ b/src/parse/condition/operators/comparison/operators/wildcard.ts @@ -93,7 +93,7 @@ const toRegExp = (subject: string, caseSensitive: boolean) : RegExp => { export default { name: 'wildcard', - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, description: "Checks if the left operand is a match of the right operand wildcard", alias: ['iswm'], cased: true, diff --git a/src/parse/condition/operators/logical/operators/and.ts b/src/parse/condition/operators/logical/operators/and.ts index bb482bc..5c4b6de 100644 --- a/src/parse/condition/operators/logical/operators/and.ts +++ b/src/parse/condition/operators/logical/operators/and.ts @@ -4,7 +4,7 @@ import { type IOperator, type IHandleStateDeferred, ArgumentsQuantifier } from ' export default { name: 'logical-and', description: "Checks if two conditions are truthy", - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, defer: true, alias: ['&&'], handle: async function (options: IParseOptions, meta: unknown, state: IHandleStateDeferred) : Promise { diff --git a/src/parse/condition/operators/logical/operators/not.ts b/src/parse/condition/operators/logical/operators/not.ts index f79b564..060a22a 100644 --- a/src/parse/condition/operators/logical/operators/not.ts +++ b/src/parse/condition/operators/logical/operators/not.ts @@ -4,7 +4,7 @@ import { type IOperator, type IHandleStateDeferred, ArgumentsQuantifier } from ' export default { name: 'logical-not', description: "Checks if two conditions are truthy", - arguments: ArgumentsQuantifier.LEFTONLY, + quantifier: ArgumentsQuantifier.LEFTONLY, defer: true, alias: [], handle: async function (options: IParseOptions, meta: unknown, state: IHandleStateDeferred) : Promise { diff --git a/src/parse/condition/operators/logical/operators/or.ts b/src/parse/condition/operators/logical/operators/or.ts index 2cc8ff3..c6f1450 100644 --- a/src/parse/condition/operators/logical/operators/or.ts +++ b/src/parse/condition/operators/logical/operators/or.ts @@ -4,7 +4,7 @@ import { type IOperator, type IHandleStateDeferred, ArgumentsQuantifier } from ' export default { name: 'logical-or', description: "Checks if two conditions are truthy", - arguments: ArgumentsQuantifier.RIGHTREQUIRED, + quantifier: ArgumentsQuantifier.RIGHTREQUIRED, defer: true, alias: ['||'], handle: async function (options: IParseOptions, meta: unknown, state: IHandleStateDeferred) : Promise { diff --git a/src/parse/condition/operators/token/index.ts b/src/parse/condition/operators/token/index.ts index a1c30b5..099ddab 100644 --- a/src/parse/condition/operators/token/index.ts +++ b/src/parse/condition/operators/token/index.ts @@ -29,7 +29,7 @@ export type IHandleFn = (options: IParseOptions, meta: unknown, state: IHandleSt export interface IOperator { name: string; description: string; - arguments: ArgumentsQuantifier; + quantifier: ArgumentsQuantifier; defer: boolean; cased?: boolean, alias: string[]; @@ -43,31 +43,64 @@ export interface IOperator { export interface IOperatorToken extends IToken { caseSensitive?: boolean; - argumentsQuantifier: ArgumentsQuantifier; + quantifier: ArgumentsQuantifier; arguments: Token[]; defer?: boolean; handle: IHandleFn; } export default class OperatorToken extends Token { - public caseSensitive : boolean; + public quantifier: ArgumentsQuantifier; public arguments: Token[]; public defer: boolean; + public caseSensitive : boolean; public handle : IHandleFn; constructor(token: IOperatorToken) { - if (token.arguments == null || !Array.isArray(token.arguments)) { - throw new Error('TODO - ExpressionError - invalid arguments list'); + if (token == null) { + throw new Error('TODO - ExpressionError: token must not be nullish'); + } + + if (token.quantifier == null) { + throw new Error('TODO - ExpressionError: arguments quantifier not specified'); + } + if (!Number.isFinite(token.quantifier)) { + throw new Error('TODO - ExpressionError: must be a number'); + } + + if (token.arguments == null) { + throw new Error('TODO - ExpressionError: arguments list not specified'); + } + if (!Array.isArray(token.arguments)) { + throw new Error('TODO - ExpressionError: arguments list must be an array') } if (token.arguments.length === 0) { - throw new Error('TODO - ExpressionError - at least one argument is required'); + throw new Error('TODO - ExpressionError: at least one argument is required'); } if (token.arguments.length > 2) { - throw new Error('TODO - ExpressionError - too many arguments specified') + throw new Error('TODO - ExpressionError: too many arguments specified') + } + if (token.arguments.length !== 2 && token.quantifier === ArgumentsQuantifier.RIGHTREQUIRED) { + throw new Error('TODO - ExpressionError: right argument required'); + } + + if (token.defer != null && token.defer !== false && token.defer !== true) { + throw new Error('TODO - ExpressionError: if specified defer must be boolean'); + } + + if (token.caseSensitive != null && token.caseSensitive !== false && token.caseSensitive !== true) { + throw new Error('TODO - ExpressionError: if specified caseSensitive must be boolean'); } - if (token.arguments.length !== 2 && token.argumentsQuantifier === ArgumentsQuantifier.RIGHTREQUIRED) { - throw new Error('TODO - ExpressionError - right argument required'); + if (token.handle == null) { + throw new Error('TODO - ExpressionError: handle function not specified'); + } + if ( + typeof token.handle !== 'function' && + //eslint-disable-next-line @typescript-eslint/no-explicit-any + token.handle instanceof Function + ) { + throw new Error('TODO - ExpressionError: specified handle must be a function'); } super({ @@ -75,8 +108,10 @@ export default class OperatorToken extends Token { type: TokenType.OPERATOR }); - this.caseSensitive = token.caseSensitive === true; + this.quantifier = token.quantifier; + this.arguments = [ ...(token.arguments) ]; this.defer = token.defer === true; + this.caseSensitive = token.caseSensitive === true; this.handle = token.handle; } diff --git a/src/parse/condition/tokenize/comparison.ts b/src/parse/condition/tokenize/comparison.ts index c6978f5..9827368 100644 --- a/src/parse/condition/tokenize/comparison.ts +++ b/src/parse/condition/tokenize/comparison.ts @@ -87,7 +87,7 @@ export default async (state: ITokenizeState, asArgument = true) : Promiseoperator).arguments; + const argQuant = (operator).quantifier; if (argQuant !== ArgumentsQuantifier.LEFTONLY) { const mockState : ITokenizeState = { options: { ...options }, @@ -120,7 +120,7 @@ export default async (state: ITokenizeState, asArgument = true) : Promiseoperator).name, - argumentsQuantifier: (operator).arguments, + quantifier: (operator).quantifier, arguments: args, handle: (operator).handle }); diff --git a/src/parse/condition/tokenize/logical-not.ts b/src/parse/condition/tokenize/logical-not.ts index cc4389d..653e281 100644 --- a/src/parse/condition/tokenize/logical-not.ts +++ b/src/parse/condition/tokenize/logical-not.ts @@ -36,7 +36,7 @@ export default async (state: ITokenizeState) : Promise => { state.output = new OperatorToken({ position: startPosition, value: notOperator.name, - argumentsQuantifier: notOperator.arguments, + quantifier: notOperator.quantifier, arguments: [mockState.output], handle: notOperator.handle }); diff --git a/src/parse/condition/tokenize/logical-operator.ts b/src/parse/condition/tokenize/logical-operator.ts index 2af584f..82f5766 100644 --- a/src/parse/condition/tokenize/logical-operator.ts +++ b/src/parse/condition/tokenize/logical-operator.ts @@ -52,7 +52,7 @@ export default async (state: ITokenizeState, leftCondition: Token, asArgument = position: startPosition, value: operator.name, arguments: [leftCondition, rightCondition], - argumentsQuantifier: operator.arguments, + quantifier: operator.quantifier, handle: operator.handle }); return true; From b3489f8369bb03cef3a381f445784850d8fe7cfe Mon Sep 17 00:00:00 2001 From: SReject Date: Sat, 24 Sep 2022 19:28:36 -0400 Subject: [PATCH 83/92] fix: verify inputs --- src/parse/if/token/index.ts | 42 ++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/parse/if/token/index.ts b/src/parse/if/token/index.ts index 3456264..29ce377 100644 --- a/src/parse/if/token/index.ts +++ b/src/parse/if/token/index.ts @@ -3,7 +3,7 @@ import type IParserOptions from '../../../types/options'; import Token from '../../token'; -import { type OperatorToken } from '../../condition'; +import { OperatorToken } from '../../condition'; export interface IIfStatementToken { position: number; @@ -18,6 +18,28 @@ export default class IfStatementToken extends Token { public whenFalse?: Token; constructor(token: IIfStatementToken) { + if (token == null) { + throw new Error('TODO - ExpressionError: token not specified'); + } + if (typeof token !== 'object') { + throw new Error('TODO - ExpressionError: token must be an object'); + } + if (token.condition == null) { + throw new Error('TODO - ExpressionError: condition not specified'); + } + if (!(token.condition instanceof OperatorToken)) { + throw new Error('TODO - ExpressionError: condition must be an instance of OperatorToken'); + } + if (token.whenTrue == null) { + throw new Error('TODO - ExpressionError: truthy argument must be specified'); + } + if (!(token.whenTrue instanceof Token)) { + throw new Error('TODO - ExpressionError: truthy argument must be a token instance'); + } + if (token.whenFalse !== null && !(token.whenFalse instanceof Token)) { + throw new Error('TODO - ExpressionError: falsy argument must be a token instance'); + } + super({ ...token, type: TokenType.IFSTATEMENT, @@ -28,6 +50,15 @@ export default class IfStatementToken extends Token { this.whenFalse = token.whenFalse; } + toTJSON() : object { + return { + ...(super.toJSON()), + condition: this.condition.toJSON(), + whenTrue: this.whenTrue.toJSON(), + whenFalse: this.whenFalse?.toJSON() + } + } + async evaluate(options: IParserOptions, meta: unknown) : Promise { if (options == null) { options = {}; @@ -56,13 +87,4 @@ export default class IfStatementToken extends Token { return this.whenFalse.evaluate(options, {...(meta) }); } } - - toToken() : object { - return { - ...(super.toJSON()), - condition: this.condition.toJSON(), - whenTrue: this.whenTrue.toJSON(), - whenFalse: this.whenFalse?.toJSON() - } - } } \ No newline at end of file From 1e6d1759b65b3f1ac360d4233540a191bf6f8969 Mon Sep 17 00:00:00 2001 From: SReject Date: Sun, 25 Sep 2022 06:24:54 -0400 Subject: [PATCH 84/92] chore(tests): fix transform regex --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 4d75ca9..92545cf 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,7 +3,7 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', transform: { - '.ts$': [ + '\\.ts$': [ 'ts-jest', { tsconfig: "./tsconfig.json" From fcf7caf653b99a7f1eda4b957c7b2291289c7cdb Mon Sep 17 00:00:00 2001 From: SReject Date: Sun, 25 Sep 2022 06:25:45 -0400 Subject: [PATCH 85/92] feat: remove 'statement' from names --- src/parse/if/token/index.ts | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/parse/if/token/index.ts b/src/parse/if/token/index.ts index 29ce377..72da2d5 100644 --- a/src/parse/if/token/index.ts +++ b/src/parse/if/token/index.ts @@ -5,19 +5,19 @@ import Token from '../../token'; import { OperatorToken } from '../../condition'; -export interface IIfStatementToken { - position: number; +export interface IIfToken { + position?: number; condition: OperatorToken; whenTrue: Token, whenFalse?: Token } -export default class IfStatementToken extends Token { +export default class IfToken extends Token { public condition: OperatorToken; public whenTrue: Token; public whenFalse?: Token; - constructor(token: IIfStatementToken) { + constructor(token: IIfToken) { if (token == null) { throw new Error('TODO - ExpressionError: token not specified'); } @@ -36,7 +36,7 @@ export default class IfStatementToken extends Token { if (!(token.whenTrue instanceof Token)) { throw new Error('TODO - ExpressionError: truthy argument must be a token instance'); } - if (token.whenFalse !== null && !(token.whenFalse instanceof Token)) { + if (token.whenFalse != null && !(token.whenFalse instanceof Token)) { throw new Error('TODO - ExpressionError: falsy argument must be a token instance'); } @@ -50,13 +50,16 @@ export default class IfStatementToken extends Token { this.whenFalse = token.whenFalse; } - toTJSON() : object { - return { + toJSON() : Record { + const result : Record = { ...(super.toJSON()), condition: this.condition.toJSON(), - whenTrue: this.whenTrue.toJSON(), - whenFalse: this.whenFalse?.toJSON() + whenTrue: this.whenTrue.toJSON() + }; + if (this.whenFalse != null) { + result.whenFalse = (this.whenFalse).toJSON(); } + return result; } async evaluate(options: IParserOptions, meta: unknown) : Promise { @@ -68,23 +71,23 @@ export default class IfStatementToken extends Token { meta = {}; } - const res = await this.condition.evaluate(options, {...(meta) }); + const res = await this.condition.evaluate(options, meta); if (options.verifyOnly) { - await this.whenTrue.evaluate(options, {...(meta) }); + await this.whenTrue.evaluate(options, meta); if (this.whenFalse != null) { - await this.whenFalse.evaluate(options, {...(meta) }); + await this.whenFalse.evaluate(options, meta); } return; } if (res != null && res !== false) { - return this.whenTrue.evaluate(options, {...(meta) }); + return this.whenTrue.evaluate(options, meta); } if (this.whenFalse != null) { - return this.whenFalse.evaluate(options, {...(meta) }); + return this.whenFalse.evaluate(options, meta); } } } \ No newline at end of file From 726de0d8049f4da8b7c64a82271ec658eed23f9c Mon Sep 17 00:00:00 2001 From: SReject Date: Sun, 25 Sep 2022 06:26:01 -0400 Subject: [PATCH 86/92] chore(tests): increase coverage --- src/parse/if/token/index.spec.ts | 305 +++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 src/parse/if/token/index.spec.ts diff --git a/src/parse/if/token/index.spec.ts b/src/parse/if/token/index.spec.ts new file mode 100644 index 0000000..8dda246 --- /dev/null +++ b/src/parse/if/token/index.spec.ts @@ -0,0 +1,305 @@ +import '../../../../jest/helpers'; + +import IfToken from './index'; +import { ArgumentsQuantifier, OperatorToken } from '../../condition/'; +import Token from '../../token'; +import { TextToken } from '../../text'; + +test('Exports constructor', () => { + expect(typeof IfToken).toBe('function'); + expect(IfToken.prototype).toBeDefined(); +}); + +test('Has .toJSON() function', () => { + expect(IfToken.prototype).toHaveOwnProperty('toJSON'); + expect(typeof IfToken.prototype.toJSON).toBe('function'); +}); + +test('Has .evaluate function', () => { + expect(IfToken.prototype).toHaveOwnProperty('evaluate'); + expect(typeof IfToken.prototype.evaluate).toBe('function'); +}); + +test('Constructor throws when token is nullish', () => { + expect(() => { + // @ts-expect-error: Testing unspecified input + new IfToken() + }).toThrow(); + + expect(() => { + const input : void | null = undefined; + // @ts-expect-error: Testing undefined input + new IfToken(input); + }).toThrow(); + + expect(() => { + const input : void | null = null; + // @ts-expect-error: Testing null input + new IfToken(input); + }).toThrow(); +}); + +test('Consturctot throw when token is not an object', () => { + expect(() => { + // @ts-expect-error: Testing invalid token input + new IfToken('test') + }).toThrow(); +}); + +test('Constructor throws when condition is nullish', () => { + expect(() => { + // @ts-expect-error: Testing nullish condition input + new IfToken({}); + }).toThrow(); + + expect(() => { + // @ts-expect-error: Testing nullish condition input + new IfToken({condition: undefined}); + }).toThrow(); + + expect(() => { + // @ts-expect-error: Testing nullish condition input + new IfToken({condition: null}); + }).toThrow(); +}); + +test('Constructor throws when condition is not an OperatorToken instance', () => { + expect(() => { + // @ts-expect-error: Testing nullish condition input + new IfToken({condition: 'text'}); + }).toThrow(); +}); + +test('Constructor throws if whenTrue is nullish', () => { + const op = new OperatorToken({ + quantifier: ArgumentsQuantifier.LEFTONLY, + arguments: [new Token()], + handle: async () => false + }); + + expect(() => { + /*eslint-disable-next-line @typescript-eslint/ban-ts-comment*/ + // @ts-ignore: Testing whenTrue value is not specified + new IfToken({condition: op}); + }).toThrow(); + + expect(() => { + /*eslint-disable-next-line @typescript-eslint/ban-ts-comment*/ + // @ts-ignore: Testing whenTrue value is undefined + new IfToken({ condition: op, whenTrue: undefined }); + }).toThrow(); + + expect(() => { + /*eslint-disable-next-line @typescript-eslint/ban-ts-comment*/ + // @ts-ignore: Testing whenTrue value null + new IfToken({ condition: op, whenTrue: null }); + }).toThrow(); +}); + +test('Constructor throws if whenTrue is not a token', () => { + const op = new OperatorToken({ + quantifier: ArgumentsQuantifier.LEFTONLY, + arguments: [new Token()], + handle: async () => false + }); + + expect(() => { + const input = { condition: op, whenTrue: '' }; + // @ts-expect-error: Testing whenTrue value is not a Token + new IfToken(input); + }).toThrow(); +}); + +test('Constructor throws if whenFalse is specified but not a token', () => { + const op = new OperatorToken({ + quantifier: ArgumentsQuantifier.LEFTONLY, + arguments: [new Token()], + handle: async () => false + }); + + expect(() => { + // @ts-expect-error: Testing whenTrue value is not a Token + new IfToken({ condition: op, whenTrue: new Token(), whenFalse: '' }); + }).toThrow(); +}); + +test('Constructs without error', () => { + const op = new OperatorToken({ + quantifier: ArgumentsQuantifier.LEFTONLY, + arguments: [new Token()], + handle: async () => false + }); + expect(() => { + new IfToken({ + condition: op, + whenTrue: new Token() + }); + }).not.toThrow(); +}); + +test('.toJSON() calls super.toJSON', () => { + const spy = jest.spyOn(Token.prototype, 'toJSON'); + const token = new IfToken({ + condition: new OperatorToken({ + quantifier: ArgumentsQuantifier.LEFTONLY, + arguments: [new Token()], + handle: async () => false + }), + whenTrue: new Token() + }); + expect(() => token.toJSON()).not.toThrow(); + expect(spy).toHaveBeenCalled(); + jest.clearAllMocks(); +}); + +test('.toJSON() returns proper representation', () => { + expect.assertions(8); + + const toJSONResult = {}; + + let condToJSONCalls = 0; + const condition = new OperatorToken({ + quantifier: ArgumentsQuantifier.LEFTONLY, + arguments: [new Token()], + handle: async () => false + }); + condition.toJSON = () : Record => { + condToJSONCalls += 1; + return toJSONResult; + }; + + let whenTrueJSONCalls = 0; + const whenTrue = new Token(); + whenTrue.toJSON = () : Record => { + whenTrueJSONCalls += 1; + return toJSONResult; + }; + + let whenFalseJSONCalls = 0; + const whenFalse = new Token(); + whenFalse.toJSON = () : Record => { + whenFalseJSONCalls += 1; + return toJSONResult; + }; + + const token1 = new IfToken({ + condition, + whenTrue + }); + const result1 = token1.toJSON(); + expect(condToJSONCalls).toBe(1); + expect(result1).toHaveOwnProperty('condition', toJSONResult); + + expect(whenTrueJSONCalls).toBe(1); + expect(result1).toHaveOwnProperty('whenTrue', toJSONResult); + + expect(whenFalseJSONCalls).toBe(0); + expect(result1.whenFalse).toBeUndefined(); + + const token2 = new IfToken({ + condition, + whenTrue, + whenFalse + }); + const result2 = token2.toJSON(); + expect(whenFalseJSONCalls).toBe(1); + expect(result2).toHaveOwnProperty('whenFalse', toJSONResult); +}); + +test('.evaluate() calls evaluate for condition/falls back to defaults for inputs', async () => { + let conditionCalled = 0, + optionsSet = false, + metaSet = false, + whenTrueCalled = 0; + + const condition = new OperatorToken({ + position: 0, + value: 'test', + quantifier: ArgumentsQuantifier.LEFTONLY, + defer: false, + arguments: [new TextToken({value: 'text'})], + handle: async function (options, meta) : Promise { + conditionCalled += 1; + optionsSet = !!options; + metaSet = !!meta; + return true; + } + }); + + const whenTrue = new Token(); + whenTrue.evaluate = async function () { + whenTrueCalled += 1; + return false; + } + + const token = new IfToken({ + condition, + whenTrue: whenTrue + }); + + expect.assertions(4); + + // @ts-expect-error : testing empty inputs + await token.evaluate(); + + expect(conditionCalled).toBe(1); + expect(optionsSet).toBe(true); + expect(metaSet).toBe(true); + expect(whenTrueCalled).toBe(1); +}); + +test('.evaluate() evaluates all inputs when verifyOnly is true', async () => { + const condition = new OperatorToken({ + position: 0, + value: 'test', + quantifier: ArgumentsQuantifier.LEFTONLY, + defer: false, + arguments: [new TextToken({value: 'text'})], + handle: async function () : Promise { + return true; + } + }); + + let whenCalls = 0; + const when = new Token(); + when.evaluate = async function () { + whenCalls += 1; + return true; + } + + const token = new IfToken({ condition, whenTrue: when, whenFalse: when }); + + expect.assertions(2); + const result = await token.evaluate({verifyOnly: true}, {}); + + expect(result).toBeUndefined(); + expect(whenCalls).toBe(2); +}); + +test('.evaluate() evaluates false argument when condition is false and returns result', async () => { + const condition = new OperatorToken({ + position: 0, + value: 'test', + quantifier: ArgumentsQuantifier.LEFTONLY, + defer: false, + arguments: [new TextToken({value: 'text'})], + handle: async function () : Promise { + return false; + } + }); + + let whenCalls = 0; + const when = new Token(); + when.evaluate = async function () { + whenCalls += 1; + return 'falsey'; + } + + const token = new IfToken({ condition, whenTrue: new Token(), whenFalse: when }); + + expect.assertions(2); + const result = await token.evaluate({}, {}); + + expect(result).toBe('falsey') + expect(whenCalls).toBe(1); +}); \ No newline at end of file From 1f4453620e37852624693dc52dfc127ec35c75ed Mon Sep 17 00:00:00 2001 From: SReject Date: Sun, 25 Sep 2022 08:08:37 -0400 Subject: [PATCH 87/92] chore(tests): only output lcov coverage reports --- jest.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 92545cf..e66b945 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,5 +12,6 @@ module.exports = { }, collectCoverageFrom: [ "src/**/*{!(.spec),}.ts" - ] + ], + coverageReporters: ['lcov'] }; \ No newline at end of file From bd2a522704ba2452f6fc985165864393aaa3cfb7 Mon Sep 17 00:00:00 2001 From: SReject Date: Sun, 25 Sep 2022 08:10:21 -0400 Subject: [PATCH 88/92] rebase: move operators to own directory --- src/parse/condition/operators/comparison/index.ts | 4 ++-- .../operators/{contains.ts => contains/index.ts} | 4 ++-- .../operators/{equal-loose.ts => equal-loose/index.ts} | 8 ++++---- .../operators/{equal-strict.ts => equal-strict/index.ts} | 4 ++-- .../comparison/operators/{exists.ts => exists/index.ts} | 4 ++-- .../index.ts} | 6 +++--- .../operators/{greater-than.ts => greater-than/index.ts} | 6 +++--- .../comparison/operators/{is-bool.ts => is-bool/index.ts} | 4 ++-- .../comparison/operators/{is-null.ts => is-null/index.ts} | 4 ++-- .../{less-than-equal.ts => less-than-or-equal/index.ts} | 6 +++--- .../operators/{less-than.ts => less-than/index.ts} | 6 +++--- .../operators/{numerical.ts => numerical/index.ts} | 6 +++--- .../comparison/operators/{regex.ts => regex/index.ts} | 6 +++--- .../operators/{wildcard.ts => wildcard/index.ts} | 8 ++++---- src/parse/condition/operators/token/index.ts | 4 ++-- 15 files changed, 40 insertions(+), 40 deletions(-) rename src/parse/condition/operators/comparison/operators/{contains.ts => contains/index.ts} (92%) rename src/parse/condition/operators/comparison/operators/{equal-loose.ts => equal-loose/index.ts} (82%) rename src/parse/condition/operators/comparison/operators/{equal-strict.ts => equal-strict/index.ts} (86%) rename src/parse/condition/operators/comparison/operators/{exists.ts => exists/index.ts} (83%) rename src/parse/condition/operators/comparison/operators/{greater-than-equal.ts => greater-than-or-equal/index.ts} (81%) rename src/parse/condition/operators/comparison/operators/{greater-than.ts => greater-than/index.ts} (80%) rename src/parse/condition/operators/comparison/operators/{is-bool.ts => is-bool/index.ts} (92%) rename src/parse/condition/operators/comparison/operators/{is-null.ts => is-null/index.ts} (83%) rename src/parse/condition/operators/comparison/operators/{less-than-equal.ts => less-than-or-equal/index.ts} (80%) rename src/parse/condition/operators/comparison/operators/{less-than.ts => less-than/index.ts} (80%) rename src/parse/condition/operators/comparison/operators/{numerical.ts => numerical/index.ts} (89%) rename src/parse/condition/operators/comparison/operators/{regex.ts => regex/index.ts} (87%) rename src/parse/condition/operators/comparison/operators/{wildcard.ts => wildcard/index.ts} (93%) diff --git a/src/parse/condition/operators/comparison/index.ts b/src/parse/condition/operators/comparison/index.ts index b231459..74dcb96 100644 --- a/src/parse/condition/operators/comparison/index.ts +++ b/src/parse/condition/operators/comparison/index.ts @@ -6,11 +6,11 @@ import operatorContains from './operators/contains'; import operatorEqualLoose from './operators/equal-loose'; import operatorEqualStrict from './operators/equal-strict'; import operatorExists from './operators/exists'; -import operatorGreaterThanOrEqual from './operators/greater-than-equal'; +import operatorGreaterThanOrEqual from './operators/greater-than-or-equal'; import operatorGreaterThan from './operators/greater-than'; import operatorIsBool from './operators/is-bool'; import operatorIsNull from './operators/is-null'; -import operatorLessThanOrEqual from './operators/less-than-equal'; +import operatorLessThanOrEqual from './operators/less-than-or-equal'; import operatorLessThan from './operators/less-than'; import operatorNumerical from './operators/numerical'; import operatorRegex from './operators/regex'; diff --git a/src/parse/condition/operators/comparison/operators/contains.ts b/src/parse/condition/operators/comparison/operators/contains/index.ts similarity index 92% rename from src/parse/condition/operators/comparison/operators/contains.ts rename to src/parse/condition/operators/comparison/operators/contains/index.ts index 5c584de..cc099e8 100644 --- a/src/parse/condition/operators/comparison/operators/contains.ts +++ b/src/parse/condition/operators/comparison/operators/contains/index.ts @@ -1,6 +1,6 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; +import { type default as IParseOptions } from '../../../../../../types/options'; export default { name: 'contains', diff --git a/src/parse/condition/operators/comparison/operators/equal-loose.ts b/src/parse/condition/operators/comparison/operators/equal-loose/index.ts similarity index 82% rename from src/parse/condition/operators/comparison/operators/equal-loose.ts rename to src/parse/condition/operators/comparison/operators/equal-loose/index.ts index fc8a0ee..81d9497 100644 --- a/src/parse/condition/operators/comparison/operators/equal-loose.ts +++ b/src/parse/condition/operators/comparison/operators/equal-loose/index.ts @@ -1,8 +1,8 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; -import isPrimitive from '../../../../../helpers/is-primitive'; -import toNumber from '../../../../../helpers/to-number'; +import isPrimitive from '../../../../../../helpers/is-primitive'; +import toNumber from '../../../../../../helpers/to-number'; export default { name: 'equals-loose', diff --git a/src/parse/condition/operators/comparison/operators/equal-strict.ts b/src/parse/condition/operators/comparison/operators/equal-strict/index.ts similarity index 86% rename from src/parse/condition/operators/comparison/operators/equal-strict.ts rename to src/parse/condition/operators/comparison/operators/equal-strict/index.ts index 5516b2a..4d26405 100644 --- a/src/parse/condition/operators/comparison/operators/equal-strict.ts +++ b/src/parse/condition/operators/comparison/operators/equal-strict/index.ts @@ -1,5 +1,5 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; export default { name: 'equal-strict', diff --git a/src/parse/condition/operators/comparison/operators/exists.ts b/src/parse/condition/operators/comparison/operators/exists/index.ts similarity index 83% rename from src/parse/condition/operators/comparison/operators/exists.ts rename to src/parse/condition/operators/comparison/operators/exists/index.ts index 4eaecbb..e2ea2be 100644 --- a/src/parse/condition/operators/comparison/operators/exists.ts +++ b/src/parse/condition/operators/comparison/operators/exists/index.ts @@ -1,5 +1,5 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; export default { name: 'exists', diff --git a/src/parse/condition/operators/comparison/operators/greater-than-equal.ts b/src/parse/condition/operators/comparison/operators/greater-than-or-equal/index.ts similarity index 81% rename from src/parse/condition/operators/comparison/operators/greater-than-equal.ts rename to src/parse/condition/operators/comparison/operators/greater-than-or-equal/index.ts index 2131dc9..a8b91db 100644 --- a/src/parse/condition/operators/comparison/operators/greater-than-equal.ts +++ b/src/parse/condition/operators/comparison/operators/greater-than-or-equal/index.ts @@ -1,6 +1,6 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; -import toNumber from '../../../../../helpers/to-number'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; +import toNumber from '../../../../../../helpers/to-number'; export default { name: "greater-than-or-equal", diff --git a/src/parse/condition/operators/comparison/operators/greater-than.ts b/src/parse/condition/operators/comparison/operators/greater-than/index.ts similarity index 80% rename from src/parse/condition/operators/comparison/operators/greater-than.ts rename to src/parse/condition/operators/comparison/operators/greater-than/index.ts index d93a886..72d047d 100644 --- a/src/parse/condition/operators/comparison/operators/greater-than.ts +++ b/src/parse/condition/operators/comparison/operators/greater-than/index.ts @@ -1,6 +1,6 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; -import toNumber from '../../../../../helpers/to-number'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; +import toNumber from '../../../../../../helpers/to-number'; export default { name: "greater-than", diff --git a/src/parse/condition/operators/comparison/operators/is-bool.ts b/src/parse/condition/operators/comparison/operators/is-bool/index.ts similarity index 92% rename from src/parse/condition/operators/comparison/operators/is-bool.ts rename to src/parse/condition/operators/comparison/operators/is-bool/index.ts index 6c8458f..8d13d1d 100644 --- a/src/parse/condition/operators/comparison/operators/is-bool.ts +++ b/src/parse/condition/operators/comparison/operators/is-bool/index.ts @@ -1,5 +1,5 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; const toBool = (subject: unknown) => { if (subject === true || subject === false) { diff --git a/src/parse/condition/operators/comparison/operators/is-null.ts b/src/parse/condition/operators/comparison/operators/is-null/index.ts similarity index 83% rename from src/parse/condition/operators/comparison/operators/is-null.ts rename to src/parse/condition/operators/comparison/operators/is-null/index.ts index f1abf60..8fb9aa1 100644 --- a/src/parse/condition/operators/comparison/operators/is-null.ts +++ b/src/parse/condition/operators/comparison/operators/is-null/index.ts @@ -1,5 +1,5 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; export default { name: 'isnull', diff --git a/src/parse/condition/operators/comparison/operators/less-than-equal.ts b/src/parse/condition/operators/comparison/operators/less-than-or-equal/index.ts similarity index 80% rename from src/parse/condition/operators/comparison/operators/less-than-equal.ts rename to src/parse/condition/operators/comparison/operators/less-than-or-equal/index.ts index 88b856a..c4348a1 100644 --- a/src/parse/condition/operators/comparison/operators/less-than-equal.ts +++ b/src/parse/condition/operators/comparison/operators/less-than-or-equal/index.ts @@ -1,6 +1,6 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; -import toNumber from '../../../../../helpers/to-number'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; +import toNumber from '../../../../../../helpers/to-number'; export default { name: 'less-than-or-equal', diff --git a/src/parse/condition/operators/comparison/operators/less-than.ts b/src/parse/condition/operators/comparison/operators/less-than/index.ts similarity index 80% rename from src/parse/condition/operators/comparison/operators/less-than.ts rename to src/parse/condition/operators/comparison/operators/less-than/index.ts index e4aa926..8c741f4 100644 --- a/src/parse/condition/operators/comparison/operators/less-than.ts +++ b/src/parse/condition/operators/comparison/operators/less-than/index.ts @@ -1,6 +1,6 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; -import toNumber from '../../../../../helpers/to-number'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; +import toNumber from '../../../../../../helpers/to-number'; export default { name: 'less-than', diff --git a/src/parse/condition/operators/comparison/operators/numerical.ts b/src/parse/condition/operators/comparison/operators/numerical/index.ts similarity index 89% rename from src/parse/condition/operators/comparison/operators/numerical.ts rename to src/parse/condition/operators/comparison/operators/numerical/index.ts index 2ef2ef1..8d157ee 100644 --- a/src/parse/condition/operators/comparison/operators/numerical.ts +++ b/src/parse/condition/operators/comparison/operators/numerical/index.ts @@ -1,6 +1,6 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; -import toNumber from '../../../../../helpers/to-number'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; +import toNumber from '../../../../../../helpers/to-number'; const isRange = /^((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))-((?:[+-]?\d+(?:\.\d+)?)|(?:[+-]?\.\d+))$/; diff --git a/src/parse/condition/operators/comparison/operators/regex.ts b/src/parse/condition/operators/comparison/operators/regex/index.ts similarity index 87% rename from src/parse/condition/operators/comparison/operators/regex.ts rename to src/parse/condition/operators/comparison/operators/regex/index.ts index 3871b65..27a8914 100644 --- a/src/parse/condition/operators/comparison/operators/regex.ts +++ b/src/parse/condition/operators/comparison/operators/regex/index.ts @@ -1,6 +1,6 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; -import toText from '../../../../../helpers/to-text'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; +import toText from '../../../../../../helpers/to-text'; export default { name: 'regex', diff --git a/src/parse/condition/operators/comparison/operators/wildcard.ts b/src/parse/condition/operators/comparison/operators/wildcard/index.ts similarity index 93% rename from src/parse/condition/operators/comparison/operators/wildcard.ts rename to src/parse/condition/operators/comparison/operators/wildcard/index.ts index 9a8a233..4973cfa 100644 --- a/src/parse/condition/operators/comparison/operators/wildcard.ts +++ b/src/parse/condition/operators/comparison/operators/wildcard/index.ts @@ -1,7 +1,7 @@ -import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../token'; -import { type default as IParseOptions } from '../../../../../types/options'; -import split from '../../../../../helpers/unicode-safe-split'; -import toText from '../../../../../helpers/to-text'; +import { type IOperator, type IHandleState, ArgumentsQuantifier } from '../../../token'; +import { type default as IParseOptions } from '../../../../../../types/options'; +import split from '../../../../../../helpers/unicode-safe-split'; +import toText from '../../../../../../helpers/to-text'; const toRegExp = (subject: string, caseSensitive: boolean) : RegExp => { const wc = split(subject); diff --git a/src/parse/condition/operators/token/index.ts b/src/parse/condition/operators/token/index.ts index 099ddab..1c6e8a5 100644 --- a/src/parse/condition/operators/token/index.ts +++ b/src/parse/condition/operators/token/index.ts @@ -30,13 +30,13 @@ export interface IOperator { name: string; description: string; quantifier: ArgumentsQuantifier; - defer: boolean; + defer?: boolean; cased?: boolean, alias: string[]; inverse?: { description: string; alias: string[]; - handle: IHandleFn; + handle?: IHandleFn; }; handle: IHandleFn; } From 3e3a956f8b3708a2eacf8d9c10ef05c4f83c0633 Mon Sep 17 00:00:00 2001 From: SReject Date: Sun, 25 Sep 2022 08:10:50 -0400 Subject: [PATCH 89/92] chore(tests): increase test coverage --- jest/helpers.ts | 63 +++++++++++++ .../operators/contains/index.spec.ts | 7 ++ .../operators/equal-loose/index.spec.ts | 7 ++ .../operators/equal-strict/index.spec.ts | 26 ++++++ .../comparison/operators/exists/index.spec.ts | 7 ++ .../greater-than-or-equal/index.spec.ts | 7 ++ .../operators/greater-than/index.spec.ts | 7 ++ .../operators/is-bool/index.spec.ts | 7 ++ .../operators/is-null/index.spec.ts | 7 ++ .../less-than-or-equal/index.spec.ts | 7 ++ .../operators/less-than/index.spec.ts | 7 ++ .../operators/numerical/index.spec.ts | 7 ++ .../comparison/operators/regex/index.spec.ts | 7 ++ .../operators/wildcard/index.spec.ts | 7 ++ .../condition/operators/logical/index.spec.ts | 22 +++++ .../operators/logical/operators/and.spec.ts | 92 +++++++++++++++++++ .../operators/logical/operators/not.spec.ts | 46 ++++++++++ .../operators/logical/operators/or.spec.ts | 92 +++++++++++++++++++ 18 files changed, 425 insertions(+) create mode 100644 src/parse/condition/operators/comparison/operators/contains/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/equal-loose/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/equal-strict/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/exists/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/greater-than-or-equal/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/greater-than/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/is-bool/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/is-null/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/less-than-or-equal/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/less-than/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/numerical/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/regex/index.spec.ts create mode 100644 src/parse/condition/operators/comparison/operators/wildcard/index.spec.ts create mode 100644 src/parse/condition/operators/logical/index.spec.ts create mode 100644 src/parse/condition/operators/logical/operators/and.spec.ts create mode 100644 src/parse/condition/operators/logical/operators/not.spec.ts create mode 100644 src/parse/condition/operators/logical/operators/or.spec.ts diff --git a/jest/helpers.ts b/jest/helpers.ts index f6b6db4..eea99f3 100644 --- a/jest/helpers.ts +++ b/jest/helpers.ts @@ -2,6 +2,7 @@ export {} interface CustomMatchers { + toBeAnOperator(): R; toHaveOwnProperty(key: string, value?: unknown): R; toAsyncThrow(): R; } @@ -23,6 +24,68 @@ const hasOwnProperty = Object.prototype.hasOwnProperty expect.extend({ + //eslint-disable-next-line @typescript-eslint/no-explicit-any + toBeAnOperator: (subject: any) => { + if (subject == null) { + return { pass: false, message: () => `expected subject to be an operator definition` }; + } + if (typeof subject.name !== 'string') { + return { pass: false, message: () => `name must be a string` } + } + if (typeof subject.description !== 'string') { + return { pass: false, message: () => `description must be a string` } + } + if (!Number.isFinite(subject.quantifier)) { + return { pass: false, message: () => `arguments quantifier must be a numeric value` } + } + if (subject.defer != null && typeof subject.defer !== 'boolean') { + return { pass: false, message: () => `defer must be a boolean value` } + } + if (subject.cased != null && typeof subject.cased !== 'boolean') { + return { pass: false, message: () => `cased must be a boolean value` } + } + if ( + !Array.isArray(subject.alias) || + subject.alias.some((value: unknown) => typeof value !== 'string') + ) { + return { pass: false, message: () => `alias must be a array of strings` } + } + if (subject.inverse != null) { + if (typeof subject.inverse !== 'object') { + return { pass: false, message: () => `inverse must be an object` } + } + + const inverse : Record = subject.inverse; + if (typeof inverse.description !== 'string') { + return { pass: false, message: () => `inverse description must be a string` } + } + if ( + !Array.isArray(inverse.alias) || + inverse.alias.some((value: unknown) => typeof value !== 'string') + ) { + return { pass: false, message: () => `inverse alias must be an array of strings` } + } + if ( + inverse.handle != null && + ( + typeof inverse.handle !== 'function' || + !(inverse.handle instanceof Function) + ) + ) { + return { pass: false, message: () => `inverse handle must be a function`} + } + } + if ( + typeof subject.handle !== 'function' || + !(subject.handle instanceof Function) + ) { + return { pass: false, message: () => `handle must be a function`} + } + + + return { pass: true, message: () => `expected subject not to be an operator definition` } + }, + //eslint-disable-next-line @typescript-eslint/no-explicit-any toHaveOwnProperty: (...args: any[]) : IResult => { diff --git a/src/parse/condition/operators/comparison/operators/contains/index.spec.ts b/src/parse/condition/operators/comparison/operators/contains/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/contains/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/equal-loose/index.spec.ts b/src/parse/condition/operators/comparison/operators/equal-loose/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/equal-loose/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/equal-strict/index.spec.ts b/src/parse/condition/operators/comparison/operators/equal-strict/index.spec.ts new file mode 100644 index 0000000..fbb207e --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/equal-strict/index.spec.ts @@ -0,0 +1,26 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); + +test('Returns true for strictly equal values', async () => { + expect(await operator.handle({}, {}, { caseSensitive: false, left: false, right: false })).toBe(true); + expect(await operator.handle({}, {}, { caseSensitive: false, left: true, right: true })).toBe(true); + expect(await operator.handle({}, {}, { caseSensitive: false, left: 1, right: 1 })).toBe(true); + expect(await operator.handle({}, {}, { caseSensitive: false, left: null, right: undefined })).toBe(true); + expect(await operator.handle({}, {}, { caseSensitive: false, left: NaN, right: NaN })).toBe(true); + + const value = {}; + expect(await operator.handle({}, {}, { caseSensitive: false, left: value, right: value})).toBe(true); +}); + +test('Returns false when in-equal', async () => { + expect(await operator.handle({}, {}, { caseSensitive: false, left: true, right: false })).toBe(false); + expect(await operator.handle({}, {}, { caseSensitive: false, left: null, right: 'null' })).toBe(false); + expect(await operator.handle({}, {}, { caseSensitive: false, left: null, right: '' })).toBe(false); + expect(await operator.handle({}, {}, { caseSensitive: false, left: 1, right: '1' })).toBe(false); + expect(await operator.handle({}, {}, { caseSensitive: false, left: {}, right: {} })).toBe(false); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/exists/index.spec.ts b/src/parse/condition/operators/comparison/operators/exists/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/exists/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/greater-than-or-equal/index.spec.ts b/src/parse/condition/operators/comparison/operators/greater-than-or-equal/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/greater-than-or-equal/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/greater-than/index.spec.ts b/src/parse/condition/operators/comparison/operators/greater-than/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/greater-than/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/is-bool/index.spec.ts b/src/parse/condition/operators/comparison/operators/is-bool/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/is-bool/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/is-null/index.spec.ts b/src/parse/condition/operators/comparison/operators/is-null/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/is-null/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/less-than-or-equal/index.spec.ts b/src/parse/condition/operators/comparison/operators/less-than-or-equal/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/less-than-or-equal/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/less-than/index.spec.ts b/src/parse/condition/operators/comparison/operators/less-than/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/less-than/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/numerical/index.spec.ts b/src/parse/condition/operators/comparison/operators/numerical/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/numerical/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/regex/index.spec.ts b/src/parse/condition/operators/comparison/operators/regex/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/regex/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/wildcard/index.spec.ts b/src/parse/condition/operators/comparison/operators/wildcard/index.spec.ts new file mode 100644 index 0000000..67e3ebb --- /dev/null +++ b/src/parse/condition/operators/comparison/operators/wildcard/index.spec.ts @@ -0,0 +1,7 @@ +import '../../../../../../../jest/helpers'; + +import operator from './index'; + +test('Exports an operator definition', () => { + expect(operator).toBeAnOperator(); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/logical/index.spec.ts b/src/parse/condition/operators/logical/index.spec.ts new file mode 100644 index 0000000..648f60c --- /dev/null +++ b/src/parse/condition/operators/logical/index.spec.ts @@ -0,0 +1,22 @@ +import notOperatorDef from './operators/not'; +import andOperatorDef from './operators/and'; +import orOperatorDef from './operators/or'; +import { notOperator, default as operatorMap } from './index'; + +test('Exports default as map instance', () => { + expect(operatorMap).toBeInstanceOf(Map); +}); + +test('Exports not operator', () => { + expect(notOperator === notOperatorDef).toBe(true); +}); + +test('Addresses \'and\' operator as &&', () => { + expect(operatorMap.has('&&')).toBe(true); + expect(operatorMap.get('&&') === andOperatorDef).toBe(true); +}); + +test('Addresses \'or\' operator as ||', () => { + expect(operatorMap.has('||')).toBe(true); + expect(operatorMap.get('||') === orOperatorDef).toBe(true); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/logical/operators/and.spec.ts b/src/parse/condition/operators/logical/operators/and.spec.ts new file mode 100644 index 0000000..1d3dddf --- /dev/null +++ b/src/parse/condition/operators/logical/operators/and.spec.ts @@ -0,0 +1,92 @@ +import '../../../../../../jest/helpers'; +import Token from '../../../../token'; + +import andOperator from './and'; + +test('Exports an operator definition', () => { + expect(andOperator).toBeAnOperator(); +}); + +test('Returns false when first arg is false', async () => { + + const left = new Token(); + let leftCalled = 0; + left.evaluate = async () => { + leftCalled += 1; + return false; + }; + + const right = new Token(); + let rightCalled = 0; + right.evaluate = async () => { + rightCalled += 1; + return false; + }; + + expect.assertions(3); + + const result = await andOperator.handle({}, {}, { + caseSensitive: false, + arguments: [left, right] + }); + + expect(result).toBe(false); + expect(leftCalled).toBe(1); + expect(rightCalled).toBe(0); +}); + +test('Returns false when second arg is false', async () => { + + const left = new Token(); + let leftCalled = 0; + left.evaluate = async () => { + leftCalled += 1; + return true; + }; + + const right = new Token(); + let rightCalled = 0; + right.evaluate = async () => { + rightCalled += 1; + return false; + }; + + expect.assertions(3); + + const result = await andOperator.handle({}, {}, { + caseSensitive: false, + arguments: [left, right] + }); + + expect(result).toBe(false); + expect(leftCalled).toBe(1); + expect(rightCalled).toBe(1); +}); + +test('Returns true when both args are true', async () => { + + const left = new Token(); + let leftCalled = 0; + left.evaluate = async () => { + leftCalled += 1; + return true; + }; + + const right = new Token(); + let rightCalled = 0; + right.evaluate = async () => { + rightCalled += 1; + return true; + }; + + expect.assertions(3); + + const result = await andOperator.handle({}, {}, { + caseSensitive: false, + arguments: [left, right] + }); + + expect(result).toBe(true); + expect(leftCalled).toBe(1); + expect(rightCalled).toBe(1); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/logical/operators/not.spec.ts b/src/parse/condition/operators/logical/operators/not.spec.ts new file mode 100644 index 0000000..ba241d8 --- /dev/null +++ b/src/parse/condition/operators/logical/operators/not.spec.ts @@ -0,0 +1,46 @@ +import '../../../../../../jest/helpers'; +import Token from '../../../../token'; + +import notOperator from './not'; + +test('Exports an operator definition', () => { + expect(notOperator).toBeAnOperator(); +}); + +test('Returns false when arg is truthy', async () => { + let argCalls = 0; + const arg = new Token(); + arg.evaluate = async () => { + argCalls += 1; + return true; + }; + + expect.assertions(2); + + const result = await notOperator.handle({}, {}, { + caseSensitive: false, + arguments: [arg] + }); + + expect(result).toBe(false); + expect(argCalls).toBe(1); +}); + +test('Returns true when arg is falsey', async () => { + let argCalls = 0; + const arg = new Token(); + arg.evaluate = async () => { + argCalls += 1; + return false; + }; + + expect.assertions(2); + + const result = await notOperator.handle({}, {}, { + caseSensitive: false, + arguments: [arg] + }); + + expect(result).toBe(true); + expect(argCalls).toBe(1); +}); \ No newline at end of file diff --git a/src/parse/condition/operators/logical/operators/or.spec.ts b/src/parse/condition/operators/logical/operators/or.spec.ts new file mode 100644 index 0000000..f914af4 --- /dev/null +++ b/src/parse/condition/operators/logical/operators/or.spec.ts @@ -0,0 +1,92 @@ +import '../../../../../../jest/helpers'; +import Token from '../../../../token'; + +import orOperator from './or'; + +test('Exports an operator definition', () => { + expect(orOperator).toBeAnOperator(); +}); + +test('Returns true when first arg is true', async () => { + + const left = new Token(); + let leftCalled = 0; + left.evaluate = async () => { + leftCalled += 1; + return true; + }; + + const right = new Token(); + let rightCalled = 0; + right.evaluate = async () => { + rightCalled += 1; + return true; + }; + + expect.assertions(3); + + const result = await orOperator.handle({}, {}, { + caseSensitive: false, + arguments: [left, right] + }); + + expect(result).toBe(true); + expect(leftCalled).toBe(1); + expect(rightCalled).toBe(0); +}); + +test('Returns true when second arg is true', async () => { + + const left = new Token(); + let leftCalled = 0; + left.evaluate = async () => { + leftCalled += 1; + return false; + }; + + const right = new Token(); + let rightCalled = 0; + right.evaluate = async () => { + rightCalled += 1; + return true; + }; + + expect.assertions(3); + + const result = await orOperator.handle({}, {}, { + caseSensitive: false, + arguments: [left, right] + }); + + expect(result).toBe(true); + expect(leftCalled).toBe(1); + expect(rightCalled).toBe(1); +}); + +test('Returns false when both args are false', async () => { + + const left = new Token(); + let leftCalled = 0; + left.evaluate = async () => { + leftCalled += 1; + return false; + }; + + const right = new Token(); + let rightCalled = 0; + right.evaluate = async () => { + rightCalled += 1; + return false; + }; + + expect.assertions(3); + + const result = await orOperator.handle({}, {}, { + caseSensitive: false, + arguments: [left, right] + }); + + expect(result).toBe(false); + expect(leftCalled).toBe(1); + expect(rightCalled).toBe(1); +}); \ No newline at end of file From 2224188a25f9783945d6d7905eab573f03d19e90 Mon Sep 17 00:00:00 2001 From: SReject Date: Sun, 25 Sep 2022 19:58:29 -0400 Subject: [PATCH 90/92] rebase: rename TokenList to ListToken --- src/parse/argument/tokenize/index.ts | 4 +- src/parse/condition/tokenize/operand.ts | 4 +- .../index.spec.ts} | 66 +++++++++---------- src/parse/{token-list.ts => list/index.ts} | 16 ++--- src/parse/text/tokenize/escape-block.ts | 4 +- src/parse/text/tokenize/quoted.ts | 4 +- src/types/token-types.ts | 2 +- 7 files changed, 50 insertions(+), 50 deletions(-) rename src/parse/{token-list.spec.ts => list/index.spec.ts} (74%) rename src/parse/{token-list.ts => list/index.ts} (85%) diff --git a/src/parse/argument/tokenize/index.ts b/src/parse/argument/tokenize/index.ts index cfd494a..14fbbec 100644 --- a/src/parse/argument/tokenize/index.ts +++ b/src/parse/argument/tokenize/index.ts @@ -2,7 +2,7 @@ import TokenType from '../../../types/token-types'; import type ITokenizeState from '../../../types/tokenize-state'; import type Token from '../../token'; -import TokenList from '../../token-list'; +import ListToken from '../../list'; import { TextToken, tokenizeEscape, @@ -137,7 +137,7 @@ export default async (state: ITokenizeState) : Promise => { state.tokens = tokens; state.cursor = cursor; - state.output = new TokenList({ + state.output = new ListToken({ position, value: result }) diff --git a/src/parse/condition/tokenize/operand.ts b/src/parse/condition/tokenize/operand.ts index 86baee3..8265f6e 100644 --- a/src/parse/condition/tokenize/operand.ts +++ b/src/parse/condition/tokenize/operand.ts @@ -15,7 +15,7 @@ import { tokenizeIf } from '../../if'; import { tokenizeFunction } from '../../function'; import type Token from '../../token'; -import TokenList from '../../token-list'; +import ListToken from '../../list'; export interface IOperandState extends ITokenizeState { endOfOperand?: boolean; @@ -157,7 +157,7 @@ export default async ( state.tokens = tokens; state.cursor = cursor; - state.output = new TokenList({ + state.output = new ListToken({ position: startPosition, value: operand }); diff --git a/src/parse/token-list.spec.ts b/src/parse/list/index.spec.ts similarity index 74% rename from src/parse/token-list.spec.ts rename to src/parse/list/index.spec.ts index b371b1a..5b283e3 100644 --- a/src/parse/token-list.spec.ts +++ b/src/parse/list/index.spec.ts @@ -1,85 +1,85 @@ -import '../../jest/helpers'; +import '../../../jest/helpers'; -import TokenType from '../types/token-types'; -import IParseOptions from '../types/options'; +import TokenType from '../../types/token-types'; +import IParseOptions from '../../types/options'; -import Token from './token'; -import TokenList from './token-list'; +import Token from '../token'; +import ListToken from './index'; test('Exports a constructor', () => { - expect(typeof TokenList).toBe('function'); - expect(TokenList.prototype).toBeDefined(); + expect(typeof ListToken).toBe('function'); + expect(ListToken.prototype).toBeDefined(); }); test('Has .toJSON() function', () => { - expect(TokenList.prototype).toHaveOwnProperty('toJSON'); - expect(typeof TokenList.prototype.toJSON).toBe('function'); + expect(ListToken.prototype).toHaveOwnProperty('toJSON'); + expect(typeof ListToken.prototype.toJSON).toBe('function'); }); test('Has .evaluate() function', () => { - expect(TokenList.prototype).toHaveOwnProperty('evaluate'); - expect(typeof TokenList.prototype.evaluate).toBe('function'); + expect(ListToken.prototype).toHaveOwnProperty('evaluate'); + expect(typeof ListToken.prototype.evaluate).toBe('function'); }); test('Constructor errors when token is nullish', () => { expect(() => { // @ts-expect-error: Testing nullish token - new TokenList(); + new ListToken(); }).toThrow(); }); test('Constructor errors when token is not an object', () => { expect(() => { // @ts-expect-error: Testing invalid token - new TokenList(true); + new ListToken(true); }).toThrow(); }); test('Constructor errors when token.value is nullish', () => { expect(() => { // @ts-expect-error: Testing nullish token.value - new TokenList({}); + new ListToken({}); }).toThrow(); }); test('Constructor errors when token.value is not an array', () => { expect(() => { // @ts-expect-error: Testing invalid token.value - new TokenList({value: ''}); + new ListToken({value: ''}); }).toThrow(); }); test('Constructor errors when token.value contains a non-token instance', () => { expect(() => { // @ts-expect-error: Testing invalid token.value - new TokenList({value: ['']}); + new ListToken({value: ['']}); }).toThrow(); }); test('Constructs without error', () => { - expect(() => new TokenList({value: []})).not.toThrow(); + expect(() => new ListToken({value: []})).not.toThrow(); }); -test('Instance to be instance of TokenList and Token', () => { - const token = new TokenList({value: []}); - expect(token).toBeInstanceOf(TokenList); +test('Instance to be instance of ListToken and Token', () => { + const token = new ListToken({value: []}); + expect(token).toBeInstanceOf(ListToken); expect(token).toBeInstanceOf(Token); }); -test('.type is set to TOKENLIST', () => { - const token = new TokenList({value: []}); - expect(token).toHaveOwnProperty('type', TokenType.TOKENLIST); +test('.type is set to TokenType.LIST', () => { + const token = new ListToken({value: []}); + expect(token).toHaveOwnProperty('type', TokenType.LIST); }); test('.value is set to input', () => { const value : Token[] = []; - const token = new TokenList({ value }); + const token = new ListToken({ value }); expect(token).toHaveOwnProperty('value', value); }); test('.toJSON() calls super.toJSON()', () => { const spy = jest.spyOn(Token.prototype, 'toJSON'); - const token = new TokenList({ value: [] }); + const token = new ListToken({ value: [] }); expect(() => token.toJSON()).not.toThrow(); expect(spy).toHaveBeenCalledTimes(1); jest.clearAllMocks(); @@ -93,7 +93,7 @@ test('.toJSON() returns valid representation', () => { return Token.prototype.toJSON.call(this); }; - const token = new TokenList({value: [value]}); + const token = new ListToken({value: [value]}); const json = token.toJSON(); expect(calls).toBe(1); @@ -112,7 +112,7 @@ test('.evaluate() calls evaluate for values/falls back to defaults for inputs', receivedMeta = meta; return; }; - const token = new TokenList({value: [value]}); + const token = new ListToken({value: [value]}); // @ts-expect-error : Testing nulled inputs await token.evaluate(); @@ -125,7 +125,7 @@ test('.evaluate() returns undefined when options.verifyOnly is specified', async value.evaluate = async function() : Promise { return 'text'; } - const token = new TokenList({value: [value]}); + const token = new ListToken({value: [value]}); const result = await token.evaluate({verifyOnly: true}, {}); expect(result).toBeUndefined(); @@ -136,7 +136,7 @@ test('.evaluate() sets result to value when result is not set', async () => { value.evaluate = async function() : Promise { return 'test'; } - const token = new TokenList({value: [value]}); + const token = new ListToken({value: [value]}); const result = await token.evaluate({}, {}); expect(result).toBe('test'); @@ -147,7 +147,7 @@ test('.evaluate() returns null if only a single value is given and its null', as value.evaluate = async function() : Promise { return null; } - const token = new TokenList({value: [value]}); + const token = new ListToken({value: [value]}); const result = await token.evaluate({}, {}); expect(result).toBeNull(); @@ -166,7 +166,7 @@ test('.evaluate() does not append non-textable values', async () => { const value4 = new Token(); value4.evaluate = async function() : Promise { return 'b'; } - const token = new TokenList({value: [value1, value2, value3, value4]}); + const token = new ListToken({value: [value1, value2, value3, value4]}); const result = await token.evaluate({}, {}); expect(result).toBe('ab'); @@ -179,7 +179,7 @@ test('.evaluate() converts to text when appending', async () => { const value2 = new Token(); value2.evaluate = async function() : Promise { return 'a'; } - const token = new TokenList({value: [value1, value2]}); + const token = new ListToken({value: [value1, value2]}); const result = await token.evaluate({}, {}); expect(result).toBe('{}a'); @@ -192,7 +192,7 @@ test('.evaluate() ignores functions', async () => { const value2 = new Token(); value2.evaluate = async function() : Promise { return 'a'; } - const token = new TokenList({value: [value1, value2]}); + const token = new ListToken({value: [value1, value2]}); const result = await token.evaluate({}, {}); expect(result).toBe('a'); diff --git a/src/parse/token-list.ts b/src/parse/list/index.ts similarity index 85% rename from src/parse/token-list.ts rename to src/parse/list/index.ts index 9bf29a4..6005e88 100644 --- a/src/parse/token-list.ts +++ b/src/parse/list/index.ts @@ -1,18 +1,18 @@ -import TokenType from '../types/token-types'; -import type IParserOptions from '../types/options'; +import TokenType from '../../types/token-types'; +import type IParserOptions from '../../types/options'; -import toText from '../helpers/to-text'; +import toText from '../../helpers/to-text'; -import Token, { type IToken } from './token'; +import Token, { type IToken } from '../token'; -export interface ITokenList extends IToken { +export interface IListToken extends IToken { value: Token[]; } -export default class TokenList extends Token { +export default class ListToken extends Token { public value : Token[]; - constructor(token: ITokenList) { + constructor(token: IListToken) { if (token == null) { throw new Error('TODO - ExpressionError: token not specified'); } @@ -34,7 +34,7 @@ export default class TokenList extends Token { super({ ...token, - type: TokenType.TOKENLIST + type: TokenType.LIST }); } diff --git a/src/parse/text/tokenize/escape-block.ts b/src/parse/text/tokenize/escape-block.ts index 1dd047d..e8d43c4 100644 --- a/src/parse/text/tokenize/escape-block.ts +++ b/src/parse/text/tokenize/escape-block.ts @@ -2,7 +2,7 @@ import TokenType from '../../../types/token-types'; import type ITokenizeState from '../../../types/tokenize-state'; import type Token from '../../token'; -import TokenList from '../../token-list'; +import ListToken from '../../list'; import TextToken from '../token'; @@ -69,7 +69,7 @@ export default async (state: ITokenizeState) : Promise => { state.tokens = tokens; state.cursor = cursor + 1; - state.output = new TokenList({ + state.output = new ListToken({ position, value: escTokens }); diff --git a/src/parse/text/tokenize/quoted.ts b/src/parse/text/tokenize/quoted.ts index c49a464..e501200 100644 --- a/src/parse/text/tokenize/quoted.ts +++ b/src/parse/text/tokenize/quoted.ts @@ -4,7 +4,7 @@ import type ITokenizeState from '../../../types/tokenize-state'; import { ExpressionSyntaxError } from '../../../errors'; import type Token from '../../token'; -import TokenList from '../../token-list'; +import ListToken from '../../list'; import TextToken from '../token'; @@ -88,7 +88,7 @@ export default async (state: ITokenizeState) : Promise => { state.tokens = tokens; state.cursor = cursor + 2; - state.output = new TokenList({ + state.output = new ListToken({ position, value: quoteTokens }); diff --git a/src/types/token-types.ts b/src/types/token-types.ts index 4ad7d1d..dc0bfd3 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -1,7 +1,7 @@ const enum TokenType { UNKNOWN, EXPRESSION, - TOKENLIST, + LIST, IFSTATEMENT, FUNCTIONAL, TEXT, From 531ef84d5db0f3fb227967b4b85f115e19b92100 Mon Sep 17 00:00:00 2001 From: SReject Date: Sun, 25 Sep 2022 19:58:59 -0400 Subject: [PATCH 91/92] chore(tests): increase coverage --- .../comparison/operators/exists/index.spec.ts | 8 ++++ .../comparison/operators/exists/index.ts | 2 +- src/parse/text/index.spec.ts | 37 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/parse/text/index.spec.ts diff --git a/src/parse/condition/operators/comparison/operators/exists/index.spec.ts b/src/parse/condition/operators/comparison/operators/exists/index.spec.ts index 67e3ebb..914de72 100644 --- a/src/parse/condition/operators/comparison/operators/exists/index.spec.ts +++ b/src/parse/condition/operators/comparison/operators/exists/index.spec.ts @@ -4,4 +4,12 @@ import operator from './index'; test('Exports an operator definition', () => { expect(operator).toBeAnOperator(); +}); + +test('Returns truthy when value is not falsey', async () => { + expect(await operator.handle({}, {}, { caseSensitive: false, left: undefined })).toBe(false); + expect(await operator.handle({}, {}, { caseSensitive: false, left: null })).toBe(false); + expect(await operator.handle({}, {}, { caseSensitive: false, left: false })).toBe(false); + expect(await operator.handle({}, {}, { caseSensitive: false, left: '' })).toBe(false); + expect(await operator.handle({}, {}, { caseSensitive: false, left: 'test' })).toBe(true); }); \ No newline at end of file diff --git a/src/parse/condition/operators/comparison/operators/exists/index.ts b/src/parse/condition/operators/comparison/operators/exists/index.ts index e2ea2be..0a8896d 100644 --- a/src/parse/condition/operators/comparison/operators/exists/index.ts +++ b/src/parse/condition/operators/comparison/operators/exists/index.ts @@ -11,6 +11,6 @@ export default { alias: ['!exists'] }, handle: async function (options: IParseOptions, meta: unknown, state: IHandleState) : Promise { - return state.left != null && state.left !== '' && state.left !== false; + return state.left != null && state.left !== false && state.left !== ''; } } \ No newline at end of file diff --git a/src/parse/text/index.spec.ts b/src/parse/text/index.spec.ts new file mode 100644 index 0000000..19c4bdb --- /dev/null +++ b/src/parse/text/index.spec.ts @@ -0,0 +1,37 @@ +import { + TextToken, + tokenizeEscape, + tokenizeEscapeBlock, + tokenizeQuoted, + tokenizeSpecial +} from './index'; + +test('Exports TextToken constructor', () => { + expect(typeof TextToken).toBe('function'); + expect(TextToken).toBeInstanceOf(Function); + expect(TextToken.prototype).toBeDefined(); +}); + +test('Exports tokenizeEscape function', () => { + expect(typeof tokenizeEscape).toBe('function'); + expect(tokenizeEscape).toBeInstanceOf(Function); + expect(tokenizeEscape.prototype).toBeUndefined(); +}); + +test('Exports tokenizeEscapeBlock function', () => { + expect(typeof tokenizeEscapeBlock).toBe('function'); + expect(tokenizeEscapeBlock).toBeInstanceOf(Function); + expect(tokenizeEscapeBlock.prototype).toBeUndefined(); +}); + +test('Exports tokenizeQuoted function', () => { + expect(typeof tokenizeQuoted).toBe('function'); + expect(tokenizeQuoted).toBeInstanceOf(Function); + expect(tokenizeQuoted.prototype).toBeUndefined(); +}); + +test('Exports tokenizeSpecial function', () => { + expect(typeof tokenizeSpecial).toBe('function'); + expect(tokenizeSpecial).toBeInstanceOf(Function); + expect(tokenizeSpecial.prototype).toBeUndefined(); +}); \ No newline at end of file From 76884ccd5d09d659d44ac1e554c114988cd2b405 Mon Sep 17 00:00:00 2001 From: SReject Date: Wed, 25 Jan 2023 20:08:38 -0500 Subject: [PATCH 92/92] . --- src/expressionish/default/block-operators.ts | 3 + .../default/comparison-operators.ts | 3 + .../default/function-handlers.ts | 3 + .../default/special-sequences.ts | 5 + src/expressionish/index.ts | 422 ++++++++++++++++++ src/expressionish/options.ts | 103 +++++ src/expressionish/options/_support.ts | 25 ++ src/expressionish/options/end-of-line.ts | 30 ++ .../options/function-signifier.ts | 18 + src/expressionish/options/group-signifier.ts | 57 +++ src/expressionish/options/operators.ts | 208 +++++++++ .../options/quotes-signifiers.ts | 72 +++ .../options/special-sequences.ts | 67 +++ .../options/special-signifier.ts | 18 + src/helpers/deep-freeze.ts | 17 + src/helpers/get-potential-tokens.ts | 13 +- src/helpers/is-object.ts | 9 + src/index.ts | 395 +++++++++++++--- src/parse/function/token/index.spec.ts | 220 +++++++++ src/parse/function/token/index.ts | 67 ++- src/types/options.ts | 62 ++- src/types/token-types.ts | 2 +- src2/expressionish/index.ts | 28 ++ src2/index.ts | 12 + src2/types/global.d.ts | 20 + tsconfig.json | 3 +- 26 files changed, 1783 insertions(+), 99 deletions(-) create mode 100644 src/expressionish/default/block-operators.ts create mode 100644 src/expressionish/default/comparison-operators.ts create mode 100644 src/expressionish/default/function-handlers.ts create mode 100644 src/expressionish/default/special-sequences.ts create mode 100644 src/expressionish/index.ts create mode 100644 src/expressionish/options.ts create mode 100644 src/expressionish/options/_support.ts create mode 100644 src/expressionish/options/end-of-line.ts create mode 100644 src/expressionish/options/function-signifier.ts create mode 100644 src/expressionish/options/group-signifier.ts create mode 100644 src/expressionish/options/operators.ts create mode 100644 src/expressionish/options/quotes-signifiers.ts create mode 100644 src/expressionish/options/special-sequences.ts create mode 100644 src/expressionish/options/special-signifier.ts create mode 100644 src/helpers/deep-freeze.ts create mode 100644 src/helpers/is-object.ts create mode 100644 src/parse/function/token/index.spec.ts create mode 100644 src2/expressionish/index.ts create mode 100644 src2/index.ts create mode 100644 src2/types/global.d.ts diff --git a/src/expressionish/default/block-operators.ts b/src/expressionish/default/block-operators.ts new file mode 100644 index 0000000..e41f54f --- /dev/null +++ b/src/expressionish/default/block-operators.ts @@ -0,0 +1,3 @@ +import { type IOperatorOptionsDefinition } from '../options/operators'; + +export default []; \ No newline at end of file diff --git a/src/expressionish/default/comparison-operators.ts b/src/expressionish/default/comparison-operators.ts new file mode 100644 index 0000000..e41f54f --- /dev/null +++ b/src/expressionish/default/comparison-operators.ts @@ -0,0 +1,3 @@ +import { type IOperatorOptionsDefinition } from '../options/operators'; + +export default []; \ No newline at end of file diff --git a/src/expressionish/default/function-handlers.ts b/src/expressionish/default/function-handlers.ts new file mode 100644 index 0000000..7499008 --- /dev/null +++ b/src/expressionish/default/function-handlers.ts @@ -0,0 +1,3 @@ +import { type IOperatorDefinition } from '../options'; + +export default >{}; \ No newline at end of file diff --git a/src/expressionish/default/special-sequences.ts b/src/expressionish/default/special-sequences.ts new file mode 100644 index 0000000..022389c --- /dev/null +++ b/src/expressionish/default/special-sequences.ts @@ -0,0 +1,5 @@ +export default >{ + 't': '\t', + 'n': '\n', + 'r': '\r' +}; \ No newline at end of file diff --git a/src/expressionish/index.ts b/src/expressionish/index.ts new file mode 100644 index 0000000..90f9cf1 --- /dev/null +++ b/src/expressionish/index.ts @@ -0,0 +1,422 @@ +import has from '../helpers/has'; +import freeze from '../helpers/deep-freeze'; + +import { IQuantifiedList, verifyQuantifiedList } from './options/_support'; + +import quantifyEndOfLine, { + type IEndOfLineOptions, + type IEndOfLineQuantified +} from './options/end-of-line'; + +import quantifySpecialSignifier from './options/special-signifier'; + +import quantifyFunctionSignifier from './options/function-signifier'; + +import quantifyGroupSignifier, { + type IGroupSignifierOptions, + type IGroupSignifierQuantified +} from './options/group-signifier'; + +import quantifyQuotesSignifiers, { + type IQuotesOptions, + type IQuotesQuantified +} from './options/quotes-signifiers' + +import quantifySpecialSequences, { + type ISpecialSequencesOptions, + type ISpecialSequencesQuantified +} from './options/special-sequences'; + +import expandOperator, { + OperatorQuantifier, + type IOperatorOptionsDefinition, + type IOperatorOptions, + type IOperatorQuantified +} from './options/operators'; + +import quantifyFunctionHandlers, { + type IFunctionHandlersOptions, + type IFunctionHandlersQuantified +} from './options/function-handlers'; + + + + +import { + type IHandleFnc, + // type IOperatorDefinition, + type ILookupHandler, +} from './options'; +import defaultComparisonOperators from './default/comparison-operators'; +import defaultBlockOperators from './default/block-operators'; +import defaultFunctionHandlers from './default/function-handlers'; +import { ExpressionError, ExpressionArgumentsError, ExpressionSyntaxError, ExpressionVariableError } from '../errors'; + + + + +interface IExpressionOptions { + eol?: IEndOfLineOptions; + specialSignifier?: string; + functionSignifier?: string; + groupSignifier?: IGroupSignifierOptions; + quotes?: IQuotesOptions; + specialSequences?: ISpecialSequencesOptions; + comparisonOperators?: IOperatorOptions; + blockOperators?: IOperatorOptions; + + functionHandlers?: IFunctionHandlersOptions; + +} +class Expressionish { + + private eol : IEndOfLineQuantified; + private specialSignifier: string; + private functionSignifier : string; + private groupSignifier: IGroupSignifierQuantified; + private quotes : IQuotesQuantified[]; + private specialSequences : ISpecialSequencesQuantified; + private comparisonOperators: Record; + private blockOperators: Record; + private functionHandlers : Record; + + private lookupHandlers : Record; + + constructor(options: IExpressionOptions = {}) { + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const define = (key: string, value: unknown, deepFreeze = false) => { + return Object.defineProperty(this, key, { + configurable: false, + enumerable: false, + writable: false, + value: freeze(value, deepFreeze) + }); + } + + + define( + 'eol', + quantifyEndOfLine(options.eol) + ); + + + define( + 'specialSignifier', + quantifySpecialSignifier(options.specialSignifier) + ); + const signifiers : string[] = [this.specialSignifier]; + + + define( + 'functionSignifier', + quantifyFunctionSignifier( + options.functionSignifier, + signifiers + ) + ); + signifiers.push(this.functionSignifier); + + + define('groupSignifier', + quantifyGroupSignifier( + options.groupSignifier, + signifiers + ), + true + ); + signifiers.push(this.groupSignifier.open, this.groupSignifier.delimiter, this.groupSignifier.close); + + + define('quotes', + quantifyQuotesSignifiers( + options.quotes, + [ + this.specialSignifier, + this.functionSignifier, + this.groupSignifier.open, + this.groupSignifier.delimiter, + this.groupSignifier.close + ] + ), + true + ); + this.quotes.forEach(quoteDef => { + if (!signifiers.includes(quoteDef.open)) + signifiers.push(quoteDef.open); + if (!signifiers.includes(quoteDef.close)) + signifiers.push(quoteDef.close); + }); + + + + define( + 'specialSequences', + quantifySpecialSequences(options.specialSequences), + true + ); + + + /* #region Comparison Operators */ + define('comparisonOperators', {}); + /* + define( + 'comparisonOperators', + quantifyOperators( + options.comparisonOperators, + signifiers, + defaultComparisonOperators, + {} + ) + ); + define( + 'blockOperators', + quantifyOperators( + options.blockOperators, + signifiers, + defaultBlockOperators, + this.comparisonOperators + ) + ) + */ + const comparisonOperators = options.comparisonOperators; + if (comparisonOperators == null || comparisonOperators === true) { + this.registerComparisonOperators(defaultComparisonOperators); + + } else if (typeof comparisonOperators === 'object') { + verifyQuantifiedList(comparisonOperators); + if (comparisonOperators.disposition === 'append') { + this.registerComparisonOperators(defaultComparisonOperators); + } + this.registerComparisonOperators(comparisonOperators.items); + } else if (comparisonOperators !== false) { + throw new Error('TODO'); + } + /* #endregion */ + + + /* #region Block Operators */ + define('blockOperators', {}); + const blockOperators = options.blockOperators; + if (blockOperators == null || blockOperators === true) { + this.registerBlockOperators(defaultBlockOperators); + } else if (typeof blockOperators === 'object') { + verifyQuantifiedList(blockOperators); + if (blockOperators.disposition === 'append') { + this.registerBlockOperators(defaultBlockOperators); + } + this.registerBlockOperators(blockOperators.items); + } else if (blockOperators !== false) { + throw new Error('TODO'); + } + /* #endregion */ + + + /* #region Function Handlers */ + define('functionHandlers', {}); + const functionHandlers = options.functionHandlers; + if (functionHandlers == null || functionHandlers === true) { + this.registerFunctionHandlers(defaultFunctionHandlers); + } else if (typeof functionHandlers === 'object') { + verifyQuantifiedList(functionHandlers); + if (functionHandlers.disposition === 'append') { + this.registerFunctionHandlers(defaultFunctionHandlers); + } + this.registerFunctionHandlers(functionHandlers.items); + } else if (functionHandlers !== false) { + throw new Error('TODO'); + } + + + /* #region lookup handlers */ + define('lookupHandlers', {}); + if (typeof options.lookupHandlers !== 'object') { + throw new Error('TODO'); + } else if (options.lookupHandlers != null) { + this.registerLookupHandlers(options.lookupHandlers); + } + /* #endregion */ + + + Object.freeze(this); + } + + + + registerComparisonOperator(operator: IOperatorOptionsDefinition) { + const signifiers : string[] = [ + this.specialSignifier, + this.functionSignifier, + this.groupSignifier.open, + this.groupSignifier.delimiter, + this.groupSignifier.close + ]; + this.quotes.forEach(quotedef => { + if (!signifiers.includes(quotedef.open)) { + signifiers.push(quotedef.open); + } + if (!signifiers.includes(quotedef.close)) { + signifiers.push(quotedef.close); + } + }); + + expandOperator(signifiers, this.comparisonOperators, operator); + } + registerComparisonOperators(operators: IOperatorOptionsDefinition[]) { + if (!Array.isArray(operators)) { + throw new Error('TODO'); + } + operators.forEach(operator => this.registerComparisonOperator(operator)); + } + + + + registerBlockOperator(operator: IOperatorOptionsDefinition) { + const signifiers : string[] = [ + this.specialSignifier, + this.functionSignifier, + this.groupSignifier.open, + this.groupSignifier.delimiter, + this.groupSignifier.close + ]; + this.quotes.forEach(quotedef => { + if (!signifiers.includes(quotedef.open)) { + signifiers.push(quotedef.open); + } + if (!signifiers.includes(quotedef.close)) { + signifiers.push(quotedef.close); + } + }); + expandOperator(signifiers, this.blockOperators, operator) + } + registerBlockOperators(operators: IOperatorOptionsDefinition[]) { + if (!Array.isArray(operators)) { + throw new Error('TODO'); + } + operators.forEach(operator => this.registerBlockOperator(operator)); + } + + + + registerFunctionHandler(name: string, handler: IFunctionHandler | IHandleFnc) { + if (typeof name !== 'string') { + throw new Error('TODO'); + } else if (!/^[a-z][a-z\d]{2,}$/i.test(name)) { + throw new Error('TODO'); + } else if (has(this.functionHandlers, name.toLowerCase())) { + throw new Error('TODO'); + } else if (typeof handler === 'function') { + this.functionHandlers[name.toLowerCase()] = { defer: false, evaluate: handler }; + } else if (handler == null || typeof handler !== 'object') { + throw new Error('TODO'); + } else if (handler.defer != null && typeof handler.defer !== 'boolean') { + throw new Error('TODO'); + } else if (handler.stackCheck != null && typeof handler.stackCheck !== 'function') { + throw new Error('TODO'); + } else if (handler.argsCheck != null && typeof handler.argsCheck !== 'function') { + throw new Error('TODO'); + } else if (handler.evaluate == null || typeof handler.evaluate !== 'function') { + throw new Error('TODO'); + } else { + const { defer, stackCheck, argsCheck, evaluate } = handler; + this.functionHandlers[name.toLowerCase()] = Object.freeze({ + defer: !!defer, + stackCheck, + argsCheck, + evaluate + }); + } + } + registerFunctionHandlers(list: Record>) { + if (list == null) { + throw new Error('TODO'); + } else if (typeof list !== 'object') { + throw new Error('TODO'); + } else { + Object.keys(list).forEach(prefix => { + this.registerFunctionHandler(prefix, list[prefix]); + }); + } + } + registerLookupHandler(prefix: string, handler: ILookupHandler) { + if (typeof prefix !== 'string') { + throw new Error('TODO'); + } else if ( + prefix === '' || + /[!\s\\[,\]"`]/.test(prefix) || + prefix.includes(this.functionSignifier) + ) { + throw new Error('TODO'); + } else if (has(this.lookupHandlers, prefix.toLowerCase())) { + throw new Error('TODO'); + } else if (typeof handler !== 'function') { + throw new Error('TODO'); + } else { + this.lookupHandlers[prefix.toLowerCase()] = handler; + } + } + registerLookupHandlers(list: Record) { + if (list == null) { + throw new Error('TODO'); + } else if (typeof list !== 'object') { + throw new Error('TODO'); + } else { + Object.keys(list).forEach(prefix => { + this.registerLookupHandler(prefix, list[prefix]); + }); + } + } + + tokenize(subject: string) { + // todo + } + + toJSON() : Record { + const comparisonOperators : Record = {}; + Object.keys(this.comparisonOperators).forEach(operatorName => { + const { signifier, quantifier, cased, defer } = this.comparisonOperators[operatorName]; + comparisonOperators[operatorName] = { signifier, quantifier, cased, defer }; + }); + + const blockOperators : Record = {}; + Object.keys(this.blockOperators).forEach(operatorName => { + const { signifier, quantifier, cased, defer } = this.blockOperators[operatorName]; + blockOperators[operatorName] = { signifier, quantifier, cased, defer }; + }); + + const functionHandlers : Record = {}; + Object.keys(this.functionHandlers).forEach(funcName => { + const { defer } = this.functionHandlers[funcName]; + functionHandlers[funcName] = { defer }; + }); + + return { + eol: this.eol.quantifier, + escapeSignifier: this.escapeSignifier, + functionSignifier: this.functionSignifier, + groupSignifier: this.groupSignifier, + quotes: this.quotes, + specialSequences: this.specialSequences, + comparisonOperators, + blockOperators, + functionHandlers, + lookupHandlers: Object.keys(this.lookupHandlers) + }; + } +} + +export { + ExpressionError, + ExpressionSyntaxError, + ExpressionVariableError, + ExpressionArgumentsError, + + OperatorQuantifier, + type IOperatorDefinition, + type IFunctionHandler, + type ILookupHandler, + type IExpressionOptions, + + Expressionish, + Expressionish as default, +}; \ No newline at end of file diff --git a/src/expressionish/options.ts b/src/expressionish/options.ts new file mode 100644 index 0000000..2bddc1b --- /dev/null +++ b/src/expressionish/options.ts @@ -0,0 +1,103 @@ +export const enum OperatorQuantifier { + LEFTONLY, + RIGHTOPTIONAL, + RIGHTREQUIRED, + PREBLOCK, + BLOCK +} + +export type IMeta = Record; + +export type IHandleFnc = (options: IEvaluateOptions, meta: IMeta, stack: string[], ...args: unknown[]) => Promise; + +export type IEOLQuantifier = 'keep' | 'remove' | 'space' | 'error'; + +export type IEndOfLine = IEOLQuantifier | ((eol: string) => string); + +export interface IEOLDefinition { + quantifier: IEOLQuantifier | 'transform'; + transform: (char: string) => string; +} + +export interface IRealizedOperator { + signifier: string; + quantifier: OperatorQuantifier + cased: boolean; + defer: boolean; + evaluate: IHandleFnc +} + +export interface IOperatorDefinition { + signifier: string | string[]; + quantifier: OperatorQuantifier; + cased?: boolean; + defer?: boolean; + inverse?: { + signifier?: string | string[]; + evaluate?: IHandleFnc; + }; + evaluate: IHandleFnc; +} + +export interface IOptionsList { + disposition?: 'append' | 'replace'; + items: T; +} + +export interface IFunctionHandler { + defer?: boolean; + stackCheck?: (options: IEvaluateOptions, meta: IMeta, stack: string[]) => Promise; + argsCheck?: IHandleFnc; + evaluate: IHandleFnc; +} + +export type ILookupHandler = (options: IExpressionOptions, meta: IMeta, stack: string[], name: string) => Promise; + +export interface IGroupSignifier { + open: string, + delimiter?: string, + close: string +} + +export interface IQuoteQuantifier { + open: string; + close: string; +} + +export interface IExpressionOptions { + eol?: IEndOfLine; + escapeSignifier?: string; + functionSignifier?: string; + groupSignifier?: 'parens' | 'brackets' | 'curly' | IGroupSignifier; + quotes?: false | 'single' | 'double' | 'both' | IQuoteQuantifier | IQuoteQuantifier[]; + specialSequences?: boolean | IOptionsList>; + comparisonOperators?: boolean | IOptionsList; + blockOperators?: boolean | IOptionsList; + functionHandlers?: boolean | IOptionsList>; + lookupHandlers?: Record; +} + +export interface IEvaluateOptions { + verifyOnly?: boolean; + skipStackChecks?: boolean; + skipArgumentsChecks?: boolean; +} + +export interface IRealizedGroupSignifier { + open: string, + delimiter: string, + close: string +} + +export interface IRealizedOptions { + eol: IEOLDefinition; + escapeSignifier: string; + functionSignifier: string; + groupSignifier: IRealizedGroupSignifier; + quotes: IQuoteQuantifier[]; + specialSequence: Record; + comparisonOperators: Record; + blockOperators: Record; + functionHandlers: Record; + lookupHandler: Record; +} \ No newline at end of file diff --git a/src/expressionish/options/_support.ts b/src/expressionish/options/_support.ts new file mode 100644 index 0000000..b7d5b6a --- /dev/null +++ b/src/expressionish/options/_support.ts @@ -0,0 +1,25 @@ +import isObject from 'src/helpers/is-object'; + +export type RequireAtLeastOne = { [K in keyof T]-?: Required> & Partial>>; }[keyof T] + +export type dispsosition = 'append' | 'override'; + +export interface IQuantifiedList { + disposition?: dispsosition; + items: T; +} + +export const verifyQuantifiedList = (subject: unknown) => { + if (!isObject(subject)) { + throw new Error('TODO'); + } + + const { disposition, items } = >subject; + if (disposition == null || (disposition !== 'append' && disposition !== 'override')) { + throw new Error('TODO'); + } + + if (items == null || (!isObject(items) && !Array.isArray(items))) { + throw new Error('TODO'); + } +} \ No newline at end of file diff --git a/src/expressionish/options/end-of-line.ts b/src/expressionish/options/end-of-line.ts new file mode 100644 index 0000000..9378914 --- /dev/null +++ b/src/expressionish/options/end-of-line.ts @@ -0,0 +1,30 @@ +type IEOLQuantifier = 'keep' | 'remove' | 'space' | 'error'; + +export type IEndOfLineOptions = IEOLQuantifier | ((eol: string) => string); + +export interface IEndOfLineQuantified { + quantifier: IEOLQuantifier | 'transform'; + transform: (char: string) => string; +} + +export default (value: unknown) : IEndOfLineQuantified => { + + if (value === 'error') + return { quantifier: 'error', transform: () => { throw new Error('TODO') } }; + + if (value == null || value === 'keep') + return { quantifier: 'keep', transform: (char) => char }; + + if (value === 'remove') + return { quantifier: 'remove', transform: () => '' }; + + if (value === 'space') + return { quantifier: 'space', transform: () => ' '}; + + if (typeof value === 'function') + return { quantifier: 'transform', transform: value }; + + throw new Error('TODO'); + +}; + diff --git a/src/expressionish/options/function-signifier.ts b/src/expressionish/options/function-signifier.ts new file mode 100644 index 0000000..99369ae --- /dev/null +++ b/src/expressionish/options/function-signifier.ts @@ -0,0 +1,18 @@ +export default (value: unknown, significant: string[]) : string => { + if (value == null) + return '$'; + + if (typeof value !== 'string') + throw new Error('TODO'); + + if (value.length !== 1) + throw new Error('TODO'); + + if (/[!\s'"]/.test(value)) + throw new Error('TODO'); + + if (significant !== null && significant.some(significant => value.includes(significant))) + throw new Error('TODO'); + + return value; +} \ No newline at end of file diff --git a/src/expressionish/options/group-signifier.ts b/src/expressionish/options/group-signifier.ts new file mode 100644 index 0000000..3905655 --- /dev/null +++ b/src/expressionish/options/group-signifier.ts @@ -0,0 +1,57 @@ +interface IGroupSignifier { + open: string, + delimiter?: string, + close: string +} + +export type IGroupSignifierOptions = 'parens' | 'brackets' | 'curly' | IGroupSignifier; + +export interface IGroupSignifierQuantified { + open: string, + delimiter: string, + close: string +} + +export default (value: unknown, significant: string[]) : IGroupSignifierQuantified => { + + if (value == null || value === 'brackets') + return { open: '[', delimiter: ',', close: ']' }; + + if (value === 'parens') + return { open: '(', delimiter: ',', close: ')' }; + + if (value === 'curly') + return { open: '{', delimiter: ',', close: '}' }; + + if (typeof value !== 'object') + throw new Error('TODO'); + + const invalidChars = new RegExp(`[!\\s'"${ + significant + .filter(value => value.length === 1) + .map(value => `\\${value}`) + .join('') + }]`); + + const isValid = (value: unknown) => ( + typeof value === 'string' && + value.length === 1 && + !invalidChars.test(value) + ); + + const { open, delimiter = ',', close } = value; + + if (!isValid(open)) + throw new Error('TODO'); + + if (!isValid(delimiter)) + throw new Error('TODO'); + + if (!isValid(close)) + throw new Error('TODO'); + + if (open === delimiter || delimiter === close || open === close) + throw new Error('TODO'); + + return { open, delimiter, close }; +} \ No newline at end of file diff --git a/src/expressionish/options/operators.ts b/src/expressionish/options/operators.ts new file mode 100644 index 0000000..bfdeeac --- /dev/null +++ b/src/expressionish/options/operators.ts @@ -0,0 +1,208 @@ +import has from '../../helpers/has'; +import isObject from '../../helpers/is-object'; + +import { + type RequireAtLeastOne, + type IQuantifiedList +} from './_support'; + +type IHandleFnc = (...args: unknown[]) => Promise; + +interface IInverseQuantified { + signifier: string | string[]; + evaluate: IHandleFnc; +} +type IInverseOptions = RequireAtLeastOne + +export enum OperatorQuantifier { + LEFTONLY, + RIGHTOPTIONAL, + RIGHTREQUIRED, + PREBLOCK, + BLOCK +} + +export interface IOperatorOptionsDefinition { + signifier: string | string[]; + quantifier: OperatorQuantifier; + cased?: boolean; + defer?: boolean; + inverse?: boolean | IInverseOptions; + evaluate: IHandleFnc +} + +export type IOperatorOptions = boolean | IQuantifiedList; + +export interface IOperatorQuantified { + signifier: string; + quantifier: OperatorQuantifier; + cased: boolean; + defer: boolean; + evaluate: IHandleFnc +} + +export default ( + signifiers: string[], + registry: Record, + operator: IOperatorOptionsDefinition +) => { + + // helpers + const PROPBASE = { writable: false, enumerable: true, configurable: false }; + const invalidMultis : string[] = []; + const invalidSingles = new RegExp(`[!\\s${ + signifiers.map(value => { + if (value.length === 1) return '\\value'; + if (value.length > 1) invalidMultis.push(value); + }).filter(value => value != null).join('') + }]`); + + + // operator + if (operator == null || !isObject(operator)) { + throw new Error('TODO'); + } + + const { quantifier } = operator + let { signifier, cased, defer, inverse, evaluate } = operator; + + + // operator.signifier + if (signifier == null || signifier === '') { + throw new Error('TODO'); + } else if (typeof signifier === 'string') { + signifier = [signifier]; + } else if (!Array.isArray(signifier)) { + throw new Error('TODO'); + } else if (!signifier.length) { + throw new Error('TODO'); + } + + + // operator.cased + if (typeof cased == null) { + cased = false; + } else if (typeof cased !== 'boolean') { + throw new Error('TODO'); + } + + + // operator.defer + if (typeof defer == null) { + defer = false; + } else if (typeof defer !== 'boolean') { + throw new Error('TODO'); + } + + + // operator.quantifier + if (quantifier == null || !Number.isInteger(quantifier) || !(quantifier in OperatorQuantifier)) { + throw new Error('TODO'); + } + + + // operator.evaluate + if (typeof evaluate !== 'function') { + throw new Error('TODO'); + } + + + // operator.inverse + if (inverse == null) { + inverse = false; + } else if (inverse === true) { + inverse = { + signifier: signifier.map(value => `!${value}`), + evaluate: async (...args: unknown[]) => !(await evaluate(...args)) + }; + } else if (inverse !== false) { + if (!isObject(inverse)) { + throw new Error('TODO'); + } + if (inverse.signifier == null && inverse.evaluate == null) { + throw new Error('TODO'); + } + if (inverse.signifier == null) { + inverse.signifier = signifier.map(value => `!${value}`); + } else if (inverse.signifier === '') { + throw new Error('TODO'); + } else if (typeof inverse.signifier === 'string') { + inverse.signifier = [inverse.signifier]; + } else if (!Array.isArray(inverse.signifier)) { + throw new Error('TODO'); + } else if (!inverse.signifier.length) { + throw new Error('TODO'); + } + + if (inverse.evaluate == null) { + inverse.evaluate = async (...args: unknown[]) => !(await evaluate(...args)) + } else if (typeof inverse.evaluate !== 'function') { + throw new Error('TODO'); + } + } + + + const operators : Record = {}; + + // Realize operators + signifier.forEach(key => { + if (typeof key !== 'string') { + throw new Error('todo'); + } + if ( + key === '' || + invalidSingles.test(key) || + invalidMultis.some(multi => key.includes(multi)) + ) { + throw new Error('TODO'); + } + key = key.toLowerCase(); + if (has(registry, key) || has(operators, key)) { + throw new Error('TODO'); + } + operators[key] = Object.freeze(Object.create(null, { + 'signifier': { ...PROPBASE, value: key }, + 'cased': { ...PROPBASE, value: cased}, + 'defer': { ...PROPBASE, value: defer}, + 'quantifier': { ...PROPBASE, value: quantifier }, + 'evaluate': { ...PROPBASE, value: evaluate } + })); + }); + + if (inverse == null || inverse === false) { + Object.assign(registry, operators); + } + + signifier = (inverse).signifier; + evaluate = (inverse).evaluate; + + (signifier).forEach(key => { + if (typeof key !== 'string') { + throw new Error('todo'); + } + let value = key; + if (key[0] === '!') { + value = key.slice(1); + } + if ( + value === '' || + invalidSingles.test(value) || + invalidMultis.some(multi => value.includes(multi)) + ) { + throw new Error('TODO'); + } + key = key.toLowerCase(); + if (has(registry, key) || has(operators, key)) { + throw new Error('TODO'); + } + operators[key] = Object.freeze(Object.create(null, { + 'signifier': { ...PROPBASE, value: key }, + 'cased': { ...PROPBASE, value: cased}, + 'defer': { ...PROPBASE, value: defer}, + 'quantifier': { ...PROPBASE, value: quantifier }, + 'evaluate': { ...PROPBASE, value: evaluate } + })); + }); + + return Object.assign(registry, operators); +}; \ No newline at end of file diff --git a/src/expressionish/options/quotes-signifiers.ts b/src/expressionish/options/quotes-signifiers.ts new file mode 100644 index 0000000..be6f283 --- /dev/null +++ b/src/expressionish/options/quotes-signifiers.ts @@ -0,0 +1,72 @@ +export interface IQuotesQuantified { + open: string; + close: string; +} + +export type IQuotesOptions = false | 'single' | 'double' | 'both' | IQuotesQuantified | IQuotesQuantified[]; + +export default (value: unknown, significant: string[]) : IQuotesQuantified[] => { + if (value === false) + return []; + + if (value == null || value === 'double') + return [ Object.freeze({open: '"', close: '"'}) ]; + + if (value === 'single') + return [ Object.freeze({ open: "'", close: "'"}) ]; + + if (value === 'both') + return [ + Object.freeze({ open: '"', close: '"'}), + Object.freeze({ open: "'", close: "'"}) + ]; + + if (typeof value !== 'object') + throw new Error('TODO'); + + if (!Array.isArray(value)) + value = [value]; + + const invalidMulti : string[] = []; + const invalidChars = new RegExp(`[!\\s${ + significant + .filter(value => { + if (value.length > 1) { + invalidMulti.push(value); + return false; + } + return value.length === 1; + }) + .map(value => `\\${value}`) + .join('') + }]`); + const inuse : string[] = []; + const isValid = (value: unknown) => ( + typeof value === 'string' && + value.length === 1 && + !invalidChars.test(value) && + !invalidMulti.some(multi => value.includes(multi)) && + !inuse.some(used => used === value) + ); + + return (value) + .filter(value => value != null) + .map(value => { + if (typeof value !== 'object') + throw new Error('TODO'); + + const { open, close } = value; + + if (!isValid(open)) + throw new Error('TODO'); + + if (!isValid(close)) + throw new Error('TODO'); + + inuse.push(open); + if (open !== close) + inuse.push(close); + + return { open, close }; + }); +} \ No newline at end of file diff --git a/src/expressionish/options/special-sequences.ts b/src/expressionish/options/special-sequences.ts new file mode 100644 index 0000000..5487e37 --- /dev/null +++ b/src/expressionish/options/special-sequences.ts @@ -0,0 +1,67 @@ +import has from 'src/helpers/has'; +import isObject from '../../helpers/is-object'; + +import { type IQuantifiedList } from './_support'; + +type ISpecialSequences = IQuantifiedList>; + +export type ISpecialSequencesOptions = boolean | ISpecialSequences; + +export type ISpecialSequencesQuantified = Record; + +const defaultSequences : ISpecialSequencesQuantified = { + 't': '\\t', + 'n': '\\n', + 'r': '\\r' +}; + +export default (value: ISpecialSequencesOptions) : Record => { + if (value === false) + return Object.freeze({}); + + if (value == null || value === true) + return Object.freeze( { ...defaultSequences }); + + if (!isObject(value)) { + throw new Error('TODO'); + } + + const { disposition = 'append', items} = value; + + if (disposition !== 'append' && disposition !== 'override') { + throw new Error('TODO'); + } + if (!isObject(items)) { + throw new Error('TODO'); + } + + const result : ISpecialSequencesQuantified = {}; + + if (disposition === 'append') { + Object + .keys(defaultSequences) + .forEach(key => { + result[key] = defaultSequences[key]; + }); + } + + Object + .keys(items) + .forEach(key => { + if (typeof key !== 'string') + throw new Error('TODO'); + + if (items[key] == null) + throw new Error('TODO'); + + if (typeof items[key] !== 'string') + throw new Error('TODO'); + + if (has(result, key)) + throw new Error('TODO'); + + result[key] = items[key]; + }); + + return Object.freeze(result); +} \ No newline at end of file diff --git a/src/expressionish/options/special-signifier.ts b/src/expressionish/options/special-signifier.ts new file mode 100644 index 0000000..a687c1d --- /dev/null +++ b/src/expressionish/options/special-signifier.ts @@ -0,0 +1,18 @@ +export default (value: unknown) : string => { + if (value == null) + return '\\'; + + if (typeof value !== 'string') + throw new Error('TODO'); + + if (value == '') + throw new Error('TODO'); + + if (value.length !== 1) + throw new Error('TODO'); + + if (/[!\s'"]/.test(value)) + throw new Error('TODO'); + + return value; +} \ No newline at end of file diff --git a/src/helpers/deep-freeze.ts b/src/helpers/deep-freeze.ts new file mode 100644 index 0000000..57e4adf --- /dev/null +++ b/src/helpers/deep-freeze.ts @@ -0,0 +1,17 @@ +import has from './has'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const freeze = (subject: T, deepFreeze = false) : T => { + if (deepFreeze === true && subject != null && typeof subject === 'object') { + for (const key in subject) { + if (!has(subject, key) || subject[key] == null || typeof subject[key] !== 'object') { + continue; + } + freeze(subject[key], true); + } + freeze(subject, true); + } + return subject; +} + +export default freeze; \ No newline at end of file diff --git a/src/helpers/get-potential-tokens.ts b/src/helpers/get-potential-tokens.ts index ccefcb6..7e71765 100644 --- a/src/helpers/get-potential-tokens.ts +++ b/src/helpers/get-potential-tokens.ts @@ -63,13 +63,16 @@ export default (options: ParserOptions, subject: string) : IPreToken[] => { return inc; } - const nextChar = subject[position + 1]; - // \\, $ + const nextChar = subject[position + 1]; if ( nextChar != null && - (char === '\\' && nextChar !== '') || - (char === '$' && nextChar !== '\\') + nextChar !== '' && + /^\s$/.test(nextChar) && + ( + char === '\\' || + (char === '$' && /^[^a-z\d\\]$/i.test(nextChar)) + ) ) { if (textToken !== null) { result.push(textToken); @@ -85,7 +88,7 @@ export default (options: ParserOptions, subject: string) : IPreToken[] => { return 1; } - // Block Escape, Single Escape, &&, || + // Block Escape, &&, || const seq = subject.slice(position, position + 2); if ( seq === '``' || diff --git a/src/helpers/is-object.ts b/src/helpers/is-object.ts new file mode 100644 index 0000000..646c1f6 --- /dev/null +++ b/src/helpers/is-object.ts @@ -0,0 +1,9 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default (subject: any) => ( + subject != null && + typeof subject === 'object' && + ( + subject.prototype == null || + subject.prototype === Object.prototype + ) +); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 712d201..f21c1c5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,99 +1,348 @@ import has from './helpers/has'; -import IParseOptions from './types/options'; +import tokenize from './parse'; +import Expression from './parse/expression'; +import { type default as Token } from './parse/token'; -import { - type default as ParserOptions, - type IFunctionHandler, - type IFunctionLookup -} from './types/options'; +const enum ArgumentType { + VALUE, + CONDITION +} -import Expression from './parse/expression'; -import tokenize from './parse'; +const enum ComparisonQuantifier { + LEFTONLY, + RIGHTOPTIONAL, + RIGHTREQUIRED +} -export { - ExpressionError, - ExpressionArgumentsError, - ExpressionSyntaxError, - ExpressionVariableError -} from './errors'; +const enum ComparisonBlockQuantifier { + PRE, + POST +} -export class Expressionish { - private functionHandlers : Record; - private functionLookups : Record; +type IEOLTransformer = (char: string) => Promise; +type IEndOfLine = 'error' | 'remove' | 'space' | 'keep' | IEOLTransformer; + +type disposition = 'append' | 'replace'; + +interface ISpecialSequences { + disposition?: disposition; + sequences: Record; +} + +type IMeta = Record; + +type IEvaluate = (options: IEvaluateOptions, meta: IMeta, ...args: Token[]) => Promise; + +interface IComparisonInverseOperator { + signifier?: string | string[]; + evaluate?: IEvaluate; +} + +interface IComparisonOperator { + signifier: string | string[]; + cased?: boolean; + quantifier: ComparisonQuantifier; + inverse?: boolean | IComparisonInverseOperator; + evaluate: IEvaluate; +} + +interface IBlockOperator extends Omit { + quantifier: ComparisonBlockQuantifier; + defer?: boolean +} + +interface IOperatorOptions { + disposition?: disposition; + operators?: Array; +} + +interface ArgumentsDescriptor { + type: ArgumentType; + multi: boolean; + optional?: boolean; +} + +interface IFunctionHandler { + defer?: boolean; + arguments?: ArgumentsDescriptor[]; + stackCheck?: (options: IEvaluateOptions, meta: IMeta, stack: string[]) => Promise; + argsCheck?: (options: IEvaluateOptions, meta: IMeta, ...args: Token[]) => Promise; + evaluate: (options: IEvaluateOptions, meta: IMeta, ...args: Token[]) => Promise; +} + +type IFunctionHandlers = Record; +type IFunctionLookupHandler = (options: IEvaluateOptions, meta: IMeta, name: string) => Promise; +type IFunctionLookupHandlers = Record; - private config: ParserOptions; +interface IExpressionishOptions { + eol?: IEndOfLine; + specialSequences?: boolean | ISpecialSequences; - constructor(options: ParserOptions = {}) { - this.functionHandlers = options.functionHandlers || {}; - this.functionLookups = options.functionLookups || {}; + comparisonOperators?: boolean | IOperatorOptions; + blockOperators?: boolean | IOperatorOptions; - this.config = { - "if": options.if || true, - eol: options.eol || 'keep', - specialSequences: options.specialSequences || true - }; + functionHandlers?: boolean | IFunctionHandlers; + functionLookupHandlers?: IFunctionLookupHandlers; +} + +interface IEvaluateOptions { + verifyOnly?: boolean; + skipStackChecks?: boolean; + skipArgumentsChecks?: boolean; +} + +const defaultSpecialSequences : Record = { + '\\t': '\t', + '\\n': '\n', + '\\r': '\r' +}; + +interface IExpandedOperator { + signifier: string[]; + cased: boolean; + defer: boolean; + quantifier: T; + inverse: false | { + signifier: string[]; + evaluate: IEvaluate; + }; + evaluate: IEvaluate; +} + +const isArrayOfString = (subject: unknown[]) : boolean => { + if (!Array.isArray(subject)) { + return false; } + return (>subject).every((value: unknown) => { + return ( + typeof value === 'string' && + value !== '' && + value !== ' ' && + value !== '\t' && + value !== '\n' && + value !== '\r' + ); + }); +}; + +export class Expressionish { - registerFunction(name: string, handler: IFunctionHandler) : void { - name = name.toLowerCase(); - if (has(this.functionHandlers, name)) { - throw new Error(`'$${name}' already registered`); + private eol : IEndOfLine; + private specialSequences : Record; + + private comparisonOperators: Record; + private blockOperators: Record; + + private functionHandlers : IFunctionHandlers; + private functionLookupHandlers : IFunctionLookupHandlers; + + constructor(options: IExpressionishOptions = {}) { + + if (options.eol == null) { + this.eol === 'keep'; + } else { + this.eol = options.eol; } - this.functionHandlers[name] = handler; - } - unregisterFunction(name: string, handler: IFunctionHandler) : void { - name = name.toLowerCase(); - if (has(this.functionHandlers, name) && this.functionHandlers[name] === handler) { - delete this.functionHandlers[name]; + + // special sequences + if (options.specialSequences === false) { + this.specialSequences = {}; + + } else if (options.specialSequences == null || options.specialSequences === true) { + this.specialSequences = { ...defaultSpecialSequences }; + + } else if (options.specialSequences.disposition === 'replace') { + this.specialSequences = { ...(options.specialSequences.sequences)}; + + } else { + this.specialSequences = { ...defaultSpecialSequences, ...(options.specialSequences.sequences) }; } - } - registerLookup(prefix: string, handler: IFunctionLookup) : void { - prefix = prefix.toLowerCase(); - if (has(this.functionLookups, prefix)) { - throw new Error(`prefix '$${prefix}' already registered`); + + // comparison operators + this.comparisonOperators = {}; + if ( + options.comparisonOperators == null || + options.comparisonOperators === true || + (>options.comparisonOperators)?.disposition === 'append' + ) { + defaultComparisonOperators.forEach((operator: IComparisonOperator) => { + this.registerComparisonOperator(operator); + }); + } + if ((>options.comparisonOperators)?.operators != null) { + (>options.comparisonOperators)?.operators.forEach((operator: IComparisonOperator) => { + this.registerComparisonOperator(operator); + }); } - this.functionLookups[prefix] = handler; - } - unregisterLookup(prefix: string, handler: IFunctionLookup) : void { - prefix = prefix.toLowerCase(); - if (has(this.functionLookups, prefix) && this.functionLookups[prefix] === handler) { - delete this.functionLookups[prefix]; + + // block operators + this.blockOperators = {}; + if ( + options.blockOperators == null || + options.blockOperators === true || + (>options.blockOperators)?.disposition === 'append' + ) { + defaultBlockOperators.forEach((operator: IBlockOperator) => { + this.registerComparisonOperator(operator); + }); + } + if ((>options.blockOperators)?.operators != null) { + (>options.blockOperators)?.operators.forEach((operator: IBlockOperator) => { + this.registerBlockOperator(operator); + }); + } + + + // function handlers + if ( + options.functionHandlers == null || + options.functionHandlers === false + ) { + this.functionHandlers = {}; + + } else if (options.functionHandlers === true) { + // TODO: register $if[] + + } else { + this.functionHandlers = options.functionHandlers; + } + + + // function lookups + if (options.functionLookupHandlers == null) { + this.functionLookupHandlers = {}; + + } else { + this.functionLookupHandlers = options.functionLookupHandlers; } } - async tokenize(subject: string) : Promise { - const config : IParseOptions = { - functionHandlers: { ...(this.functionHandlers) }, - functionLookups: { ...(this.functionLookups) }, - ...(this.config) + + registerSpecialSequence(key: string, value: string) { + if (typeof key !== 'string' || key.length !== 1) { + // error + } else if (key === ' ') { + // error + } else if (typeof value !== 'string' || key.length < 1) { + // error + } else if (has(this.specialSequences, key)) { + // error + } else { + this.specialSequences[key] = value; } - return tokenize(config, subject); } - static async tokenize(subject: string, options?: ParserOptions) : Promise { - return tokenize({ - functionHandlers: {}, - functionLookups: {}, - "if": true, - eol: 'keep', - specialSequences: true, - ...options - }, subject); + + registerComparisonOperator(operator: IComparisonOperator) { + + const expandedOperator : Partial> = {}; + + if (typeof operator.signifier === 'string') { + expandedOperator.signifier = [operator.signifier]; + } else if (isArrayOfString(operator.signifier)) { + expandedOperator.signifier = operator.signifier; + } else { + throw new Error('TODO'); + } + + + if (typeof operator.cased == null) { + expandedOperator.cased = false; + } else if (typeof operator.cased !== 'boolean') { + throw new Error('TODO'); + } else { + expandedOperator.cased = operator.cased; + } + + + expandedOperator.defer = false; + + + if ( + operator.quantifier != null && + operator.quantifier !== ComparisonQuantifier.LEFTONLY && + operator.quantifier !== ComparisonQuantifier.RIGHTOPTIONAL && + operator.quantifier !== ComparisonQuantifier.RIGHTREQUIRED + ) { + throw new Error('TODO'); + } + expandedOperator.quantifier = operator.quantifier; + + + const inverse = operator.inverse; + if (inverse == null || inverse === false) { + expandedOperator.inverse = false; + + } else if (inverse !== true && typeof inverse !== 'object') { + throw new Error('TODO'); + + } else { + let signifier : string[]; + let evaluate: IEvaluate; + if (inverse == true || inverse.signifier == null) { + signifier = expandedOperator.signifier.map((signifier: string) => `!${signifier}`); + } else if (typeof inverse.signifier === 'string') { + signifier = [inverse.signifier]; + } else if (isArrayOfString(signifier)) { + signifier = inverse.signifier; + } else { + throw new Error('TODO'); + } + if (inverse == true || inverse.evaluate == null) { + evaluate = async (options: IEvaluateOptions, meta: IMeta, ...args: Token[]) : Promise => { + return !(await operator.evaluate(options, meta, ...args)); + }; + } else if (typeof inverse.evaluate === 'function') { + evaluate = inverse.evaluate; + } else { + throw new Error('TODO'); + } + expandedOperator.inverse = {signifier, evaluate} + } + + if (typeof operator.evaluate !== 'function') { + throw new Error('TODO'); + } else { + expandedOperator.evaluate = operator.evaluate; + } + + for (let idx = 0; idx < expandedOperator.signifier.length; idx += 1) { + const signifier = expandedOperator.signifier[idx].toLowerCase(); + const { cased, quantifier, evaluate } = expandedOperator; + if (has(this.comparisonOperators, signifier)) { + throw new Error('comparison operator already registered'); + } + this.comparisonOperators[signifier] = { signifier, cased, quantifier, evaluate}; + } + + if (expandedOperator.inverse) { + const { cased, quantifier, inverse } = expandedOperator; + for (let idx = 0; idx < inverse.signifier.length; idx += 1) { + const signifier = inverse.signifier[idx].toLowerCase(); + const evaluate = inverse.evaluate; + if (has(this.comparisonOperators, signifier)) { + throw new Error('comparison operator already registered'); + } + this.comparisonOperators[signifier] = { signifier, cased, quantifier, evaluate }; + } + } } - static async evaluate(subject: string, meta: unknown = {}, options?: ParserOptions) : Promise { - const expression = await tokenize({ - functionHandlers: {}, - functionLookups: {}, - "if": true, - eol: 'keep', - specialSequences: true, - ...options - }, subject); - return expression.evaluate(meta); + registerBlockOperator(operator: IBlockOperator) { + } + registerFunctionHandler(name: string, handler: IFunctionHandler) { } -} \ No newline at end of file + registerFunctionLookupHandler(prefix: string, handler: IFunctionLookupHandler) { + } +} + +export { + ExpressionError, + ExpressionArgumentsError, + ExpressionSyntaxError, + ExpressionVariableError +} from './errors'; \ No newline at end of file diff --git a/src/parse/function/token/index.spec.ts b/src/parse/function/token/index.spec.ts new file mode 100644 index 0000000..e2a1949 --- /dev/null +++ b/src/parse/function/token/index.spec.ts @@ -0,0 +1,220 @@ +import '../../../../jest/helpers'; + +import TokenType from '../../../types/token-types'; + +import FunctionToken from './index'; +import Token from '../../token'; +import { TextToken } from '../../text/'; +import { type IFunctionHandler, type IFunctionLookup } from '../../../types/options'; + +const base = { + prefix: '$', + value: 'test', + arguments: [ + new TextToken({value: 'a'}), + new TextToken({value: 'b'}) + ] +}; + +describe('Default Export', () => { + test('Is a constructor', () => { + expect(typeof FunctionToken).toBe('function'); + expect(FunctionToken).toBeInstanceOf(Function); + expect(FunctionToken.prototype).toBeDefined(); + }); + + test('Has .toJSON() function', () => { + expect(FunctionToken.prototype).toHaveOwnProperty('toJSON'); + expect(typeof FunctionToken.prototype.toJSON).toBe('function'); + }); + + test('Has .evaluate() function', () => { + expect(FunctionToken.prototype).toHaveOwnProperty('evaluate'); + expect(typeof FunctionToken.prototype.evaluate).toBe('function'); + }); +}); + +describe('Constructor', () => { + + test('Errors when token is nullish', () => { + expect(() => { + // @ts-expect-error: Testing nullish token + new FunctionToken(); + }).toThrow(); + }); + + test('Errors when token is not an object', () => { + expect(() => { + // @ts-expect-error: Testing input is not an object + new FunctionToken(true); + }).toThrow(); + }); + + test('Errors when token.prefix is not a string', () => { + expect(() => { + // @ts-expect-error: Testing nullish token.prefix + new FunctionToken({}); + }).toThrow(); + + expect(() => { + // @ts-expect-error: Testing token.prefix is not a string + new FunctionToken({prefix: true}); + }).toThrow(); + }); + + test('Errors when token.value is not a string', () => { + expect(() => { + // @ts-expect-error: Testing nullish token.value + new FunctionToken({prefix: '$'}); + }).toThrow(); + + expect(() => { + // @ts-expect-error: Testing token.value is not string + new FunctionToken({prefix: '$', value: true}); + }).toThrow(); + }); + + test('Errors when token.arguments is not a populated array', () => { + expect(() => { + // @ts-expect-error: Testing unspecified arguments input + new FunctionToken({prefix: '$', value: ''}); + }).toThrow(); + + expect(() => { + // @ts-expect-error: Testing non array arguments input + new FunctionToken({prefix: '$', value: '', arguments: true}); + }).toThrow(); + + expect(() => { + // @ts-expect-error: Testing empty arguments array + new FunctionToken({prefix: '$', value: '', arguments: []}); + }).toThrow(); + }); + + test('Errors when token.arguments contains non-token instance', () => { + expect(() => { + // @ts-expect-error: Testing empty arguments array + new FunctionToken({prefix: '$', value: '', arguments: ['']}); + }).toThrow(); + }); + + test('Errors when neither handler or lookupFn are defined', () => { + expect(() => { + // @ts-expect-error: Testing neither handler or lookupFn specified + new FunctionToken({...base}); + }).toThrow(); + }); + + test('Errors when handler is not an object', () => { + expect(() => { + // @ts-expect-error: Testing handler is not an object + new FunctionToken({...base, handler: true}); + }).toThrow(); + }); + + test('Errors when handler.evaluator is nullish', () => { + expect(() => { + // @ts-expect-error: Testing handler.evaluator is not specified + new FunctionToken({...base, handler: { }}); + }).toThrow(); + }); + + test('Errors when handler.evaluator is not a function', () => { + expect(() => { + // @ts-expect-error: Testing evaluator is not a function + new FunctionToken({...base, handler: { evaluator: ''}}); + }).toThrow(); + }); + + test('Errors when specified handler.stackCheck is not a function', () => { + expect(() => { + // @ts-expect-error: Testing stackCheck is not a function + new FunctionToken({...base, handler: { stackCheck: '' }}); + }).toThrow(); + }); + + test('Errors when specified handler.argsCheck is not a function', () => { + expect(() => { + // @ts-expect-error: Testing argsCheck is not a function + new FunctionToken({...base, handler: { argsCheck: '' }}); + }).toThrow(); + }); + + test('Errors when specified lookupFn is not a function', () => { + expect(() => { + // @ts-expect-error: Testing specified lookupFn is not a function + new FunctionToken({...base, lookupFn: true}); + }).toThrow(); + }); + + test('Constructs without error when input is valid', () => { + expect(() => { + new FunctionToken({...base, handler: { evaluate: async () => undefined } }); + }).not.toThrow(); + }); +}); + +describe('Instance', () => { + const handler : IFunctionHandler = { evaluate: async () => undefined }; + const token = new FunctionToken({...base, handler }); + + test('Is instance of ListToken and Token', () => { + expect(token).toBeInstanceOf(FunctionToken); + expect(token).toBeInstanceOf(Token); + }); + + test('Sets .type to TokenType.FUNCTION', () => { + expect(token.type).toBe(TokenType.FUNCTION); + }); + + test('Stores function identifier(.value)', () => { + expect(token.value).toBe('test'); + }); + + test('Stores prefix', () => { + expect(token.prefix).toBe('$'); + }); + + test('Stores arguments', () => { + expect(token.arguments === base.arguments).toBe(true); + }); + + test('Stores handler', () => { + expect(token.handler === handler).toBe(true); + }); + + test('Stores lookupFn', () => { + const handler : IFunctionHandler = { evaluate: async () => undefined }; + const lookupFn : IFunctionLookup = async () => handler; + const token = new FunctionToken({ ...base, lookupFn }); + expect(token.lookupFn === lookupFn).toBe(true); + }); +}); + +describe('Instance#toJSON() ', () => { + const handler : IFunctionHandler = { evaluate: async () => undefined }; + let token : FunctionToken; + beforeEach(() => { + token = new FunctionToken({...base, handler }); + }); + + test('calls super.toJSON()', () => { + const spy = jest.spyOn(Token.prototype, 'toJSON'); + token.arguments = []; + + expect(() => token.toJSON()).not.toThrow(); + expect(spy).toHaveBeenCalledTimes(1); + jest.clearAllMocks(); + }); + + test('Returns prefix', () => { + const result = token.toJSON(); + expect(result).toHaveOwnProperty('prefix', '$'); + }); + + test('Returns arguments', () => { + const result = token.toJSON(); + expect(result).toHaveOwnProperty('arguments'); + expect(result.arguments).toHaveLength(2); + }); +}); \ No newline at end of file diff --git a/src/parse/function/token/index.ts b/src/parse/function/token/index.ts index 9f778d5..abff01f 100644 --- a/src/parse/function/token/index.ts +++ b/src/parse/function/token/index.ts @@ -32,7 +32,9 @@ export default class FunctionToken extends Token { if (token == null) { throw new Error('TODO - ExpressionError: token info missing') } - + if (typeof token !== 'object') { + throw new Error('TODO - ExpressionError: token must be an object'); + } if (typeof token.prefix !== 'string') { throw new Error('TODO - ExpressionError: token contains invalid prefix'); } @@ -49,29 +51,41 @@ export default class FunctionToken extends Token { throw new Error('TODO - ExpressionError: token info arguments is unpopulated array') } - if (token.handler != null) { + if (token.arguments.some((value: unknown) => !(value instanceof Token))) { + throw new Error('arguments must be a list of tokens'); + } + + if (token.handler == null && token.lookupFn == null) { + throw new Error('TODO - ExpressionError: token info not contain lookupFn or handlerFn'); + + } else if (token.handler != null) { if (token.lookupFn != null) { throw new Error('TODO - ExpressionError: token contains both lookupFn and handlerFn'); } - if (typeof token.handler !== 'object') { + + const handler : IFunctionHandler = token.handler; + if (typeof handler !== 'object') { throw new Error('TODO - ExpressionError: specified handler must be an object'); } - if (token.handler.evaluator == null) { + if (handler.evaluate == null) { throw new Error('TODO - ExpressionError: specified handler does not have .evaluator property'); } - if (typeof token.handler.evaluator !== 'function') { + if (typeof handler.evaluate !== 'function') { throw new Error('TODO - ExpressionError: specified handler.evaluator property not a function'); } - - } else if (token.lookupFn == null) { - throw new Error('TODO - ExpressionError: token info not contain lookupFn or handlerFn'); + if (handler.stackCheck != null && typeof handler.stackCheck !== 'function') { + throw new Error('TODO - ExpressionError: stackCheck must be a function'); + } + if (handler.argsCheck != null && typeof handler.argsCheck !== 'function') { + throw new Error('TODO - ExpressionError: argsCheck must be a function'); + } } else if (typeof token.lookupFn !== 'function') { throw new Error('TODO - ExpressionError: specified lookupFn must be a function'); } super({ - type: TokenType.FUNCTIONAL, + type: TokenType.FUNCTION, ...token }); this.prefix = '' + token.prefix; @@ -100,6 +114,10 @@ export default class FunctionToken extends Token { if (meta == null) { meta = {}; } + if (options.stack == null) { + options.stack = []; + } + const stack = [ ...options.stack ] let handler : IFunctionHandler; if (this.handler) { @@ -113,11 +131,26 @@ export default class FunctionToken extends Token { throw new Error(`TODO - ExpressionError: No handler for ${this.prefix}${this.value}`); } - const args : unknown[] = []; - if (this.arguments != null) { + if (!options.skipStackCheck && handler.stackCheck != null) { + try { + await handler.stackCheck.call(this, options, meta, stack); + } catch (err) { + throw new Error('TODO - ExpressionError: stack check failed') + } + } + + let args : unknown[] = []; + if (!options.verifyOnly && handler.defer === true) { + args = this.arguments; + + } else if (this.arguments != null) { const argList = this.arguments; for (let idx = 0; idx < argList.length; idx += 1) { - const arg = await argList[idx].evaluate(options, meta); + const opts = { + ...options, + stack: [...(options.stack), `${this.prefix}${this.value}`] + } + const arg = await argList[idx].evaluate(opts, meta); args.push(arg); } } @@ -126,14 +159,18 @@ export default class FunctionToken extends Token { return; } - if (!options.skipArgumentsCheck && handler.argsCheck != null) { + if ( + !options.skipArgumentsCheck && + handler.defer !== true && + handler.argsCheck != null + ) { try { - await handler.argsCheck.call(this, meta, args); + await handler.argsCheck.call(this, options, meta, args); } catch (err) { throw new Error(`TODO - ArgumentsError: ${err.message}`); } } - return handler.evaluator.call(this, meta, ...args); + return handler.evaluate.call(this, options, meta, ...args); } } \ No newline at end of file diff --git a/src/types/options.ts b/src/types/options.ts index 0665186..7566aa7 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -1,12 +1,16 @@ +export type IMeta = Record; + export interface IFunctionHandler { - stackCheck?: (stack: string[]) => Promise; - argsCheck?: (meta: unknown, ...args: unknown[]) => Promise; - evaluator: (meta: unknown, ...args: unknown[]) => Promise; + /** When true argument evaluation will defer to handling functions */ + defer?: boolean; + stackCheck?: (options: IEvaluateOptions, meta: IMeta, stack: string[]) => Promise; + argsCheck?: (options: IEvaluateOptions, meta: IMeta, ...args: unknown[]) => Promise; + evaluate: (options: IEvaluateOptions, meta: IMeta, ...args: unknown[]) => Promise; } export type IFunctionLookup = (name: string, stack?: string[], meta?: unknown) => Promise; -export default interface ParserOptions { +export default interface IParserOptions { functionHandlers?: Record; functionLookups?: Record; @@ -15,5 +19,55 @@ export default interface ParserOptions { specialSequences?: boolean; verifyOnly?: boolean; + skipStackCheck?: boolean; skipArgumentsCheck?: boolean; + + stack?: string[]; +} + +export const enum OperatorQuantifier { + LEFTONLY, + RIGHTOPTIONAL, + RIGHTREQUIRED, + PREBLOCK, + BLOCK +} + +export type IOperatorEvaluate = (options: IExpressionOptions, meta: IMeta, ...args: unknown[]) => Promise; + +export interface IOperatorDefinition { + signifier: string | string[]; + quantifier: OperatorQuantifier; + cased?: boolean; + defer?: boolean; + inverse?: { + signifier?: string | string[]; + evaluator?: IOperatorEvaluate; + }; + evaluate: IOperatorEvaluate; +} + +export interface IOperatorList { + disposition?: 'append' | 'replace'; + operators: IOperatorDefinition[]; +} + +export type ILookupHandler = (options: IExpressionOptions, meta: IMeta, stack: string[], name: string) => Promise; + +export interface IExpressionOptions { + eol?: 'error' | 'remove' | 'space' | 'keep'; + specialSequences?: boolean | Record; + + functionHandlers?: boolean | Record; + lookupHandlers?: boolean | Record; + + comparisonOperators?: boolean | IOperatorList; + blockOperators?: boolean | IOperatorList; +} + + +export interface IEvaluateOptions { + verifyOnly?: boolean; + skipStackChecks?: boolean; + skipArgumentsChecks?: boolean; } \ No newline at end of file diff --git a/src/types/token-types.ts b/src/types/token-types.ts index dc0bfd3..2f16e61 100644 --- a/src/types/token-types.ts +++ b/src/types/token-types.ts @@ -3,7 +3,7 @@ const enum TokenType { EXPRESSION, LIST, IFSTATEMENT, - FUNCTIONAL, + FUNCTION, TEXT, ARGUMENT, OPERATOR, diff --git a/src2/expressionish/index.ts b/src2/expressionish/index.ts new file mode 100644 index 0000000..74fdaa9 --- /dev/null +++ b/src2/expressionish/index.ts @@ -0,0 +1,28 @@ + +export default class Expressionish { + + private functionDenoter : string; + private specialDenoter : string; + + private groupingDenoter : IGroupDenoterQuantified; + private quotesDenoters : IQuotesQuantified[]; + + private endofline : IEndOfLineQuantified; + + constructor(options) { + + } + + registerOperator() {} + registerOperators() {} + + registerFunctionHandler() {} + registerFunctionHandlers() {} + + registerLookupHandler() {} + registerLookupHandlers() {} + + tokenize() {} + + toJSON() {} +} \ No newline at end of file diff --git a/src2/index.ts b/src2/index.ts new file mode 100644 index 0000000..6d0122f --- /dev/null +++ b/src2/index.ts @@ -0,0 +1,12 @@ +export { + ExpressionError, + ExpressionOptionsError, + ExpressionFunctionError, + ExpressionStackError, + ExpressionArgumentsError +} from './errors/index'; + +export { + default, + default as Expressionish +} from './expressionish/' \ No newline at end of file diff --git a/src2/types/global.d.ts b/src2/types/global.d.ts new file mode 100644 index 0000000..cb05f22 --- /dev/null +++ b/src2/types/global.d.ts @@ -0,0 +1,20 @@ +declare type RequireAtLeastOne = { [K in keyof T]-?: Required> & Partial>>; }[keyof T]; + +declare type IMeta = Record; + +declare enum OperatorQuantifier { + LEFTONLY, + RIGHTOPTIONAL, + RIGHTREQUIRED, + PREBLOCK, + POSTBLOCK +} + +declare enum TokenType { + UNKNOWN, + EMPTY, + TEXT, + LIST, + FUNCTION, + CONDITION +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index b725a18..c948acb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,7 +23,8 @@ "removeComments": true }, "files":[ - "./src/index.ts" + "./src2/types/global.d.ts", + "./src/expressionish/index.ts" ], "exclude": [ "node_modules/**/*"