diff --git a/.github/actions/check-public-api/index.js b/.github/actions/check-public-api/index.js
index b0517e1ee6..7de7f06375 100644
--- a/.github/actions/check-public-api/index.js
+++ b/.github/actions/check-public-api/index.js
@@ -57831,4303 +57831,6 @@ module.exports = {
module.exports = __nccwpck_require__(9023).deprecate;
-/***/ }),
-
-/***/ 5231:
-/***/ (function(module) {
-
-/*!
- * Voca string library 1.4.1
- * https://vocajs.pages.dev
- *
- * Copyright Dmitri Pavlutin and other contributors
- * Released under the MIT license
- */
-
-(function (global, factory) {
- true ? module.exports = factory() :
- 0;
-}(this, (function () { 'use strict';
-
- function _extends() {
- _extends = Object.assign || function (target) {
- for (var i = 1; i < arguments.length; i++) {
- var source = arguments[i];
-
- for (var key in source) {
- if (Object.prototype.hasOwnProperty.call(source, key)) {
- target[key] = source[key];
- }
- }
- }
-
- return target;
- };
-
- return _extends.apply(this, arguments);
- }
-
- function _slicedToArray(arr, i) {
- return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
- }
-
- function _toConsumableArray(arr) {
- return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
- }
-
- function _arrayWithoutHoles(arr) {
- if (Array.isArray(arr)) {
- for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
-
- return arr2;
- }
- }
-
- function _arrayWithHoles(arr) {
- if (Array.isArray(arr)) return arr;
- }
-
- function _iterableToArray(iter) {
- if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
- }
-
- function _iterableToArrayLimit(arr, i) {
- if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
- return;
- }
-
- var _arr = [];
- var _n = true;
- var _d = false;
- var _e = undefined;
-
- try {
- for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
- _arr.push(_s.value);
-
- if (i && _arr.length === i) break;
- }
- } catch (err) {
- _d = true;
- _e = err;
- } finally {
- try {
- if (!_n && _i["return"] != null) _i["return"]();
- } finally {
- if (_d) throw _e;
- }
- }
-
- return _arr;
- }
-
- function _nonIterableSpread() {
- throw new TypeError("Invalid attempt to spread non-iterable instance");
- }
-
- function _nonIterableRest() {
- throw new TypeError("Invalid attempt to destructure non-iterable instance");
- }
-
- /**
- * Checks if `value` is `null` or `undefined`
- *
- * @ignore
- * @function isNil
- * @param {*} value The object to check
- * @return {boolean} Returns `true` is `value` is `undefined` or `null`, `false` otherwise
- */
- function isNil(value) {
- return value === undefined || value === null;
- }
-
- /**
- * Converts the `value` to a boolean. If `value` is `undefined` or `null`, returns `defaultValue`.
- *
- * @ignore
- * @function toBoolean
- * @param {*} value The value to convert.
- * @param {boolean} [defaultValue=false] The default value.
- * @return {boolean} Returns the coercion to boolean.
- */
-
- function coerceToBoolean(value) {
- var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
-
- if (isNil(value)) {
- return defaultValue;
- }
-
- return Boolean(value);
- }
-
- /**
- * Checks whether `subject` is a string primitive type.
- *
- * @function isString
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} subject The value to verify.
- * @return {boolean} Returns `true` if `subject` is string primitive type or `false` otherwise.
- * @example
- * v.isString('vacation');
- * // => true
- *
- * v.isString(560);
- * // => false
- */
- function isString(subject) {
- return typeof subject === 'string';
- }
-
- /**
- * Get the string representation of the `value`.
- * Converts the `value` to string.
- * If `value` is `null` or `undefined`, return `defaultValue`.
- *
- * @ignore
- * @function toString
- * @param {*} value The value to convert.
- * @param {*} [defaultValue=''] The default value to return.
- * @return {string|null} Returns the string representation of `value`. Returns `defaultValue` if `value` is
- * `null` or `undefined`.
- */
-
- function coerceToString(value) {
- var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
-
- if (isNil(value)) {
- return defaultValue;
- }
-
- if (isString(value)) {
- return value;
- }
-
- return String(value);
- }
-
- /**
- * Converts the first character of `subject` to upper case. If `restToLower` is `true`, convert the rest of
- * `subject` to lower case.
- *
- * @function capitalize
- * @static
- * @since 1.0.0
- * @memberOf Case
- * @param {string} [subject=''] The string to capitalize.
- * @param {boolean} [restToLower=false] Convert the rest of `subject` to lower case.
- * @return {string} Returns the capitalized string.
- * @example
- * v.capitalize('apple');
- * // => 'Apple'
- *
- * v.capitalize('aPPle', true);
- * // => 'Apple'
- */
-
- function capitalize(subject, restToLower) {
- var subjectString = coerceToString(subject);
- var restToLowerCaseBoolean = coerceToBoolean(restToLower);
-
- if (subjectString === '') {
- return '';
- }
-
- if (restToLowerCaseBoolean) {
- subjectString = subjectString.toLowerCase();
- }
-
- return subjectString.substr(0, 1).toUpperCase() + subjectString.substr(1);
- }
-
- /**
- * Converts the `subject` to lower case.
- *
- * @function lowerCase
- * @static
- * @since 1.0.0
- * @memberOf Case
- * @param {string} [subject=''] The string to convert to lower case.
- * @return {string} Returns the lower case string.
- * @example
- * v.lowerCase('Green');
- * // => 'green'
- *
- * v.lowerCase('BLUE');
- * // => 'blue'
- */
-
- function lowerCase(subject) {
- var subjectString = coerceToString(subject, '');
- return subjectString.toLowerCase();
- }
-
- /**
- * A regular expression string matching digits
- *
- * @type {string}
- * @ignore
- */
- var digit = '\\d';
- /**
- * A regular expression string matching whitespace
- *
- * @type {string}
- * @ignore
- */
-
- var whitespace = '\\s\\uFEFF\\xA0';
- /**
- * A regular expression string matching high surrogate
- *
- * @type {string}
- * @ignore
- */
-
- var highSurrogate = '\\uD800-\\uDBFF';
- /**
- * A regular expression string matching low surrogate
- *
- * @type {string}
- * @ignore
- */
-
- var lowSurrogate = '\\uDC00-\\uDFFF';
- /**
- * A regular expression string matching diacritical mark
- *
- * @type {string}
- * @ignore
- */
-
- var diacriticalMark = '\\u0300-\\u036F\\u1AB0-\\u1AFF\\u1DC0-\\u1DFF\\u20D0-\\u20FF\\uFE20-\\uFE2F';
- /**
- * A regular expression to match the base character for a combining mark
- *
- * @type {string}
- * @ignore
- */
-
- var base = '\\0-\\u02FF\\u0370-\\u1AAF\\u1B00-\\u1DBF\\u1E00-\\u20CF\\u2100-\\uD7FF\\uE000-\\uFE1F\\uFE30-\\uFFFF';
- /**
- * Regular expression to match combining marks
- *
- * @see http://unicode.org/faq/char_combmark.html
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_COMBINING_MARKS = new RegExp('([' + base + ']|[' + highSurrogate + '][' + lowSurrogate + ']|[' + highSurrogate + '](?![' + lowSurrogate + '])|(?:[^' + highSurrogate + ']|^)[' + lowSurrogate + '])([' + diacriticalMark + ']+)', 'g');
- /**
- * Regular expression to match surrogate pairs
- *
- * @see http://www.unicode.org/faq/utf_bom.html#utf16-2
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_SURROGATE_PAIRS = new RegExp('([' + highSurrogate + '])([' + lowSurrogate + '])', 'g');
- /**
- * Regular expression to match a unicode character
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_UNICODE_CHARACTER = new RegExp('((?:[' + base + ']|[' + highSurrogate + '][' + lowSurrogate + ']|[' + highSurrogate + '](?![' + lowSurrogate + '])|(?:[^' + highSurrogate + ']|^)[' + lowSurrogate + '])(?:[' + diacriticalMark + ']+))|\
-([' + highSurrogate + '][' + lowSurrogate + '])|\
-([\\n\\r\\u2028\\u2029])|\
-(.)', 'g');
- /**
- * Regular expression to match whitespaces
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_WHITESPACE = new RegExp('[' + whitespace + ']');
- /**
- * Regular expression to match whitespaces from the left side
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_TRIM_LEFT = new RegExp('^[' + whitespace + ']+');
- /**
- * Regular expression to match whitespaces from the right side
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_TRIM_RIGHT = new RegExp('[' + whitespace + ']+$');
- /**
- * Regular expression to match digit characters
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_DIGIT = new RegExp('^' + digit + '+$');
- /**
- * Regular expression to match regular expression special characters
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_SPECIAL_CHARACTERS = /[-[\]{}()*+!<=:?./\\^$|#,]/g;
- /**
- * Regular expression to match not latin characters
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_NON_LATIN = /[^A-Za-z0-9]/g;
- /**
- * Regular expression to match HTML special characters.
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_HTML_SPECIAL_CHARACTERS = /[<>&"'`]/g;
- /**
- * Regular expression to match sprintf format string
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_CONVERSION_SPECIFICATION = /(%{1,2})(?:(\d+)\$)?(\+)?([ 0]|'.{1})?(-)?(\d+)?(?:\.(\d+))?([bcdiouxXeEfgGs])?/g;
- /**
- * Regular expression to match trailing zeros in a number
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_TRAILING_ZEROS = /\.?0+$/g;
- /**
- * Regular expression to match a list of tags.
- *
- * @see https://html.spec.whatwg.org/multipage/syntax.html#syntax-tag-name
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_TAG_LIST = /<([A-Za-z0-9]+)>/g;
-
- /**
- * A regular expression to match the General Punctuation Unicode block
- *
- * @type {string}
- * @ignore
- */
-
- var generalPunctuationBlock = '\\u2000-\\u206F';
- /**
- * A regular expression to match non characters from from Basic Latin and Latin-1 Supplement Unicode blocks
- *
- * @type {string}
- * @ignore
- */
-
- var nonCharacter = '\\x00-\\x2F\\x3A-\\x40\\x5B-\\x60\\x7b-\\xBF\\xD7\\xF7';
- /**
- * A regular expression to match the dingbat Unicode block
- *
- * @type {string}
- * @ignore
- */
-
- var dingbatBlock = '\\u2700-\\u27BF';
- /**
- * A regular expression string that matches lower case letters: LATIN
- *
- * @type {string}
- * @ignore
- */
-
- var lowerCaseLetter = 'a-z\\xB5\\xDF-\\xF6\\xF8-\\xFF\\u0101\\u0103\\u0105\\u0107\\u0109\\u010B\\u010D\\u010F\\u0111\\u0113\\u0115\\u0117\\u0119\\u011B\\u011D\\u011F\\u0121\\u0123\\u0125\\u0127\\u0129\\u012B\\u012D\\u012F\\u0131\\u0133\\u0135\\u0137\\u0138\\u013A\\u013C\\u013E\\u0140\\u0142\\u0144\\u0146\\u0148\\u0149\\u014B\\u014D\\u014F\\u0151\\u0153\\u0155\\u0157\\u0159\\u015B\\u015D\\u015F\\u0161\\u0163\\u0165\\u0167\\u0169\\u016B\\u016D\\u016F\\u0171\\u0173\\u0175\\u0177\\u017A\\u017C\\u017E-\\u0180\\u0183\\u0185\\u0188\\u018C\\u018D\\u0192\\u0195\\u0199-\\u019B\\u019E\\u01A1\\u01A3\\u01A5\\u01A8\\u01AA\\u01AB\\u01AD\\u01B0\\u01B4\\u01B6\\u01B9\\u01BA\\u01BD-\\u01BF\\u01C6\\u01C9\\u01CC\\u01CE\\u01D0\\u01D2\\u01D4\\u01D6\\u01D8\\u01DA\\u01DC\\u01DD\\u01DF\\u01E1\\u01E3\\u01E5\\u01E7\\u01E9\\u01EB\\u01ED\\u01EF\\u01F0\\u01F3\\u01F5\\u01F9\\u01FB\\u01FD\\u01FF\\u0201\\u0203\\u0205\\u0207\\u0209\\u020B\\u020D\\u020F\\u0211\\u0213\\u0215\\u0217\\u0219\\u021B\\u021D\\u021F\\u0221\\u0223\\u0225\\u0227\\u0229\\u022B\\u022D\\u022F\\u0231\\u0233-\\u0239\\u023C\\u023F\\u0240\\u0242\\u0247\\u0249\\u024B\\u024D\\u024F';
- /**
- * A regular expression string that matches upper case letters: LATIN
- *
- * @type {string}
- * @ignore
- */
-
- var upperCaseLetter = '\\x41-\\x5a\\xc0-\\xd6\\xd8-\\xde\\u0100\\u0102\\u0104\\u0106\\u0108\\u010a\\u010c\\u010e\\u0110\\u0112\\u0114\\u0116\\u0118\\u011a\\u011c\\u011e\\u0120\\u0122\\u0124\\u0126\\u0128\\u012a\\u012c\\u012e\\u0130\\u0132\\u0134\\u0136\\u0139\\u013b\\u013d\\u013f\\u0141\\u0143\\u0145\\u0147\\u014a\\u014c\\u014e\\u0150\\u0152\\u0154\\u0156\\u0158\\u015a\\u015c\\u015e\\u0160\\u0162\\u0164\\u0166\\u0168\\u016a\\u016c\\u016e\\u0170\\u0172\\u0174\\u0176\\u0178\\u0179\\u017b\\u017d\\u0181\\u0182\\u0184\\u0186\\u0187\\u0189-\\u018b\\u018e-\\u0191\\u0193\\u0194\\u0196-\\u0198\\u019c\\u019d\\u019f\\u01a0\\u01a2\\u01a4\\u01a6\\u01a7\\u01a9\\u01ac\\u01ae\\u01af\\u01b1-\\u01b3\\u01b5\\u01b7\\u01b8\\u01bc\\u01c4\\u01c5\\u01c7\\u01c8\\u01ca\\u01cb\\u01cd\\u01cf\\u01d1\\u01d3\\u01d5\\u01d7\\u01d9\\u01db\\u01de\\u01e0\\u01e2\\u01e4\\u01e6\\u01e8\\u01ea\\u01ec\\u01ee\\u01f1\\u01f2\\u01f4\\u01f6-\\u01f8\\u01fa\\u01fc\\u01fe\\u0200\\u0202\\u0204\\u0206\\u0208\\u020a\\u020c\\u020e\\u0210\\u0212\\u0214\\u0216\\u0218\\u021a\\u021c\\u021e\\u0220\\u0222\\u0224\\u0226\\u0228\\u022a\\u022c\\u022e\\u0230\\u0232\\u023a\\u023b\\u023d\\u023e\\u0241\\u0243-\\u0246\\u0248\\u024a\\u024c\\u024e';
- /**
- * Regular expression to match Unicode words
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_WORD = new RegExp('(?:[' + upperCaseLetter + '][' + diacriticalMark + ']*)?(?:[' + lowerCaseLetter + '][' + diacriticalMark + ']*)+|\
-(?:[' + upperCaseLetter + '][' + diacriticalMark + ']*)+(?![' + lowerCaseLetter + '])|\
-[' + digit + ']+|\
-[' + dingbatBlock + ']|\
-[^' + nonCharacter + generalPunctuationBlock + whitespace + ']+', 'g');
- /**
- * Regular expression to match words from Basic Latin and Latin-1 Supplement blocks
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_LATIN_WORD = /[A-Z\xC0-\xD6\xD8-\xDE]?[a-z\xDF-\xF6\xF8-\xFF]+|[A-Z\xC0-\xD6\xD8-\xDE]+(?![a-z\xDF-\xF6\xF8-\xFF])|\d+/g;
- /**
- * Regular expression to match alpha characters
- *
- * @see http://stackoverflow.com/a/22075070/1894471
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_ALPHA = new RegExp('^(?:[' + lowerCaseLetter + upperCaseLetter + '][' + diacriticalMark + ']*)+$');
- /**
- * Regular expression to match alpha and digit characters
- *
- * @see http://stackoverflow.com/a/22075070/1894471
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_ALPHA_DIGIT = new RegExp('^((?:[' + lowerCaseLetter + upperCaseLetter + '][' + diacriticalMark + ']*)|[' + digit + '])+$');
- /**
- * Regular expression to match Extended ASCII characters, i.e. the first 255
- *
- * @type {RegExp}
- * @ignore
- */
-
- var REGEXP_EXTENDED_ASCII = /^[\x01-\xFF]*$/;
-
- /**
- * Verifies if `value` is `undefined` or `null` and returns `defaultValue`. In other case returns `value`.
- *
- * @ignore
- * @function nilDefault
- * @param {*} value The value to verify.
- * @param {*} defaultValue The default value.
- * @return {*} Returns `defaultValue` if `value` is `undefined` or `null`, otherwise `defaultValue`.
- */
- function nilDefault(value, defaultValue) {
- return value == null ? defaultValue : value;
- }
-
- /**
- * Get the string representation of the `value`.
- * Converts the `value` to string.
- *
- * @ignore
- * @function toString
- * @param {*} value The value to convert.
- * @return {string|null} Returns the string representation of `value`.
- */
-
- function toString(value) {
- if (isNil(value)) {
- return null;
- }
-
- if (isString(value)) {
- return value;
- }
-
- return String(value);
- }
-
- /**
- * Splits `subject` into an array of words.
- *
- * @function words
- * @static
- * @since 1.0.0
- * @memberOf Split
- * @param {string} [subject=''] The string to split into words.
- * @param {string|RegExp} [pattern] The pattern to watch words. If `pattern` is not RegExp, it is transformed to `new RegExp(pattern, flags)`.
- * @param {string} [flags=''] The regular expression flags. Applies when `pattern` is string type.
- * @return {Array} Returns the array of words.
- * @example
- * v.words('gravity can cross dimensions');
- * // => ['gravity', 'can', 'cross', 'dimensions']
- *
- * v.words('GravityCanCrossDimensions');
- * // => ['Gravity', 'Can', 'Cross', 'Dimensions']
- *
- * v.words('Gravity - can cross dimensions!');
- * // => ['Gravity', 'can', 'cross', 'dimensions']
- *
- * v.words('Earth gravity', /[^\s]+/g);
- * // => ['Earth', 'gravity']
- */
-
- function words(subject, pattern, flags) {
- var subjectString = coerceToString(subject);
- var patternRegExp;
-
- if (isNil(pattern)) {
- patternRegExp = REGEXP_EXTENDED_ASCII.test(subjectString) ? REGEXP_LATIN_WORD : REGEXP_WORD;
- } else if (pattern instanceof RegExp) {
- patternRegExp = pattern;
- } else {
- var flagsString = toString(nilDefault(flags, ''));
- patternRegExp = new RegExp(toString(pattern), flagsString);
- }
-
- return nilDefault(subjectString.match(patternRegExp), []);
- }
-
- /**
- * Transforms the `word` into camel case chunk.
- *
- * @param {string} word The word string
- * @param {number} index The index of the word in phrase.
- * @return {string} The transformed word.
- * @ignore
- */
-
- function wordToCamel(word, index) {
- return index === 0 ? lowerCase(word) : capitalize(word, true);
- }
- /**
- * Converts the `subject` to camel case.
- *
- * @function camelCase
- * @static
- * @since 1.0.0
- * @memberOf Case
- * @param {string} [subject=''] The string to convert to camel case.
- * @return {string} The camel case string.
- * @example
- * v.camelCase('bird flight');
- * // => 'birdFlight'
- *
- * v.camelCase('BirdFlight');
- * // => 'birdFlight'
- *
- * v.camelCase('-BIRD-FLIGHT-');
- * // => 'birdFlight'
- */
-
-
- function camelCase(subject) {
- var subjectString = coerceToString(subject);
-
- if (subjectString === '') {
- return '';
- }
-
- return words(subjectString).map(wordToCamel).join('');
- }
-
- /**
- * Converts the first character of `subject` to lower case.
- *
- * @function decapitalize
- * @static
- * @since 1.0.0
- * @memberOf Case
- * @param {string} [subject=''] The string to decapitalize.
- * @return {string} Returns the decapitalized string.
- * @example
- * v.decapitalize('Sun');
- * // => 'sun'
- *
- * v.decapitalize('moon');
- * // => 'moon'
- */
-
- function decapitalize(subject) {
- var subjectString = coerceToString(subject);
-
- if (subjectString === '') {
- return '';
- }
-
- return subjectString.substr(0, 1).toLowerCase() + subjectString.substr(1);
- }
-
- /**
- * Converts the `subject` to kebab case,
- * also called spinal case or lisp case.
- *
- * @function kebabCase
- * @static
- * @since 1.0.0
- * @memberOf Case
- * @param {string} [subject=''] The string to convert to kebab case.
- * @return {string} Returns the kebab case string.
- * @example
- * v.kebabCase('goodbye blue sky');
- * // => 'goodbye-blue-sky'
- *
- * v.kebabCase('GoodbyeBlueSky');
- * // => 'goodbye-blue-sky'
- *
- * v.kebabCase('-Goodbye-Blue-Sky-');
- * // => 'goodbye-blue-sky'
- */
-
- function kebabCase(subject) {
- var subjectString = coerceToString(subject);
-
- if (subjectString === '') {
- return '';
- }
-
- return words(subjectString).map(lowerCase).join('-');
- }
-
- /**
- * Converts the `subject` to snake case.
- *
- * @function snakeCase
- * @static
- * @since 1.0.0
- * @memberOf Case
- * @param {string} [subject=''] The string to convert to snake case.
- * @return {string} Returns the snake case string.
- * @example
- * v.snakeCase('learning to fly');
- * // => 'learning_to_fly'
- *
- * v.snakeCase('LearningToFly');
- * // => 'learning_to_fly'
- *
- * v.snakeCase('-Learning-To-Fly-');
- * // => 'learning_to_fly'
- */
-
- function snakeCase(subject) {
- var subjectString = coerceToString(subject);
-
- if (subjectString === '') {
- return '';
- }
-
- return words(subjectString).map(lowerCase).join('_');
- }
-
- /**
- * Converts the `subject` to upper case.
- *
- * @function upperCase
- * @static
- * @since 1.0.0
- * @memberOf Case
- * @param {string} [subject=''] The string to convert to upper case.
- * @return {string} Returns the upper case string.
- * @example
- * v.upperCase('school');
- * // => 'SCHOOL'
- */
-
- function upperCase(subject) {
- var subjectString = coerceToString(subject);
- return subjectString.toUpperCase();
- }
-
- /**
- * Converts the uppercase alpha characters of `subject` to lowercase and lowercase
- * characters to uppercase.
- *
- * @function swapCase
- * @static
- * @since 1.3.0
- * @memberOf Case
- * @param {string} [subject=''] The string to swap the case.
- * @return {string} Returns the converted string.
- * @example
- * v.swapCase('League of Shadows');
- * // => 'lEAGUE OF sHADOWS'
- *
- * v.swapCase('2 Bees');
- * // => '2 bEES'
- */
-
- function swapCase(subject) {
- var subjectString = coerceToString(subject);
- return subjectString.split('').reduce(swapAndConcat, '');
- }
-
- function swapAndConcat(swapped, character) {
- var lowerCase = character.toLowerCase();
- var upperCase = character.toUpperCase();
- return swapped + (character === lowerCase ? upperCase : lowerCase);
- }
-
- /**
- * Converts the subject to title case.
- *
- * @function titleCase
- * @static
- * @since 1.4.0
- * @memberOf Case
- * @param {string} [subject=''] The string to convert to title case.
- * @param {Array} [noSplit] Do not split words at the specified characters.
- * @return {string} Returns the title case string.
- * @example
- * v.titleCase('learning to fly');
- * // => 'Learning To Fly'
- *
- * v.titleCase('jean-luc is good-looking', ['-']);
- * // => 'Jean-luc Is Good-looking'
- */
-
- function titleCase(subject, noSplit) {
- var subjectString = coerceToString(subject);
- var noSplitArray = Array.isArray(noSplit) ? noSplit : [];
- var wordsRegExp = REGEXP_EXTENDED_ASCII.test(subjectString) ? REGEXP_LATIN_WORD : REGEXP_WORD;
- return subjectString.replace(wordsRegExp, function (word, index) {
- var isNoSplit = index > 0 && noSplitArray.indexOf(subjectString[index - 1]) >= 0;
- return isNoSplit ? word.toLowerCase() : capitalize(word, true);
- });
- }
-
- /**
- * Clip the number to interval `downLimit` to `upLimit`.
- *
- * @ignore
- * @function clipNumber
- * @param {number} value The number to clip
- * @param {number} downLimit The down limit
- * @param {number} upLimit The upper limit
- * @return {number} The clipped number
- */
- function clipNumber(value, downLimit, upLimit) {
- if (value <= downLimit) {
- return downLimit;
- }
-
- if (value >= upLimit) {
- return upLimit;
- }
-
- return value;
- }
-
- /**
- * Max save integer value
- *
- * @ignore
- * @type {number}
- */
- var MAX_SAFE_INTEGER = 0x1fffffffffffff;
-
- /**
- * Transforms `value` to an integer.
- *
- * @ignore
- * @function toInteger
- * @param {number} value The number to transform.
- * @returns {number} Returns the transformed integer.
- */
-
- function toInteger(value) {
- if (value === Infinity) {
- return MAX_SAFE_INTEGER;
- }
-
- if (value === -Infinity) {
- return -MAX_SAFE_INTEGER;
- }
-
- return ~~value;
- }
-
- /**
- * Truncates `subject` to a new `length`.
- *
- * @function truncate
- * @static
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to truncate.
- * @param {int} length The length to truncate the string.
- * @param {string} [end='...'] The string to be added at the end.
- * @return {string} Returns the truncated string.
- * @example
- * v.truncate('Once upon a time', 7);
- * // => 'Once...'
- *
- * v.truncate('Good day, Little Red Riding Hood', 14, ' (...)');
- * // => 'Good day (...)'
- *
- * v.truncate('Once upon', 10);
- * // => 'Once upon'
- */
-
- function truncate(subject, length, end) {
- var subjectString = coerceToString(subject);
- var lengthInt = isNil(length) ? subjectString.length : clipNumber(toInteger(length), 0, MAX_SAFE_INTEGER);
- var endString = coerceToString(end, '...');
-
- if (lengthInt >= subjectString.length) {
- return subjectString;
- }
-
- return subjectString.substr(0, length - endString.length) + endString;
- }
-
- /**
- * Access a character from `subject` at specified `position`.
- *
- * @function charAt
- * @static
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to extract from.
- * @param {numbers} position The position to get the character.
- * @return {string} Returns the character at specified position.
- * @example
- * v.charAt('helicopter', 0);
- * // => 'h'
- *
- * v.charAt('helicopter', 1);
- * // => 'e'
- */
-
- function charAt(subject, position) {
- var subjectString = coerceToString(subject);
- return subjectString.charAt(position);
- }
-
- var HIGH_SURROGATE_START = 0xd800;
- var HIGH_SURROGATE_END = 0xdbff;
- var LOW_SURROGATE_START = 0xdc00;
- var LOW_SURROGATE_END = 0xdfff;
- /**
- * Checks if `codePoint` is a high-surrogate number from range 0xD800 to 0xDBFF.
- *
- * @ignore
- * @param {number} codePoint The code point number to be verified
- * @return {boolean} Returns a boolean whether `codePoint` is a high-surrogate number.
- */
-
- function isHighSurrogate(codePoint) {
- return codePoint >= HIGH_SURROGATE_START && codePoint <= HIGH_SURROGATE_END;
- }
- /**
- * Checks if `codePoint` is a low-surrogate number from range 0xDC00 to 0xDFFF.
- *
- * @ignore
- * @param {number} codePoint The code point number to be verified
- * @return {boolean} Returns a boolean whether `codePoint` is a low-surrogate number.
- */
-
- function isLowSurrogate(codePoint) {
- return codePoint >= LOW_SURROGATE_START && codePoint <= LOW_SURROGATE_END;
- }
- /**
- * Get the astral code point number based on surrogate pair numbers.
- *
- * @ignore
- * @param {number} highSurrogate The high-surrogate code point number.
- * @param {number} lowSurrogate The low-surrogate code point number.
- * @return {number} Returns the astral symbol number.
- */
-
- function getAstralNumberFromSurrogatePair(highSurrogate, lowSurrogate) {
- return (highSurrogate - HIGH_SURROGATE_START) * 0x400 + lowSurrogate - LOW_SURROGATE_START + 0x10000;
- }
-
- /**
- * Get the number representation of the `value`.
- * Converts the `value` to number.
- * If `value` is `null` or `undefined`, return `defaultValue`.
- *
- * @ignore
- * @function toString
- * @param {*} value The value to convert.
- * @param {*} [defaultValue=''] The default value to return.
- * @return {number|null} Returns the number representation of `value`. Returns `defaultValue` if `value` is
- * `null` or `undefined`.
- */
-
- function coerceToNumber(value) {
- var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
-
- if (isNil(value)) {
- return defaultValue;
- }
-
- if (typeof value === 'number') {
- return value;
- }
-
- return Number(value);
- }
-
- /**
- * If `value` is `NaN`, return `defaultValue`. In other case returns `value`.
- *
- * @ignore
- * @function nanDefault
- * @param {*} value The value to verify.
- * @param {*} defaultValue The default value.
- * @return {*} Returns `defaultValue` if `value` is `NaN`, otherwise `defaultValue`.
- */
- function nanDefault(value, defaultValue) {
- return value !== value ? defaultValue : value;
- }
-
- /**
- * Get the Unicode code point value of the character at `position`.
- * If a valid UTF-16
- * surrogate pair starts at `position`, the
- * astral code point
- * value at `position` is returned.
- *
- * @function codePointAt
- * @static
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to extract from.
- * @param {number} position The position to get the code point number.
- * @return {number} Returns a non-negative number less than or equal to `0x10FFFF`.
- * @example
- * v.codePointAt('rain', 1);
- * // => 97, or 0x0061
- *
- * v.codePointAt('\uD83D\uDE00 is smile', 0); // or '😀 is smile'
- * // => 128512, or 0x1F600
- */
-
- function codePointAt(subject, position) {
- var subjectString = coerceToString(subject);
- var subjectStringLength = subjectString.length;
- var positionNumber = coerceToNumber(position);
- positionNumber = nanDefault(positionNumber, 0);
-
- if (positionNumber < 0 || positionNumber >= subjectStringLength) {
- return undefined;
- }
-
- var firstCodePoint = subjectString.charCodeAt(positionNumber);
- var secondCodePoint;
-
- if (isHighSurrogate(firstCodePoint) && subjectStringLength > positionNumber + 1) {
- secondCodePoint = subjectString.charCodeAt(positionNumber + 1);
-
- if (isLowSurrogate(secondCodePoint)) {
- return getAstralNumberFromSurrogatePair(firstCodePoint, secondCodePoint);
- }
- }
-
- return firstCodePoint;
- }
-
- /**
- * Extracts the first `length` characters from `subject`.
- *
- * @function first
- * @static
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to extract from.
- * @param {int} [length=1] The number of characters to extract.
- * @return {string} Returns the first characters string.
- * @example
- * v.first('helicopter');
- * // => 'h'
- *
- * v.first('vehicle', 2);
- * // => 've'
- *
- * v.first('car', 5);
- * // => 'car'
- */
-
- function first(subject, length) {
- var subjectString = coerceToString(subject);
- var lengthInt = isNil(length) ? 1 : clipNumber(toInteger(length), 0, MAX_SAFE_INTEGER);
-
- if (subjectString.length <= lengthInt) {
- return subjectString;
- }
-
- return subjectString.substr(0, lengthInt);
- }
-
- /**
- * Get a grapheme from `subject` at specified `position` taking care of
- * surrogate pairs and
- * combining marks.
- *
- * @function graphemeAt
- * @static
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to extract from.
- * @param {number} position The position to get the grapheme.
- * @return {string} Returns the grapheme at specified position.
- * @example
- * v.graphemeAt('\uD835\uDC00\uD835\uDC01', 0); // or '𝐀𝐁'
- * // => 'A'
- *
- * v.graphemeAt('cafe\u0301', 3); // or 'café'
- * // => 'é'
- */
-
- function graphemeAt(subject, position) {
- var subjectString = coerceToString(subject);
- var positionNumber = coerceToNumber(position);
- var graphemeMatch;
- var graphemeMatchIndex = 0;
- positionNumber = nanDefault(positionNumber, 0);
-
- while ((graphemeMatch = REGEXP_UNICODE_CHARACTER.exec(subjectString)) !== null) {
- if (graphemeMatchIndex === positionNumber) {
- REGEXP_UNICODE_CHARACTER.lastIndex = 0;
- return graphemeMatch[0];
- }
-
- graphemeMatchIndex++;
- }
-
- return '';
- }
-
- /**
- * Extracts the last `length` characters from `subject`.
- *
- * @function last
- * @static
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to extract from.
- * @param {int} [length=1] The number of characters to extract.
- * @return {string} Returns the last characters string.
- * @example
- * v.last('helicopter');
- * // => 'r'
- *
- * v.last('vehicle', 2);
- * // => 'le'
- *
- * v.last('car', 5);
- * // => 'car'
- */
-
- function last(subject, length) {
- var subjectString = coerceToString(subject);
- var lengthInt = isNil(length) ? 1 : clipNumber(toInteger(length), 0, MAX_SAFE_INTEGER);
-
- if (subjectString.length <= lengthInt) {
- return subjectString;
- }
-
- return subjectString.substr(subjectString.length - lengthInt, lengthInt);
- }
-
- /**
- * Truncates `subject` to a new `length` and does not break the words. Guarantees that the truncated string is no longer
- * than `length`.
- *
- * @static
- * @function prune
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to prune.
- * @param {int} length The length to prune the string.
- * @param {string} [end='...'] The string to be added at the end.
- * @return {string} Returns the pruned string.
- * @example
- * v.prune('Once upon a time', 7);
- * // => 'Once...'
- *
- * v.prune('Good day, Little Red Riding Hood', 16, ' (more)');
- * // => 'Good day (more)'
- *
- * v.prune('Once upon', 10);
- * // => 'Once upon'
- */
-
- function prune(subject, length, end) {
- var subjectString = coerceToString(subject);
- var lengthInt = isNil(length) ? subjectString.length : clipNumber(toInteger(length), 0, MAX_SAFE_INTEGER);
- var endString = coerceToString(end, '...');
-
- if (lengthInt >= subjectString.length) {
- return subjectString;
- }
-
- var pattern = REGEXP_EXTENDED_ASCII.test(subjectString) ? REGEXP_LATIN_WORD : REGEXP_WORD;
- var truncatedLength = 0;
- subjectString.replace(pattern, function (word, offset) {
- var wordInsertLength = offset + word.length;
-
- if (wordInsertLength <= lengthInt - endString.length) {
- truncatedLength = wordInsertLength;
- }
- });
- return subjectString.substr(0, truncatedLength) + endString;
- }
-
- /**
- * Extracts from `subject` a string from `start` position up to `end` position. The character at `end` position is not
- * included.
- *
- * @function slice
- * @static
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to extract from.
- * @param {number} start The position to start extraction. If negative use `subject.length + start`.
- * @param {number} [end=subject.length] The position to end extraction. If negative use `subject.length + end`.
- * @return {string} Returns the extracted string.
- * @note Uses native `String.prototype.slice()`
- * @example
- * v.slice('miami', 1);
- * // => 'iami'
- *
- * v.slice('florida', -4);
- * // => 'rida'
- *
- * v.slice('florida', 1, 4);
- * // => "lor"
- */
-
- function slice(subject, start, end) {
- return coerceToString(subject).slice(start, end);
- }
-
- /**
- * Extracts from `subject` a string from `start` position a number of `length` characters.
- *
- * @function substr
- * @static
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to extract from.
- * @param {number} start The position to start extraction.
- * @param {number} [length=subject.endOfString] The number of characters to extract. If omitted, extract to the end of `subject`.
- * @return {string} Returns the extracted string.
- * @note Uses native `String.prototype.substr()`
- * @example
- * v.substr('infinite loop', 9);
- * // => 'loop'
- *
- * v.substr('dreams', 2, 2);
- * // => 'ea'
- */
-
- function substr(subject, start, length) {
- return coerceToString(subject).substr(start, length);
- }
-
- /**
- * Extracts from `subject` a string from `start` position up to `end` position. The character at `end` position is not
- * included.
- *
- * @function substring
- * @static
- * @since 1.0.0
- * @memberOf Chop
- * @param {string} [subject=''] The string to extract from.
- * @param {number} start The position to start extraction.
- * @param {number} [end=subject.length] The position to end extraction.
- * @return {string} Returns the extracted string.
- * @note Uses native `String.prototype.substring()`
- * @example
- * v.substring('beach', 1);
- * // => 'each'
- *
- * v.substring('ocean', 1, 3);
- * // => 'ea'
- */
-
- function substring(subject, start, end) {
- return coerceToString(subject).substring(start, end);
- }
-
- /**
- * Counts the characters in `subject`.
- *
- * @function count
- * @static
- * @since 1.0.0
- * @memberOf Count
- * @param {string} [subject=''] The string to count characters.
- * @return {number} Returns the number of characters in `subject`.
- * @example
- * v.count('rain');
- * // => 4
- */
-
- function count(subject) {
- return coerceToString(subject).length;
- }
-
- /**
- * Counts the graphemes in `subject` taking care of
- * surrogate pairs and
- * combining marks.
- *
- * @function countGraphemes
- * @static
- * @since 1.0.0
- * @memberOf Count
- * @param {string} [subject=''] The string to count graphemes.
- * @return {number} Returns the number of graphemes in `subject`.
- * @example
- * v.countGraphemes('cafe\u0301'); // or 'café'
- * // => 4
- *
- * v.countGraphemes('\uD835\uDC00\uD835\uDC01'); // or '𝐀𝐁'
- * // => 2
- *
- * v.countGraphemes('rain');
- * // => 4
- */
-
- function countGrapheme(subject) {
- return coerceToString(subject).replace(REGEXP_COMBINING_MARKS, '*').replace(REGEXP_SURROGATE_PAIRS, '*').length;
- }
-
- /**
- * Counts the number of `substring` appearances in `subject`.
- *
- * @function countSubstrings
- * @static
- * @since 1.0.0
- * @memberOf Count
- * @param {string} [subject=''] The string where to count.
- * @param {string} substring The substring to be counted.
- * @return {number} Returns the number of `substring` appearances.
- * @example
- * v.countSubstrings('bad boys, bad boys whatcha gonna do?', 'boys');
- * // => 2
- *
- * v.countSubstrings('every dog has its day', 'cat');
- * // => 0
- */
-
- function countSubstrings(subject, substring) {
- var subjectString = coerceToString(subject);
- var substringString = coerceToString(substring);
- var substringLength = substringString.length;
- var count = 0;
- var matchIndex = 0;
-
- if (subjectString === '' || substringString === '') {
- return count;
- }
-
- do {
- matchIndex = subjectString.indexOf(substringString, matchIndex);
-
- if (matchIndex !== -1) {
- count++;
- matchIndex += substringLength;
- }
- } while (matchIndex !== -1);
-
- return count;
- }
-
- var reduce = Array.prototype.reduce;
- /**
- * Counts the characters in `subject` for which `predicate` returns truthy.
- *
- * @function countWhere
- * @static
- * @since 1.0.0
- * @memberOf Count
- * @param {string} [subject=''] The string to count characters.
- * @param {Function} predicate The predicate function invoked on each character with parameters `(character, index, string)`.
- * @param {Object} [context] The context to invoke the `predicate`.
- * @return {number} Returns the number of characters for which `predicate` returns truthy.
- * @example
- * v.countWhere('hola!', v.isAlpha);
- * // => 4
- *
- * v.countWhere('2022', function(character, index, str) {
- * return character === '2';
- * });
- * // => 3
- */
-
- function countWhere(subject, predicate, context) {
- var subjectString = coerceToString(subject);
-
- if (subjectString === '' || typeof predicate !== 'function') {
- return 0;
- }
-
- var predicateWithContext = predicate.bind(context);
- return reduce.call(subjectString, function (countTruthy, character, index) {
- return predicateWithContext(character, index, subjectString) ? countTruthy + 1 : countTruthy;
- }, 0);
- }
-
- /**
- * Counts the number of words in `subject`.
- *
- * @function countWords
- * @static
- * @since 1.0.0
- * @memberOf Count
- * @param {string} [subject=''] The string to split into words.
- * @param {string|RegExp} [pattern] The pattern to watch words. If `pattern` is not RegExp, it is transformed to `new RegExp(pattern, flags)`.
- * @param {string} [flags=''] The regular expression flags. Applies when `pattern` is string type.
- * @return {number} Returns the number of words.
- * @example
- * v.countWords('gravity can cross dimensions');
- * // => 4
- *
- * v.countWords('GravityCanCrossDimensions');
- * // => 4
- *
- * v.countWords('Gravity - can cross dimensions!');
- * // => 4
- *
- * v.words('Earth gravity', /[^\s]+/g);
- * // => 2
- */
-
- function countWords(subject, pattern, flags) {
- return words(subject, pattern, flags).length;
- }
-
- /**
- * The current index.
- *
- * @ignore
- * @name ReplacementIndex#index
- * @type {number}
- * @return {ReplacementIndex} ReplacementIndex instance.
- */
-
- function ReplacementIndex() {
- this.index = 0;
- }
- /**
- * Increment the current index.
- *
- * @ignore
- * @return {undefined}
- */
-
-
- ReplacementIndex.prototype.increment = function () {
- this.index++;
- };
- /**
- * Increment the current index by position.
- *
- * @ignore
- * @param {number} [position] The replacement position.
- * @return {undefined}
- */
-
-
- ReplacementIndex.prototype.incrementOnEmptyPosition = function (position) {
- if (isNil(position)) {
- this.increment();
- }
- };
- /**
- * Get the replacement index by position.
- *
- * @ignore
- * @param {number} [position] The replacement position.
- * @return {number} The replacement index.
- */
-
-
- ReplacementIndex.prototype.getIndexByPosition = function (position) {
- return isNil(position) ? this.index : position - 1;
- };
-
- // Type specifiers
- var TYPE_INTEGER = 'i';
- var TYPE_INTEGER_BINARY = 'b';
- var TYPE_INTEGER_ASCII_CHARACTER = 'c';
- var TYPE_INTEGER_DECIMAL = 'd';
- var TYPE_INTEGER_OCTAL = 'o';
- var TYPE_INTEGER_UNSIGNED_DECIMAL = 'u';
- var TYPE_INTEGER_HEXADECIMAL = 'x';
- var TYPE_INTEGER_HEXADECIMAL_UPPERCASE = 'X';
- var TYPE_FLOAT_SCIENTIFIC = 'e';
- var TYPE_FLOAT_SCIENTIFIC_UPPERCASE = 'E';
- var TYPE_FLOAT = 'f';
- var TYPE_FLOAT_SHORT = 'g';
- var TYPE_FLOAT_SHORT_UPPERCASE = 'G';
- var TYPE_STRING = 's'; // Simple literals
- var LITERAL_SINGLE_QUOTE = "'";
- var LITERAL_PLUS = '+';
- var LITERAL_MINUS = '-';
- var LITERAL_PERCENT_SPECIFIER = '%%'; // Radix constants to format numbers
-
- var RADIX_BINARY = 2;
- var RADIX_OCTAL = 8;
- var RADIX_HEXADECIMAL = 16;
-
- /**
- * Repeats the `subject` number of `times`.
- *
- * @function repeat
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to repeat.
- * @param {number} [times=1] The number of times to repeat.
- * @return {string} Returns the repeated string.
- * @example
- * v.repeat('w', 3);
- * // => 'www'
- *
- * v.repeat('world', 0);
- * // => ''
- */
-
- function repeat(subject, times) {
- var subjectString = coerceToString(subject);
- var timesInt = isNil(times) ? 1 : clipNumber(toInteger(times), 0, MAX_SAFE_INTEGER);
- var repeatString = '';
-
- while (timesInt) {
- if (timesInt & 1) {
- repeatString += subjectString;
- }
-
- if (timesInt > 1) {
- subjectString += subjectString;
- }
-
- timesInt >>= 1;
- }
-
- return repeatString;
- }
-
- /**
- * Creates the padding string.
- *
- * @ignore
- * @param {string} padCharacters The characters to create padding string.
- * @param {number} length The padding string length.
- * @return {string} The padding string.
- */
-
- function buildPadding(padCharacters, length) {
- var padStringRepeat = toInteger(length / padCharacters.length);
- var padStringRest = length % padCharacters.length;
- return repeat(padCharacters, padStringRepeat + padStringRest).substr(0, length);
- }
-
- /**
- * Pads `subject` from left to a new `length`.
- *
- * @function padLeft
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to pad.
- * @param {int} [length=0] The length to left pad the string. No changes are made if `length` is less than `subject.length`.
- * @param {string} [pad=' '] The string to be used for padding.
- * @return {string} Returns the left padded string.
- * @example
- * v.padLeft('dog', 5);
- * // => ' dog'
- *
- * v.padLeft('bird', 6, '-');
- * // => '--bird'
- *
- * v.padLeft('cat', 6, '-=');
- * // => '-=-cat'
- */
-
- function padLeft(subject, length, pad) {
- var subjectString = coerceToString(subject);
- var lengthInt = isNil(length) ? 0 : clipNumber(toInteger(length), 0, MAX_SAFE_INTEGER);
- var padString = coerceToString(pad, ' ');
-
- if (lengthInt <= subjectString.length) {
- return subjectString;
- }
-
- return buildPadding(padString, lengthInt - subjectString.length) + subjectString;
- }
-
- /**
- * Pads `subject` from right to a new `length`.
- *
- * @function padRight
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to pad.
- * @param {int} [length=0] The length to right pad the string. No changes are made if `length` is less than `subject.length`.
- * @param {string} [pad=' '] The string to be used for padding.
- * @return {string} Returns the right padded string.
- * @example
- * v.padRight('dog', 5);
- * // => 'dog '
- *
- * v.padRight('bird', 6, '-');
- * // => 'bird--'
- *
- * v.padRight('cat', 6, '-=');
- * // => 'cat-=-'
- */
-
- function padRight(subject, length, pad) {
- var subjectString = coerceToString(subject);
- var lengthInt = isNil(length) ? 0 : clipNumber(toInteger(length), 0, MAX_SAFE_INTEGER);
- var padString = coerceToString(pad, ' ');
-
- if (lengthInt <= subjectString.length) {
- return subjectString;
- }
-
- return subjectString + buildPadding(padString, lengthInt - subjectString.length);
- }
-
- /**
- * Aligns and pads `subject` string.
- *
- * @ignore
- * @param {string} subject The subject string.
- * @param {ConversionSpecification} conversion The conversion specification object.
- * @return {string} Returns the aligned and padded string.
- */
-
- function alignAndPad(subject, conversion) {
- var width = conversion.width;
-
- if (isNil(width) || subject.length >= width) {
- return subject;
- }
-
- var padType = conversion.alignmentSpecifier === LITERAL_MINUS ? padRight : padLeft;
- return padType(subject, width, conversion.getPaddingCharacter());
- }
-
- /**
- * Add sign to the formatted number.
- *
- * @ignore
- * @name addSignToFormattedNumber
- * @param {number} replacementNumber The number to be replaced.
- * @param {string} formattedReplacement The formatted version of number.
- * @param {ConversionSpecification} conversion The conversion specification object.
- * @return {string} Returns the formatted number string with a sign.
- */
-
- function addSignToFormattedNumber(replacementNumber, formattedReplacement, conversion) {
- if (conversion.signSpecifier === LITERAL_PLUS && replacementNumber >= 0) {
- formattedReplacement = LITERAL_PLUS + formattedReplacement;
- }
-
- return formattedReplacement;
- }
-
- /**
- * Formats a float type according to specifiers.
- *
- * @ignore
- * @param {string} replacement The string to be formatted.
- * @param {ConversionSpecification} conversion The conversion specification object.
- * @return {string} Returns the formatted string.
- */
-
- function float(replacement, conversion) {
- var replacementNumber = parseFloat(replacement);
- var formattedReplacement;
-
- if (isNaN(replacementNumber)) {
- replacementNumber = 0;
- }
-
- var precision = coerceToNumber(conversion.precision, 6);
-
- switch (conversion.typeSpecifier) {
- case TYPE_FLOAT:
- formattedReplacement = replacementNumber.toFixed(precision);
- break;
-
- case TYPE_FLOAT_SCIENTIFIC:
- formattedReplacement = replacementNumber.toExponential(precision);
- break;
-
- case TYPE_FLOAT_SCIENTIFIC_UPPERCASE:
- formattedReplacement = replacementNumber.toExponential(precision).toUpperCase();
- break;
-
- case TYPE_FLOAT_SHORT:
- case TYPE_FLOAT_SHORT_UPPERCASE:
- formattedReplacement = formatFloatAsShort(replacementNumber, precision, conversion);
- break;
- }
-
- formattedReplacement = addSignToFormattedNumber(replacementNumber, formattedReplacement, conversion);
- return coerceToString(formattedReplacement);
- }
- /**
- * Formats the short float.
- *
- * @ignore
- * @param {number} replacementNumber The number to format.
- * @param {number} precision The precision to format the float.
- * @param {ConversionSpecification} conversion The conversion specification object.
- * @return {string} Returns the formatted short float.
- */
-
- function formatFloatAsShort(replacementNumber, precision, conversion) {
- if (replacementNumber === 0) {
- return '0';
- }
-
- var nonZeroPrecision = precision === 0 ? 1 : precision;
- var formattedReplacement = replacementNumber.toPrecision(nonZeroPrecision).replace(REGEXP_TRAILING_ZEROS, '');
-
- if (conversion.typeSpecifier === TYPE_FLOAT_SHORT_UPPERCASE) {
- formattedReplacement = formattedReplacement.toUpperCase();
- }
-
- return formattedReplacement;
- }
-
- /**
- * Formats an integer type according to specifiers.
- *
- * @ignore
- * @param {string} replacement The string to be formatted.
- * @param {ConversionSpecification} conversion The conversion specification object.
- * @return {string} Returns the formatted string.
- */
-
- function integerBase(replacement, conversion) {
- var integer = parseInt(replacement);
-
- if (isNaN(integer)) {
- integer = 0;
- }
-
- integer = integer >>> 0;
-
- switch (conversion.typeSpecifier) {
- case TYPE_INTEGER_ASCII_CHARACTER:
- integer = String.fromCharCode(integer);
- break;
-
- case TYPE_INTEGER_BINARY:
- integer = integer.toString(RADIX_BINARY);
- break;
-
- case TYPE_INTEGER_OCTAL:
- integer = integer.toString(RADIX_OCTAL);
- break;
-
- case TYPE_INTEGER_HEXADECIMAL:
- integer = integer.toString(RADIX_HEXADECIMAL);
- break;
-
- case TYPE_INTEGER_HEXADECIMAL_UPPERCASE:
- integer = integer.toString(RADIX_HEXADECIMAL).toUpperCase();
- break;
- }
-
- return coerceToString(integer);
- }
-
- /**
- * Formats a decimal integer type according to specifiers.
- *
- * @ignore
- * @param {string} replacement The string to be formatted.
- * @param {ConversionSpecification} conversion The conversion specification object.
- * @return {string} Returns the formatted string.
- */
-
- function integerDecimal(replacement, conversion) {
- var integer = parseInt(replacement);
-
- if (isNaN(integer)) {
- integer = 0;
- }
-
- return addSignToFormattedNumber(integer, toString(integer), conversion);
- }
-
- /**
- * Formats a string type according to specifiers.
- *
- * @ignore
- * @param {string} replacement The string to be formatted.
- * @param {ConversionSpecification} conversion The conversion specification object.
- * @return {string} Returns the formatted string.
- */
-
- function stringFormat(replacement, conversion) {
- var formattedReplacement = replacement;
- var precision = conversion.precision;
-
- if (!isNil(precision) && formattedReplacement.length > precision) {
- formattedReplacement = truncate(formattedReplacement, precision, '');
- }
-
- return formattedReplacement;
- }
-
- /**
- * Returns the computed string based on format specifiers.
- *
- * @ignore
- * @name computeReplacement
- * @param {string} replacement The replacement value.
- * @param {ConversionSpecification} conversion The conversion specification object.
- * @return {string} Returns the computed string.
- */
-
- function compute(replacement, conversion) {
- var formatFunction;
-
- switch (conversion.typeSpecifier) {
- case TYPE_STRING:
- formatFunction = stringFormat;
- break;
-
- case TYPE_INTEGER_DECIMAL:
- case TYPE_INTEGER:
- formatFunction = integerDecimal;
- break;
-
- case TYPE_INTEGER_ASCII_CHARACTER:
- case TYPE_INTEGER_BINARY:
- case TYPE_INTEGER_OCTAL:
- case TYPE_INTEGER_HEXADECIMAL:
- case TYPE_INTEGER_HEXADECIMAL_UPPERCASE:
- case TYPE_INTEGER_UNSIGNED_DECIMAL:
- formatFunction = integerBase;
- break;
-
- case TYPE_FLOAT:
- case TYPE_FLOAT_SCIENTIFIC:
- case TYPE_FLOAT_SCIENTIFIC_UPPERCASE:
- case TYPE_FLOAT_SHORT:
- case TYPE_FLOAT_SHORT_UPPERCASE:
- formatFunction = float;
- break;
- }
-
- var formattedString = formatFunction(replacement, conversion);
- return alignAndPad(formattedString, conversion);
- }
-
- /**
- * Construct the new conversion specification object.
- *
- * @ignore
- * @param {Object} properties An object with properties to initialize.
- * @return {ConversionSpecification} ConversionSpecification instance.
- */
-
- function ConversionSpecification(properties) {
- /**
- * The percent characters from conversion specification.
- *
- * @ignore
- * @name ConversionSpecification#percent
- * @type {string}
- */
- this.percent = properties.percent;
- /**
- * The sign specifier to force a sign to be used on a number.
- *
- * @ignore
- * @name ConversionSpecification#signSpecifier
- * @type {string}
- */
-
- this.signSpecifier = properties.signSpecifier;
- /**
- * The padding specifier that says what padding character will be used.
- *
- * @ignore
- * @name ConversionSpecification#paddingSpecifier
- * @type {string}
- */
-
- this.paddingSpecifier = properties.paddingSpecifier;
- /**
- * The alignment specifier that says if the result should be left-justified or right-justified.
- *
- * @ignore
- * @name ConversionSpecification#alignmentSpecifier
- * @type {string}
- */
-
- this.alignmentSpecifier = properties.alignmentSpecifier;
- /**
- * The width specifier how many characters this conversion should result in.
- *
- * @ignore
- * @name ConversionSpecification#width
- * @type {number}
- */
-
- this.width = properties.width;
- /**
- * The precision specifier says how many decimal digits should be displayed for floating-point numbers.
- *
- * @ignore
- * @name ConversionSpecification#precision
- * @type {number}
- */
-
- this.precision = properties.precision;
- /**
- * The type specifier says what type the argument data should be treated as.
- *
- * @ignore
- * @name ConversionSpecification#typeSpecifier
- * @type {string}
- */
-
- this.typeSpecifier = properties.typeSpecifier;
- }
- /**
- * Check if the conversion specification is a percent literal "%%".
- *
- * @ignore
- * @return {boolean} Returns true if the conversion is a percent literal, false otherwise.
- */
-
-
- ConversionSpecification.prototype.isPercentLiteral = function () {
- return LITERAL_PERCENT_SPECIFIER === this.percent;
- };
- /**
- * Get the padding character from padding specifier.
- *
- * @ignore
- * @returns {string} Returns the padding character.
- */
-
-
- ConversionSpecification.prototype.getPaddingCharacter = function () {
- var paddingCharacter = nilDefault(this.paddingSpecifier, ' ');
-
- if (paddingCharacter.length === 2 && paddingCharacter[0] === LITERAL_SINGLE_QUOTE) {
- paddingCharacter = paddingCharacter[1];
- }
-
- return paddingCharacter;
- };
-
- /**
- * Validates the specifier type and replacement position.
- *
- * @ignore
- * @throws {Error} Throws an exception on insufficient arguments or unknown specifier.
- * @param {number} index The index of the matched specifier.
- * @param {number} replacementsLength The number of replacements.
- * @param {ConversionSpecification} conversion The conversion specification object.
- * @return {undefined}
- */
-
- function validate(index, replacementsLength, conversion) {
- if (isNil(conversion.typeSpecifier)) {
- throw new Error('sprintf(): Unknown type specifier');
- }
-
- if (index > replacementsLength - 1) {
- throw new Error('sprintf(): Too few arguments');
- }
-
- if (index < 0) {
- throw new Error('sprintf(): Argument number must be greater than zero');
- }
- }
-
- /**
- * Return the replacement for regular expression match of the conversion specification.
- *
- * @ignore
- * @name matchReplacement
- * @param {ReplacementIndex} replacementIndex The replacement index object.
- * @param {string[]} replacements The array of replacements.
- * @param {string} conversionSpecification The conversion specification.
- * @param {string} percent The percent characters from conversion specification.
- * @param {string} position The position to insert the replacement.
- * @param {string} signSpecifier The sign specifier to force a sign to be used on a number.
- * @param {string} paddingSpecifier The padding specifier that says what padding character will be used.
- * @param {string} alignmentSpecifier The alignment specifier that says if the result should be left-justified or right-justified.
- * @param {string} widthSpecifier The width specifier how many characters this conversion should result in.
- * @param {string} precisionSpecifier The precision specifier says how many decimal digits should be displayed for floating-point numbers.
- * @param {string} typeSpecifier The type specifier says what type the argument data should be treated as.
- * @return {string} Returns the computed replacement.
- */
-
- function match(replacementIndex, replacements, conversionSpecification, percent, position, signSpecifier, paddingSpecifier, alignmentSpecifier, widthSpecifier, precisionSpecifier, typeSpecifier) {
- var conversion = new ConversionSpecification({
- percent: percent,
- signSpecifier: signSpecifier,
- paddingSpecifier: paddingSpecifier,
- alignmentSpecifier: alignmentSpecifier,
- width: coerceToNumber(widthSpecifier, null),
- precision: coerceToNumber(precisionSpecifier, null),
- typeSpecifier: typeSpecifier
- });
-
- if (conversion.isPercentLiteral()) {
- return conversionSpecification.slice(1);
- }
-
- var actualReplacementIndex = replacementIndex.getIndexByPosition(position);
- replacementIndex.incrementOnEmptyPosition(position);
- validate(actualReplacementIndex, replacements.length, conversion);
- return compute(replacements[actualReplacementIndex], conversion);
- }
-
- /**
- * Produces a string according to `format`.
- *
- *
- * `format` string is composed of zero or more directives: ordinary characters (not
%), which are copied unchanged
- * to the output string and
conversion specifications, each of which results in fetching zero or more subsequent
- * arguments.
- *
- * Each
conversion specification is introduced by the character
%, and ends with a
conversion
- * specifier. In between there may be (in this order) zero or more
flags, an optional
minimum field width
- * and an optional
precision.
- * The syntax is:
ConversionSpecification =
"%" {
Flags }
- * [
MinimumFieldWidth ] [
Precision ]
ConversionSpecifier, where curly braces { } denote repetition
- * and square brackets [ ] optionality.
- *
- * By default, the arguments are used in the given order.
- * For argument numbering and swapping, `%m$` (where `m` is a number indicating the argument order)
- * is used instead of `%` to specify explicitly which argument is taken. For instance `%1$s` fetches the 1st argument,
- * `%2$s` the 2nd and so on, no matter what position the conversion specification has in `format`.
- *
- *
- *
The flags
- * The character
% is followed by zero or more of the following flags:
- *
- *
- * + |
- *
- * A sign (+ or -) should always be placed before a number produced by a
- * signed conversion. By default a sign is used only for negative numbers.
- * |
- *
- *
- * 0 |
- * The value should be zero padded. |
- *
- *
- * ␣ |
- * (a space) The value should be space padded. |
- *
- *
- * ' |
- * Indicates alternate padding character, specified by prefixing it with a single quote '. |
- *
- *
- * - |
- * The converted value is to be left adjusted on the field boundary (the default is right justification). |
- *
- *
- *
- *
The minimum field width
- * An optional decimal digit string (with nonzero first digit) specifying a minimum field width. If the converted
- * value has fewer characters than the field width, it will be padded with spaces on the left (or right, if the
- * left-adjustment flag has been given).
- *
- *
The precision
- * An optional precision, in the form of a period `.` followed by an optional decimal digit string.
- * This gives the number of digits to appear after the radix character for `e`, `E`, `f` and `F` conversions, the
- * maximum number of significant digits for `g` and `G` conversions or the maximum number of characters to be printed
- * from a string for `s` conversion.
- *
- *
The conversion specifier
- * A specifier that mentions what type the argument should be treated as:
- *
- *
- *
- * | `s` |
- * The string argument is treated as and presented as a string. |
- *
- *
- * | `d` `i` |
- * The integer argument is converted to signed decimal notation. |
- *
- *
- * | `b` |
- * The unsigned integer argument is converted to unsigned binary. |
- *
- *
- * | `c` |
- * The unsigned integer argument is converted to an ASCII character with that number. |
- *
- *
- * | `o` |
- * The unsigned integer argument is converted to unsigned octal. |
- *
- *
- * | `u` |
- * The unsigned integer argument is converted to unsigned decimal. |
- *
- *
- * | `x` `X` |
- * The unsigned integer argument is converted to unsigned hexadecimal. The letters `abcdef` are used for `x`
- * conversions; the letters `ABCDEF` are used for `X` conversions. |
- *
- *
- * | `f` |
- *
- * The float argument is rounded and converted to decimal notation in the style `[-]ddd.ddd`, where the number of
- * digits after the decimal-point character is equal to the precision specification. If the precision is missing,
- * it is taken as 6; if the precision is explicitly zero, no decimal-point character appears.
- * If a decimal point appears, at least one digit appears before it.
- * |
- *
- *
- * | `e` `E` |
- *
- * The float argument is rounded and converted in the style `[-]d.ddde±dd`, where there is one digit
- * before the decimal-point character and the number of digits after it is equal to the precision. If
- * the precision is missing, it is taken as `6`; if the precision is zero, no decimal-point character
- * appears. An `E` conversion uses the letter `E` (rather than `e`) to introduce the exponent.
- * |
- *
- *
- * | `g` `G` |
- *
- * The float argument is converted in style `f` or `e` (or `F` or `E` for `G` conversions). The precision specifies
- * the number of significant digits. If the precision is missing, `6` digits are given; if the
- * precision is zero, it is treated as `1`. Style `e` is used if the exponent from its conversion is less
- * than `-6` or greater than or equal to the precision. Trailing zeros are removed from the fractional
- * part of the result; a decimal point appears only if it is followed by at least one digit.
- * |
- *
- *
- * | `%` |
- * A literal `%` is written. No argument is converted. The complete conversion specification is `%%`. |
- *
- *
- *
- *
- *
- * @function sprintf
- * @static
- * @since 1.0.0
- * @memberOf Format
- * @param {string} [format=''] The format string.
- * @param {...*} replacements The replacements to produce the string.
- * @return {string} Returns the produced string.
- * @example
- * v.sprintf('%s, %s!', 'Hello', 'World');
- * // => 'Hello World!'
- *
- * v.sprintf('%s costs $%d', 'coffee', 2);
- * // => 'coffee costs $2'
- *
- * v.sprintf('%1$s %2$s %1$s %2$s, watcha gonna %3$s', 'bad', 'boys', 'do')
- * // => 'bad boys bad boys, watcha gonna do'
- *
- * v.sprintf('% 6s', 'bird');
- * // => ' bird'
- *
- * v.sprintf('% -6s', 'crab');
- * // => 'crab '
- *
- * v.sprintf("%'*5s", 'cat');
- * // => '**cat'
- *
- * v.sprintf("%'*-6s", 'duck');
- * // => 'duck**'
- *
- * v.sprintf('%d %i %+d', 15, -2, 25);
- * // => '15 -2 +25'
- *
- * v.sprintf("%06d", 15);
- * // => '000015'
- *
- * v.sprintf('0b%b 0o%o 0x%X', 12, 9, 155);
- * // => '0b1100 0o11 0x9B'
- *
- * v.sprintf('%.2f', 10.469);
- * // => '10.47'
- *
- * v.sprintf('%.2e %g', 100.5, 0.455);
- * // => '1.01e+2 0.455'
- *
- */
-
- function sprintf(format) {
- var formatString = coerceToString(format);
-
- if (formatString === '') {
- return formatString;
- }
-
- for (var _len = arguments.length, replacements = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
- replacements[_key - 1] = arguments[_key];
- }
-
- var boundReplacementMatch = match.bind(undefined, new ReplacementIndex(), replacements);
- return formatString.replace(REGEXP_CONVERSION_SPECIFICATION, boundReplacementMatch);
- }
-
- /**
- * Produces a string according to `format`. Works exactly like sprintf(),
- * with the only difference that accepts the formatting arguments in an array `values`.
- * See here `format` string specifications.
- *
- * @function vprintf
- * @static
- * @since 1.0.0
- * @memberOf Format
- * @param {string} format=''] The format string.
- * @param {Array} replacements The array of replacements to produce the string.
- * @return {string} Returns the produced string.
- * @example
- * v.vprintf('%s', ['Welcome'])
- * // => 'Welcome'
- *
- * v.vprintf('%s has %d apples', ['Alexandra', 3]);
- * // => 'Alexandra has 3 apples'
- */
-
- function vprintf(format, replacements) {
- return sprintf.apply(void 0, [format].concat(_toConsumableArray(nilDefault(replacements, []))));
- }
-
- var escapeCharactersMap = {
- '<': '<',
- '>': '>',
- '&': '&',
- '"': '"',
- "'": ''',
- '`': '`'
- };
- /**
- * Return the escaped version of `character`.
- *
- * @ignore
- * @param {string} character The character to be escape.
- * @return {string} The escaped version of character.
- */
-
- function replaceSpecialCharacter(character) {
- return escapeCharactersMap[character];
- }
- /**
- * Escapes HTML special characters < > & ' " ` in subject.
- *
- * @function escapeHtml
- * @static
- * @since 1.0.0
- * @memberOf Escape
- * @param {string} [subject=''] The string to escape.
- * @return {string} Returns the escaped string.
- * @example
- * v.escapeHtml('wonderful world
');
- * // => '<p>wonderful world</p>'
- */
-
-
- function escapeHtml(subject) {
- return coerceToString(subject).replace(REGEXP_HTML_SPECIAL_CHARACTERS, replaceSpecialCharacter);
- }
-
- /**
- * Escapes the regular expression special characters `- [ ] / { } ( ) * + ? . \ ^ $ |` in `subject`.
- *
- * @function escapeRegExp
- * @static
- * @since 1.0.0
- * @memberOf Escape
- * @param {string} [subject=''] The string to escape.
- * @return {string} Returns the escaped string.
- * @example
- * v.escapeRegExp('(hours)[minutes]{seconds}');
- * // => '\(hours\)\[minutes\]\{seconds\}'
- */
-
- function escapeRegExp(subject) {
- return coerceToString(subject).replace(REGEXP_SPECIAL_CHARACTERS, '\\$&');
- }
-
- var unescapeCharactersMap = {
- '<': /(<)|(*3c;)|(*60;)/gi,
- '>': /(>)|(*3e;)|(*62;)/gi,
- '&': /(&)|(*26;)|(*38;)/gi,
- '"': /(")|(*22;)|(*34;)/gi,
- "'": /(*27;)|(*39;)/gi,
- '`': /(*60;)|(*96;)/gi
- };
- var characters = Object.keys(unescapeCharactersMap);
- /**
- * Replaces the HTML entities with corresponding characters.
- *
- * @ignore
- * @param {string} string The accumulator string.
- * @param {string} key The character.
- * @return {string} The string with replaced HTML entity
- */
-
- function reduceUnescapedString(string, key) {
- return string.replace(unescapeCharactersMap[key], key);
- }
- /**
- * Unescapes HTML special characters from < > & " ' `
- * to corresponding < > & ' " ` in subject.
- *
- * @function unescapeHtml
- * @static
- * @since 1.0.0
- * @memberOf Escape
- * @param {string} [subject=''] The string to unescape.
- * @return {string} Returns the unescaped string.
- * @example
- * v.unescapeHtml('<p>wonderful world</p>');
- * // => 'wonderful world
'
- */
-
-
- function unescapeHtml(subject) {
- var subjectString = coerceToString(subject);
- return characters.reduce(reduceUnescapedString, subjectString);
- }
-
- /**
- * Returns the first occurrence index of `search` in `subject`.
- *
- * @function indexOf
- * @static
- * @since 1.0.0
- * @memberOf Index
- * @param {string} [subject=''] The string where to search.
- * @param {string} search The string to search.
- * @param {number} [fromIndex=0] The index to start searching.
- * @return {number} Returns the first occurrence index or `-1` if not found.
- * @example
- * v.indexOf('morning', 'n');
- * // => 3
- *
- * v.indexOf('evening', 'o');
- * // => -1
- */
-
- function indexOf(subject, search, fromIndex) {
- var subjectString = coerceToString(subject);
- return subjectString.indexOf(search, fromIndex);
- }
-
- /**
- * Returns the last occurrence index of `search` in `subject`.
- *
- * @function lastIndexOf
- * @static
- * @since 1.0.0
- * @memberOf Index
- * @param {string} [subject=''] The string where to search.
- * @param {string} search The string to search.
- * @param {number} [fromIndex=subject.length - 1] The index to start searching backward in the string.
- * @return {number} Returns the last occurrence index or `-1` if not found.
- * @example
- * v.lastIndexOf('morning', 'n');
- * // => 5
- *
- * v.lastIndexOf('evening', 'o');
- * // => -1
- */
-
- function lastIndexOf(subject, search, fromIndex) {
- var subjectString = coerceToString(subject);
- return subjectString.lastIndexOf(search, fromIndex);
- }
-
- /**
- * Returns the first index of a `pattern` match in `subject`.
- *
- * @function search
- * @static
- * @since 1.0.0
- * @memberOf Index
- * @param {string} [subject=''] The string where to search.
- * @param {string|RegExp} pattern The pattern to match. If `pattern` is not RegExp, it is transformed to `new RegExp(pattern)`.
- * @param {number} [fromIndex=0] The index to start searching.
- * @return {number} Returns the first match index or `-1` if not found.
- * @example
- * v.search('morning', /rn/);
- * // => 2
- *
- * v.search('evening', '/\d/');
- * // => -1
- */
-
- function search(subject, pattern, fromIndex) {
- var subjectString = coerceToString(subject);
- var fromIndexNumber = isNil(fromIndex) ? 0 : clipNumber(toInteger(fromIndex), 0, subjectString.length);
- var matchIndex = subjectString.substr(fromIndexNumber).search(pattern);
-
- if (matchIndex !== -1 && !isNaN(fromIndexNumber)) {
- matchIndex += fromIndexNumber;
- }
-
- return matchIndex;
- }
-
- /**
- * Inserts into `subject` a string `toInsert` at specified `position`.
- *
- * @function insert
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string where to insert.
- * @param {string} [toInsert=''] The string to be inserted.
- * @param {number} [position=0] The position to insert.
- * @return {string} Returns the string after insertion.
- * @example
- * v.insert('ct', 'a', 1);
- * // => 'cat'
- *
- * v.insert('sunny', ' day', 5);
- * // => 'sunny day'
- */
-
- function insert(subject, toInsert, position) {
- var subjectString = coerceToString(subject);
- var toInsertString = coerceToString(toInsert);
- var positionNumber = coerceToNumber(position);
-
- if (positionNumber < 0 || positionNumber > subjectString.length || toInsertString === '') {
- return subjectString;
- }
-
- return subjectString.slice(0, positionNumber) + toInsertString + subjectString.slice(positionNumber);
- }
-
- /**
- * Generated diacritics map. See bellow the base code.
- * @ignore
- * @type Object
- */
- var diacritics = {
- '3': '\u039e\u03be',
- '8': '\u0398\u03b8',
- A: '\x41\xc0\xc1\xc2\xc3\xc4\xc5\u0100\u0102\u0104\u01cd\u01de\u01e0\u01fa\u0200\u0202\u0226\u023a\u0386\u0391\u0410',
- B: '\x42\u0181\u0182\u0243\u0392\u0411',
- C: '\x43\xc7\u0106\u0108\u010a\u010c\u0187\u023b\u0426',
- D: '\x44\u010e\u0110\u0189\u018a\u018b\xd0\u0394\u0414',
- E: '\x45\xc8\xc9\xca\xcb\u0112\u0114\u0116\u0118\u011a\u018e\u0190\u0204\u0206\u0228\u0388\u0395\u0415\u042d',
- F: '\x46\u0191\u03a6\u0424',
- G: '\x47\u011c\u011e\u0120\u0122\u0193\u01e4\u01e6\u01f4\u0393\u0413\u0490',
- H: '\x48\u0124\u0126\u021e\u0389\u0397\u0425',
- I: '\x49\xcc\xcd\xce\xcf\u0128\u012a\u012c\u012e\u0130\u0197\u01cf\u0208\u020a\u038a\u0399\u03aa\u0406\u0418',
- J: '\x4a\u0134\u0248\u0419',
- K: '\x4b\u0136\u0198\u01e8\u039a\u041a',
- L: '\x4c\u0139\u013b\u013d\u013f\u0141\u023d\u039b\u041b',
- M: '\x4d\u019c\u039c\u041c',
- N: '\x4e\xd1\u0143\u0145\u0147\u019d\u01f8\u0220\u039d\u041d',
- O: '\x4f\xd2\xd3\xd4\xd5\xd6\xd8\u014c\u014e\u0150\u0186\u019f\u01a0\u01d1\u01ea\u01ec\u01fe\u020c\u020e\u022a\u022c\u022e\u0230\u038c\u039f\u041e',
- P: '\x50\u01a4\u03a0\u041f',
- Q: '\x51\u024a',
- R: '\x52\u0154\u0156\u0158\u0210\u0212\u024c\u03a1\u0420',
- S: '\x53\u015a\u015c\u015e\u0160\u0218\u03a3\u0421',
- T: '\x54\u0162\u0164\u0166\u01ac\u01ae\u021a\u023e\u03a4\u0422',
- U: '\x55\xd9\xda\xdb\xdc\u0168\u016a\u016c\u016e\u0170\u0172\u01af\u01d3\u01d5\u01d7\u01d9\u01db\u0214\u0216\u0244\u0423\u042a',
- V: '\x56\u01b2\u0245\u0412',
- W: '\x57\u0174\u038f\u03a9',
- X: '\x58\u03a7',
- Y: '\x59\xdd\u0176\u0178\u01b3\u0232\u024e\u038e\u03a5\u03ab\u042b',
- Z: '\x5a\u0179\u017b\u017d\u01b5\u0224\u0396\u0417',
- a: '\x61\xe0\xe1\xe2\xe3\xe4\xe5\u0101\u0103\u0105\u01ce\u01df\u01e1\u01fb\u0201\u0203\u0227\u0250\u03ac\u03b1\u0430',
- b: '\x62\u0180\u0183\u0253\u03b2\u0431',
- c: '\x63\xe7\u0107\u0109\u010b\u010d\u0188\u023c\u0446',
- d: '\x64\u010f\u0111\u018c\u0256\u0257\xf0\u03b4\u0434',
- e: '\x65\xe8\xe9\xea\xeb\u0113\u0115\u0117\u0119\u011b\u01dd\u0205\u0207\u0229\u0247\u025b\u03ad\u03b5\u0435\u044d',
- f: '\x66\u0192\u03c6\u0444',
- g: '\x67\u011d\u011f\u0121\u0123\u01e5\u01e7\u01f5\u0260\u03b3\u0433\u0491',
- h: '\x68\u0125\u0127\u021f\u0265\u03ae\u03b7\u0445',
- i: '\x69\xec\xed\xee\xef\u0129\u012b\u012d\u012f\u0131\u01d0\u0209\u020b\u0268\u0390\u03af\u03b9\u03ca\u0438\u0456',
- j: '\x6a\u0135\u01f0\u0249\u0439',
- k: '\x6b\u0137\u0199\u01e9\u03ba\u043a',
- l: '\x6c\u013a\u013c\u013e\u0140\u0142\u017f\u019a\u026b\u03bb\u043b',
- m: '\x6d\u026f\u0271\u03bc\u043c',
- n: '\x6e\xf1\u0144\u0146\u0148\u0149\u019e\u01f9\u0272\u03bd\u043d',
- o: '\x6f\xf2\xf3\xf4\xf5\xf6\xf8\u014d\u014f\u0151\u01a1\u01d2\u01eb\u01ed\u01ff\u020d\u020f\u022b\u022d\u022f\u0231\u0254\u0275\u03bf\u03cc\u043e',
- p: '\x70\u01a5\u03c0\u043f',
- q: '\x71\u024b',
- r: '\x72\u0155\u0157\u0159\u0211\u0213\u024d\u027d\u03c1\u0440',
- s: '\x73\xdf\u015b\u015d\u015f\u0161\u0219\u023f\u03c2\u03c3\u0441',
- t: '\x74\u0163\u0165\u0167\u01ad\u021b\u0288\u03c4\u0442',
- u: '\x75\xf9\xfa\xfb\xfc\u0169\u016b\u016d\u016f\u0171\u0173\u01b0\u01d4\u01d6\u01d8\u01da\u01dc\u0215\u0217\u0289\u0443\u044a',
- v: '\x76\u028b\u028c\u0432',
- w: '\x77\u0175\u03c9\u03ce',
- x: '\x78\u03c7',
- y: '\x79\xfd\xff\u0177\u01b4\u0233\u024f\u03b0\u03c5\u03cb\u03cd\u044b',
- z: '\x7a\u017a\u017c\u017e\u01b6\u0225\u0240\u03b6\u0437',
- OE: '\x8c\u0152',
- oe: '\x9c\u0153',
- AE: '\xc6\u01e2\u01fc',
- ae: '\xe6\u01e3\u01fd',
- hv: '\u0195',
- OI: '\u01a2',
- oi: '\u01a3',
- DZ: '\u01c4\u01f1',
- Dz: '\u01c5\u01f2',
- dz: '\u01c6\u01f3',
- LJ: '\u01c7',
- Lj: '\u01c8',
- lj: '\u01c9',
- NJ: '\u01ca',
- Nj: '\u01cb',
- nj: '\u01cc',
- OU: '\u0222',
- ou: '\u0223',
- TH: '\xde',
- th: '\xfe',
- PS: '\u03a8',
- ps: '\u03c8',
- Yo: '\u0401',
- Ye: '\u0404',
- Yi: '\u0407',
- Zh: '\u0416',
- Ch: '\u0427',
- Sh: '\u0428\u0429',
- '': '\u042a\u042c\u044c',
- Yu: '\u042e',
- Ya: '\u042f',
- zh: '\u0436',
- ch: '\u0447',
- sh: '\u0448\u0449',
- yu: '\u044e',
- ya: '\u044f',
- yo: '\u0451',
- ye: '\u0454',
- yi: '\u0457'
- };
- var diacriticsMap = null;
- /**
- * Creates a map of the diacritics.
- *
- * @ignore
- * @returns {Object} Returns the diacritics map.
- */
-
- function getDiacriticsMap() {
- if (diacriticsMap !== null) {
- return diacriticsMap;
- }
-
- diacriticsMap = {};
- Object.keys(diacritics).forEach(function (key) {
- var characters = diacritics[key];
-
- for (var index = 0; index < characters.length; index++) {
- var character = characters[index];
- diacriticsMap[character] = key;
- }
- });
- return diacriticsMap;
- }
- /**
- * Get the latin character from character with diacritics.
- *
- * @ignore
- * @param {string} character The character with diacritics.
- * @returns {string} Returns the character without diacritics.
- */
-
-
- function getLatinCharacter(character) {
- var characterWithoutDiacritic = getDiacriticsMap()[character];
- return characterWithoutDiacritic ? characterWithoutDiacritic : character;
- }
-
- /**
- * Returns the `cleanCharacter` from combining marks regular expression match.
- *
- * @ignore
- * @param {string} character The character with combining marks
- * @param {string} cleanCharacter The character without combining marks.
- * @return {string} The character without combining marks.
- */
-
- function removeCombiningMarks(character, cleanCharacter) {
- return cleanCharacter;
- }
- /**
- * Latinises the `subject` by removing diacritic characters.
- *
- * @function latinise
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to latinise.
- * @return {string} Returns the latinised string.
- * @example
- * v.latinise('cafe\u0301'); // or 'café'
- * // => 'cafe'
- *
- * v.latinise('août décembre');
- * // => 'aout decembre'
- *
- * v.latinise('как прекрасен этот мир');
- * // => 'kak prekrasen etot mir'
- */
-
-
- function latinise(subject) {
- var subjectString = coerceToString(subject);
-
- if (subjectString === '') {
- return '';
- }
-
- return subjectString.replace(REGEXP_NON_LATIN, getLatinCharacter).replace(REGEXP_COMBINING_MARKS, removeCombiningMarks);
- }
-
- /**
- * Pads `subject` to a new `length`.
- *
- * @function pad
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to pad.
- * @param {int} [length=0] The length to pad the string. No changes are made if `length` is less than `subject.length`.
- * @param {string} [pad=' '] The string to be used for padding.
- * @return {string} Returns the padded string.
- * @example
- * v.pad('dog', 5);
- * // => ' dog '
- *
- * v.pad('bird', 6, '-');
- * // => '-bird-'
- *
- * v.pad('cat', 6, '-=');
- * // => '-cat-='
- */
-
- function pad(subject, length, pad) {
- var subjectString = coerceToString(subject);
- var lengthInt = isNil(length) ? 0 : clipNumber(toInteger(length), 0, MAX_SAFE_INTEGER);
- var padString = coerceToString(pad, ' ');
-
- if (lengthInt <= subjectString.length) {
- return subjectString;
- }
-
- var paddingLength = lengthInt - subjectString.length;
- var paddingSideLength = toInteger(paddingLength / 2);
- var paddingSideRemainingLength = paddingLength % 2;
- return buildPadding(padString, paddingSideLength) + subjectString + buildPadding(padString, paddingSideLength + paddingSideRemainingLength);
- }
-
- /**
- * Replaces the matches of `search` with `replace`.
- *
- * @function replace
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to verify.
- * @param {string|RegExp} search The search pattern to replace. If `search` is a string,
- * a simple string match is evaluated and only the first occurrence replaced.
- * @param {string|Function} replace The string or function which invocation result replaces `search` match.
- * @return {string} Returns the replacement result.
- * @example
- * v.replace('swan', 'wa', 'u');
- * // => 'sun'
- *
- * v.replace('domestic duck', /domestic\s/, '');
- * // => 'duck'
- *
- * v.replace('nice duck', /(nice)(duck)/, function(match, nice, duck) {
- * return 'the ' + duck + ' is ' + nice;
- * });
- * // => 'the duck is nice'
- */
-
- function replace(subject, search, replace) {
- var subjectString = coerceToString(subject);
- return subjectString.replace(search, replace);
- }
-
- /**
- * Replaces all occurrences of `search` with `replace`.
- *
- * @function replaceAll
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to verify.
- * @param {string|RegExp} search The search pattern to replace. If `search` is a string, a simple string match is evaluated.
- * All matches are replaced.
- * @param {string|Function} replace The string or function which invocation result replaces all `search` matches.
- * @return {string} Returns the replacement result.
- * @example
- * v.replaceAll('good morning', 'o', '*');
- * // => 'g**d m*rning'
- * v.replaceAll('evening', /n/g, 's');
- * // => 'evesisg'
- *
- */
-
- function replaceAll(subject, search, replace) {
- var subjectString = coerceToString(subject);
-
- if (search instanceof RegExp) {
- if (search.flags.indexOf('g') === -1) {
- throw new TypeError('search argument is a non-global regular expression');
- }
-
- return subjectString.replace(search, replace);
- }
-
- var searchString = coerceToString(search);
- var isFunctionalReplace = typeof replace === 'function';
-
- if (!isFunctionalReplace) {
- replace = coerceToString(replace);
- }
-
- var searchLength = searchString.length;
-
- if (searchLength === 0) {
- return replaceAll(subject, /(?:)/g, replace);
- }
-
- var advanceBy = searchLength > 1 ? searchLength : 1;
- var matchPositions = [];
- var position = subjectString.indexOf(searchString, 0);
-
- while (position !== -1) {
- matchPositions.push(position);
- position = subjectString.indexOf(searchString, position + advanceBy);
- }
-
- var endOfLastMatch = 0;
- var result = '';
-
- for (var i = 0; i < matchPositions.length; i++) {
- var _position = matchPositions[i];
- var replacement = replace;
-
- if (isFunctionalReplace) {
- replacement = coerceToString(replace.call(undefined, searchString, _position, subjectString));
- }
-
- result += subjectString.slice(endOfLastMatch, _position) + replacement;
- endOfLastMatch = _position + searchLength;
- }
-
- if (endOfLastMatch < subjectString.length) {
- result += subjectString.slice(endOfLastMatch);
- }
-
- return result;
- }
-
- /**
- * Reverses the `subject`.
- *
- * @function reverse
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to reverse.
- * @return {string} Returns the reversed string.
- * @example
- * v.reverse('winter');
- * // => 'retniw'
- */
-
- function reverse(subject) {
- var subjectString = coerceToString(subject);
- return subjectString.split('').reverse().join('');
- }
-
- /**
- * Reverses the `subject` taking care of
- * surrogate pairs and
- * combining marks.
- *
- * @function reverseGrapheme
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to reverse.
- * @return {string} Returns the reversed string.
- * @example
- * v.reverseGrapheme('summer');
- * // => 'remmus'
- *
- * v.reverseGrapheme('𝌆 bar mañana mañana');
- * // => 'anañam anañam rab 𝌆'
- */
-
- function reverseGrapheme(subject) {
- var subjectString = coerceToString(subject);
- /**
- * @see https://github.com/mathiasbynens/esrever
- */
-
- subjectString = subjectString.replace(REGEXP_COMBINING_MARKS, function ($0, $1, $2) {
- return reverseGrapheme($2) + $1;
- }).replace(REGEXP_SURROGATE_PAIRS, '$2$1');
- var reversedString = '';
- var index = subjectString.length;
-
- while (index--) {
- reversedString += subjectString.charAt(index);
- }
-
- return reversedString;
- }
-
- /**
- * Slugifies the `subject`. Cleans the `subject` by replacing diacritics with corresponding latin characters.
- *
- * @function slugify
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to slugify.
- * @return {string} Returns the slugified string.
- * @example
- * v.slugify('Italian cappuccino drink');
- * // => 'italian-cappuccino-drink'
- *
- * v.slugify('caffé latté');
- * // => 'caffe-latte'
- *
- * v.slugify('хорошая погода');
- * // => 'horoshaya-pogoda'
- */
-
- function slugify(subject) {
- var subjectString = coerceToString(subject);
-
- if (subjectString === '') {
- return '';
- }
-
- var cleanSubjectString = latinise(subjectString).replace(REGEXP_NON_LATIN, '-');
- return kebabCase(cleanSubjectString);
- }
-
- /**
- * Changes `subject` by deleting `deleteCount` of characters starting at position `start`. Places a new string
- * `toAdd` instead of deleted characters.
- *
- * @function splice
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string where to insert.
- * @param {string} start The position to start changing the string. For a negative position will start from the end of
- * the string.
- * @param {number} [deleteCount=subject.length-start] The number of characters to delete from string.
- * @param {string} [toAdd=''] The string to be added instead of deleted characters.
- * @return {string} Returns the modified string.
- * @example
- * v.splice('new year', 0, 4);
- * // => 'year'
- *
- * v.splice('new year', 0, 3, 'happy');
- * // => 'happy year'
- *
- * v.splice('new year', -4, 4, 'day');
- * // => 'new day'
- */
-
- function splice(subject, start, deleteCount, toAdd) {
- var subjectString = coerceToString(subject);
- var toAddString = coerceToString(toAdd);
- var startPosition = coerceToNumber(start);
-
- if (startPosition < 0) {
- startPosition = subjectString.length + startPosition;
-
- if (startPosition < 0) {
- startPosition = 0;
- }
- } else if (startPosition > subjectString.length) {
- startPosition = subjectString.length;
- }
-
- var deleteCountNumber = coerceToNumber(deleteCount, subjectString.length - startPosition);
-
- if (deleteCountNumber < 0) {
- deleteCountNumber = 0;
- }
-
- return subjectString.slice(0, startPosition) + toAddString + subjectString.slice(startPosition + deleteCountNumber);
- }
-
- /**
- * Translates characters or replaces substrings in `subject`.
- *
- * @function tr
- * @static
- * @since 1.3.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to translate.
- * @param {string|Object} from The string of characters to translate from. Or an object, then the object keys are replaced with corresponding values (longest keys are tried first).
- * @param {string} to The string of characters to translate to. Ignored when `from` is an object.
- * @return {string} Returns the translated string.
- * @example
- * v.tr('hello', 'el', 'ip');
- * // => 'hippo'
- *
- * v.tr('légèreté', 'éè', 'ee');
- * // => 'legerete'
- *
- * v.tr('Yes. The fire rises.', {
- * 'Yes': 'Awesome',
- * 'fire': 'flame'
- * })
- * // => 'Awesome. The flame rises.'
- *
- * v.tr(':where is the birthplace of :what', {
- * ':where': 'Africa',
- * ':what': 'Humanity'
- * });
- * // => 'Africa is the birthplace of Humanity'
- *
- */
-
- function tr(subject, from, to) {
- var subjectString = coerceToString(subject);
- var keys;
- var values;
-
- if (isString(from) && isString(to)) {
- keys = from.split('');
- values = to.split('');
- } else {
- var _extractKeysAndValues = extractKeysAndValues(nilDefault(from, {}));
-
- var _extractKeysAndValues2 = _slicedToArray(_extractKeysAndValues, 2);
-
- keys = _extractKeysAndValues2[0];
- values = _extractKeysAndValues2[1];
- }
-
- var keysLength = keys.length;
-
- if (keysLength === 0) {
- return subjectString;
- }
-
- var result = '';
- var valuesLength = values.length;
-
- for (var index = 0; index < subjectString.length; index++) {
- var isMatch = false;
- var matchValue = void 0;
-
- for (var keyIndex = 0; keyIndex < keysLength && keyIndex < valuesLength; keyIndex++) {
- var key = keys[keyIndex];
-
- if (subjectString.substr(index, key.length) === key) {
- isMatch = true;
- matchValue = values[keyIndex];
- index = index + key.length - 1;
- break;
- }
- }
-
- result += isMatch ? matchValue : subjectString[index];
- }
-
- return result;
- }
-
- function extractKeysAndValues(object) {
- var keys = Object.keys(object);
- var values = keys.sort(sortStringByLength).map(function (key) {
- return object[key];
- });
- return [keys, values];
- }
-
- function sortStringByLength(str1, str2) {
- if (str1.length === str2.length) {
- return 0;
- }
-
- return str1.length < str2.length ? 1 : -1;
- }
-
- /**
- * Checks whether `subject` includes `search` starting from `position`.
- *
- * @function includes
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string where to search.
- * @param {string} search The string to search.
- * @param {number} [position=0] The position to start searching.
- * @return {boolean} Returns `true` if `subject` includes `search` or `false` otherwise.
- * @example
- * v.includes('starship', 'star');
- * // => true
- *
- * v.includes('galaxy', 'g', 1);
- * // => false
- */
-
- function includes(subject, search, position) {
- var subjectString = coerceToString(subject);
- var searchString = toString(search);
-
- if (searchString === null) {
- return false;
- }
-
- if (searchString === '') {
- return true;
- }
-
- position = isNil(position) ? 0 : clipNumber(toInteger(position), 0, subjectString.length);
- return subjectString.indexOf(searchString, position) !== -1;
- }
-
- var reduce$1 = Array.prototype.reduce;
- /**
- * Removes whitespaces from the left side of the `subject`.
- *
- * @function trimLeft
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to trim.
- * @param {string} [whitespace=whitespace] The whitespace characters to trim. List all characters that you want to be stripped.
- * @return {string} Returns the trimmed string.
- * @example
- * v.trimLeft(' Starship Troopers');
- * // => 'Starship Troopers'
- *
- * v.trimLeft('***Mobile Infantry', '*');
- * // => 'Mobile Infantry'
- */
-
- function trimLeft(subject, whitespace) {
- var subjectString = coerceToString(subject);
-
- if (whitespace === '' || subjectString === '') {
- return subjectString;
- }
-
- var whitespaceString = toString(whitespace);
-
- if (isNil(whitespaceString)) {
- return subjectString.replace(REGEXP_TRIM_LEFT, '');
- }
-
- var matchWhitespace = true;
- return reduce$1.call(subjectString, function (trimmed, character) {
- if (matchWhitespace && includes(whitespaceString, character)) {
- return trimmed;
- }
-
- matchWhitespace = false;
- return trimmed + character;
- }, '');
- }
-
- var reduceRight = Array.prototype.reduceRight;
- /**
- * Removes whitespaces from the right side of the `subject`.
- *
- * @function trimRight
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to trim.
- * @param {string} [whitespace=whitespace] The whitespace characters to trim. List all characters that you want to be stripped.
- * @return {string} Returns the trimmed string.
- * @example
- * v.trimRight('the fire rises ');
- * // => 'the fire rises'
- *
- * v.trimRight('do you feel in charge?!!!', '!');
- * // => 'do you feel in charge?'
- */
-
- function trimRight(subject, whitespace) {
- var subjectString = coerceToString(subject);
-
- if (whitespace === '' || subjectString === '') {
- return subjectString;
- }
-
- var whitespaceString = toString(whitespace);
-
- if (isNil(whitespaceString)) {
- return subjectString.replace(REGEXP_TRIM_RIGHT, '');
- }
-
- var matchWhitespace = true;
- return reduceRight.call(subjectString, function (trimmed, character) {
- if (matchWhitespace && includes(whitespaceString, character)) {
- return trimmed;
- }
-
- matchWhitespace = false;
- return character + trimmed;
- }, '');
- }
-
- /**
- * Removes whitespaces from left and right sides of the `subject`.
- *
- * @function trim
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to trim.
- * @param {string} [whitespace=whitespace] The whitespace characters to trim. List all characters that you want to be stripped.
- * @return {string} Returns the trimmed string.
- * @example
- * v.trim(' Mother nature ');
- * // => 'Mother nature'
- *
- * v.trim('--Earth--', '-');
- * // => 'Earth'
- */
-
- function trim(subject, whitespace) {
- var subjectString = coerceToString(subject);
-
- if (whitespace === '' || subjectString === '') {
- return subjectString;
- }
-
- var whitespaceString = toString(whitespace);
-
- if (isNil(whitespaceString)) {
- return subjectString.trim();
- }
-
- return trimRight(trimLeft(subjectString, whitespaceString), whitespaceString);
- }
-
- var OPTION_WIDTH = 'width';
- var OPTION_NEW_LINE = 'newLine';
- var OPTION_INDENT = 'indent';
- var OPTION_CUT = 'cut';
- /**
- * Wraps `subject` to a given number of characters using a string break character.
- *
- * @function wordWrap
- * @static
- * @since 1.0.0
- * @memberOf Manipulate
- * @param {string} [subject=''] The string to wrap.
- * @param {Object} [options={}] The wrap options.
- * @param {number} [options.width=75] The number of characters at which to wrap.
- * @param {string} [options.newLine='\n'] The string to add at the end of line.
- * @param {string} [options.indent=''] The string to intend the line.
- * @param {boolean} [options.cut=false] When `false` (default) does not split the word even if word length is bigger than `width`.
- * When `true` breaks the word that has length bigger than `width`.
- *
- * @return {string} Returns wrapped string.
- * @example
- * v.wordWrap('Hello world', {
- * width: 5
- * });
- * // => 'Hello\nworld'
- *
- * v.wordWrap('Hello world', {
- * width: 5,
- * newLine: '
',
- * indent: '__'
- * });
- * // => '__Hello
__world'
- *
- * v.wordWrap('Wonderful world', {
- * width: 5,
- * cut: true
- * });
- * // => 'Wonde\nrful\nworld'
- *
- */
-
- function wordWrap(subject) {
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
- var subjectString = coerceToString(subject);
-
- var _determineOptions = determineOptions(options),
- width = _determineOptions.width,
- newLine = _determineOptions.newLine,
- indent = _determineOptions.indent,
- cut = _determineOptions.cut;
-
- if (subjectString === '' || width <= 0) {
- return indent;
- }
-
- var subjectLength = subjectString.length;
- var substring = subjectString.substring.bind(subjectString);
- var offset = 0;
- var wrappedLine = '';
-
- while (subjectLength - offset > width) {
- if (subjectString[offset] === ' ') {
- offset++;
- continue;
- }
-
- var spaceToWrapAt = subjectString.lastIndexOf(' ', width + offset);
-
- if (spaceToWrapAt >= offset) {
- wrappedLine += indent + substring(offset, spaceToWrapAt) + newLine;
- offset = spaceToWrapAt + 1;
- } else {
- if (cut) {
- wrappedLine += indent + substring(offset, width + offset) + newLine;
- offset += width;
- } else {
- spaceToWrapAt = subjectString.indexOf(' ', width + offset);
-
- if (spaceToWrapAt >= 0) {
- wrappedLine += indent + substring(offset, spaceToWrapAt) + newLine;
- offset = spaceToWrapAt + 1;
- } else {
- wrappedLine += indent + substring(offset);
- offset = subjectLength;
- }
- }
- }
- }
-
- if (offset < subjectLength) {
- wrappedLine += indent + substring(offset);
- }
-
- return wrappedLine;
- }
- /**
- * Determine the word wrap options. The missing values are filled with defaults.
- *
- * @param {Object} options The options object.
- * @return {Object} The word wrap options, with default settings if necessary.
- * @ignore
- */
-
- function determineOptions(options) {
- return {
- width: coerceToNumber(options[OPTION_WIDTH], 75),
- newLine: coerceToString(options[OPTION_NEW_LINE], '\n'),
- indent: coerceToString(options[OPTION_INDENT], ''),
- cut: coerceToBoolean(options[OPTION_CUT], false)
- };
- }
-
- /**
- * Checks whether `subject` ends with `end`.
- *
- * @function endsWith
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @param {string} end The ending string.
- * @param {number} [position=subject.length] Search within `subject` as if the string were only `position` long.
- * @return {boolean} Returns `true` if `subject` ends with `end` or `false` otherwise.
- * @example
- * v.endsWith('red alert', 'alert');
- * // => true
- *
- * v.endsWith('metro south', 'metro');
- * // => false
- *
- * v.endsWith('Murphy', 'ph', 5);
- * // => true
- */
-
- function endsWith(subject, end, position) {
- if (isNil(end)) {
- return false;
- }
-
- var subjectString = coerceToString(subject);
- var endString = coerceToString(end);
-
- if (endString === '') {
- return true;
- }
-
- position = isNil(position) ? subjectString.length : clipNumber(toInteger(position), 0, subjectString.length);
- position -= endString.length;
- var lastIndex = subjectString.indexOf(endString, position);
- return lastIndex !== -1 && lastIndex === position;
- }
-
- /**
- * Checks whether `subject` contains only alpha characters.
- *
- * @function isAlpha
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @return {boolean} Returns `true` if `subject` contains only alpha characters or `false` otherwise.
- * @example
- * v.isAlpha('bart');
- * // => true
- *
- * v.isAlpha('lisa!');
- * // => false
- *
- * v.isAlpha('lisa and bart');
- * // => false
- */
-
- function isAlpha(subject) {
- var subjectString = coerceToString(subject);
- return REGEXP_ALPHA.test(subjectString);
- }
-
- /**
- * Checks whether `subject` contains only alpha and digit characters.
- *
- * @function isAlphaDigit
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @return {boolean} Returns `true` if `subject` contains only alpha and digit characters or `false` otherwise.
- * @example
- * v.isAlphaDigit('year2020');
- * // => true
- *
- * v.isAlphaDigit('1448');
- * // => true
- *
- * v.isAlphaDigit('40-20');
- * // => false
- */
-
- function isAlphaDigit(subject) {
- var subjectString = coerceToString(subject);
- return REGEXP_ALPHA_DIGIT.test(subjectString);
- }
-
- /**
- * Checks whether `subject` is empty or contains only whitespaces.
- *
- * @function isBlank
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @return {boolean} Returns `true` if `subject` is empty or contains only whitespaces or `false` otherwise.
- * @example
- * v.isBlank('');
- * // => true
- *
- * v.isBlank(' ');
- * // => true
- *
- * v.isBlank('World');
- * // => false
- */
-
- function isBlank(subject) {
- var subjectString = coerceToString(subject);
- return subjectString.trim().length === 0;
- }
-
- /**
- * Checks whether `subject` contains only digit characters.
- *
- * @function isDigit
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @return {boolean} Returns `true` if `subject` contains only digit characters or `false` otherwise.
- * @example
- * v.isDigit('35');
- * // => true
- *
- * v.isDigit('1.5');
- * // => false
- *
- * v.isDigit('ten');
- * // => false
- */
-
- function isDigit(subject) {
- var subjectString = coerceToString(subject);
- return REGEXP_DIGIT.test(subjectString);
- }
-
- /**
- * Checks whether `subject` is empty.
- *
- * @function isEmpty
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @return {boolean} Returns `true` if `subject` is empty or `false` otherwise
- * @example
- * v.isEmpty('');
- * // => true
- *
- * v.isEmpty(' ');
- * // => false
- *
- * v.isEmpty('sun');
- * // => false
- */
-
- function isEmpty(subject) {
- var subjectString = coerceToString(subject);
- return subjectString.length === 0;
- }
-
- /**
- * Checks whether `subject` has only lower case characters.
- *
- * @function isLowerCase
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @return {boolean} Returns `true` if `subject` is lower case or `false` otherwise.
- * @example
- * v.isLowerCase('motorcycle');
- * // => true
- *
- * v.isLowerCase('John');
- * // => false
- *
- * v.isLowerCase('T1000');
- * // => false
- */
-
- function isLowerCase(subject) {
- var valueString = coerceToString(subject);
- return isAlpha(valueString) && valueString.toLowerCase() === valueString;
- }
-
- /**
- * Checks whether `subject` is numeric.
- *
- * @function isNumeric
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @return {boolean} Returns `true` if `subject` is numeric or `false` otherwise.
- * @example
- * v.isNumeric('350');
- * // => true
- *
- * v.isNumeric('-20.5');
- * // => true
- *
- * v.isNumeric('1.5E+2');
- * // => true
- *
- * v.isNumeric('five');
- * // => false
- */
-
- function isNumeric(subject) {
- var valueNumeric = typeof subject === 'object' && !isNil(subject) ? Number(subject) : subject;
- return (typeof valueNumeric === 'number' || typeof valueNumeric === 'string') && !isNaN(valueNumeric - parseFloat(valueNumeric));
- }
-
- /**
- * Checks whether `subject` contains only upper case characters.
- *
- * @function isUpperCase
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @return {boolean} Returns `true` if `subject` is upper case or `false` otherwise.
- * @example
- * v.isUpperCase('ACDC');
- * // => true
- *
- * v.isUpperCase('Morning');
- * // => false
- */
-
- function isUpperCase(subject) {
- var subjectString = coerceToString(subject);
- return isAlpha(subjectString) && subjectString.toUpperCase() === subjectString;
- }
-
- /**
- * Checks whether `subject` matches the regular expression `pattern`.
- *
- * @function matches
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @param {RegExp|string} pattern The pattern to match. If `pattern` is not RegExp, it is transformed to `new RegExp(pattern, flags)`.
- * @param {string} [flags=''] The regular expression flags. Applies when `pattern` is string type.
- * @return {boolean} Returns `true` if `subject` matches `pattern` or `false` otherwise.
- * @example
- * v.matches('pluto', /plu.{2}/);
- * // => true
- *
- * v.matches('sun', 'S', 'i');
- * // => true
- *
- * v.matches('apollo 11', '\\d{3}');
- * // => false
- */
-
- function matches(subject, pattern, flags) {
- var subjectString = coerceToString(subject);
- var flagsString = coerceToString(flags);
- var patternString;
-
- if (!(pattern instanceof RegExp)) {
- patternString = toString(pattern);
-
- if (patternString === null) {
- return false;
- }
-
- pattern = new RegExp(patternString, flagsString);
- }
-
- return pattern.test(subjectString);
- }
-
- /**
- * Checks whether `subject` starts with `start`.
- *
- * @function startsWith
- * @static
- * @since 1.0.0
- * @memberOf Query
- * @param {string} [subject=''] The string to verify.
- * @param {string} start The starting string.
- * @param {number} [position=0] The position to start searching.
- * @return {boolean} Returns `true` if `subject` starts with `start` or `false` otherwise.
- * @example
- * v.startsWith('say hello to my little friend', 'say hello');
- * // => true
- *
- * v.startsWith('tony', 'on', 1);
- * // => true
- *
- * v.startsWith('the world is yours', 'world');
- * // => false
- */
-
- function startsWith(subject, start, position) {
- var subjectString = coerceToString(subject);
- var startString = toString(start);
-
- if (startString === null) {
- return false;
- }
-
- if (startString === '') {
- return true;
- }
-
- position = isNil(position) ? 0 : clipNumber(toInteger(position), 0, subjectString.length);
- return subjectString.substr(position, startString.length) === startString;
- }
-
- /**
- * Splits `subject` into an array of characters.
- *
- * @function chars
- * @static
- * @since 1.0.0
- * @memberOf Split
- * @param {string} [subject=''] The string to split into characters.
- * @return {Array} Returns the array of characters.
- * @example
- * v.chars('cloud');
- * // => ['c', 'l', 'o', 'u', 'd']
- */
-
- function chars(subject) {
- var subjectString = coerceToString(subject);
- return subjectString.split('');
- }
-
- /**
- * Returns an array of Unicode code point values from characters of `subject`.
- *
- * @function codePoints
- * @static
- * @since 1.0.0
- * @memberOf Split
- * @param {string} [subject=''] The string to extract from.
- * @return {Array} Returns an array of non-negative numbers less than or equal to `0x10FFFF`.
- * @example
- * v.codePoints('rain');
- * // => [114, 97, 105, 110], or
- * // [0x72, 0x61, 0x69, 0x6E]
- *
- * v.codePoints('\uD83D\uDE00 smile'); // or '😀 smile'
- * // => [128512, 32, 115, 109, 105, 108, 101], or
- * // [0x1F600, 0x20, 0x73, 0x6D, 0x69, 0x6C, 0x65]
- */
-
- function codePoints(subject) {
- var subjectString = coerceToString(subject);
- var subjectStringLength = subjectString.length;
- var codePointArray = [];
- var index = 0;
- var codePointNumber;
-
- while (index < subjectStringLength) {
- codePointNumber = codePointAt(subjectString, index);
- codePointArray.push(codePointNumber);
- index += codePointNumber > 0xffff ? 2 : 1;
- }
-
- return codePointArray;
- }
-
- /**
- * Splits `subject` into an array of graphemes taking care of
- * surrogate pairs and
- * combining marks.
- *
- * @function graphemes
- * @static
- * @since 1.0.0
- * @memberOf Split
- * @param {string} [subject=''] The string to split into characters.
- * @return {Array} Returns the array of graphemes.
- * @example
- * v.graphemes('\uD835\uDC00\uD835\uDC01'); // or '𝐀𝐁'
- * // => ['\uD835\uDC00', '\uD835\uDC01'], or
- * // ['𝐀', '𝐁']
- *
- * v.graphemes('cafe\u0301'); // or 'café'
- * // => ['c', 'a', 'f', 'e\u0301'], or
- * // ['c', 'a', 'f', 'é']
- */
-
- function graphemes(subject) {
- var subjectString = coerceToString(subject);
- return nilDefault(subjectString.match(REGEXP_UNICODE_CHARACTER), []);
- }
-
- /**
- * Splits `subject` into an array of chunks by `separator`.
- *
- * @function split
- * @static
- * @since 1.0.0
- * @memberOf Split
- * @param {string} [subject=''] The string to split into characters.
- * @param {string|RegExp} [separator] The pattern to match the separator.
- * @param {number} [limit] Limit the number of chunks to be found.
- * @return {Array} Returns the array of chunks.
- * @example
- * v.split('rage against the dying of the light', ' ');
- * // => ['rage', 'against', 'the', 'dying', 'of', 'the', 'light']
- *
- * v.split('the dying of the light', /\s/, 3);
- * // => ['the', 'dying', 'of']
- */
-
- function split(subject, separator, limit) {
- var subjectString = coerceToString(subject);
- return subjectString.split(separator, limit);
- }
-
- var BYRE_ORDER_MARK = '\uFEFF';
- /**
- * Strips the byte order mark (BOM) from the beginning of `subject`.
- *
- * @function stripBom
- * @static
- * @since 1.2.0
- * @memberOf Strip
- * @param {string} [subject=''] The string to strip from.
- * @return {string} Returns the stripped string.
- * @example
- *
- * v.stripBom('\uFEFFsummertime sadness');
- * // => 'summertime sadness'
- *
- * v.stripBom('summertime happiness');
- * // => 'summertime happiness'
- *
- */
-
- function trim$1(subject) {
- var subjectString = coerceToString(subject);
-
- if (subjectString === '') {
- return '';
- }
-
- if (subjectString[0] === BYRE_ORDER_MARK) {
- return subjectString.substring(1);
- }
-
- return subjectString;
- }
-
- /**
- * Checks whether `subject` contains substring at specific `index`.
- *
- * @ignore
- * @param {string} subject The subject to search in.
- * @param {string} substring The substring to search/
- * @param {number} index The index to search substring.
- * @param {boolean} lookBehind Whether to look behind (true) or ahead (false).
- * @return {boolean} Returns a boolean whether the substring exists.
- */
- function hasSubstringAtIndex(subject, substring, index) {
- var lookBehind = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
- var indexOffset = 0;
-
- if (lookBehind) {
- indexOffset = -substring.length + 1;
- }
-
- var extractedSubstring = subject.substr(index + indexOffset, substring.length);
- return extractedSubstring.toLowerCase() === substring;
- }
-
- /**
- * Parses the tags from the string '...'.
- *
- * @ignore
- * @param {string} tags The string that contains the tags.
- * @return {string[]} Returns the array of tag names.
- */
-
- function parseTagList(tags) {
- var tagsList = [];
- var match;
-
- while ((match = REGEXP_TAG_LIST.exec(tags)) !== null) {
- tagsList.push(match[1]);
- }
-
- return tagsList;
- }
-
- var STATE_START_TAG = 0;
- var STATE_NON_WHITESPACE = 1;
- var STATE_DONE = 2;
- /**
- * Parses the tag name from html content.
- *
- * @ignore
- * @param {string} tagContent The tag content.
- * @return {string} Returns the tag name.
- */
-
- function parseTagName(tagContent) {
- var state = STATE_START_TAG;
- var tagName = '';
- var index = 0;
-
- while (state !== STATE_DONE) {
- var char = tagContent[index++].toLowerCase();
-
- switch (char) {
- case '<':
- break;
-
- case '>':
- state = STATE_DONE;
- break;
-
- default:
- if (REGEXP_WHITESPACE.test(char)) {
- if (state === STATE_NON_WHITESPACE) {
- state = STATE_DONE;
- }
- } else {
- if (state === STATE_START_TAG) {
- state = STATE_NON_WHITESPACE;
- }
-
- if (char !== '/') {
- tagName += char;
- }
- }
-
- break;
- }
- }
-
- return tagName;
- }
-
- var STATE_OUTPUT = 0;
- var STATE_HTML = 1;
- var STATE_EXCLAMATION = 2;
- var STATE_COMMENT = 3;
- /**
- * Strips HTML tags from `subject`.
- *
- * @function stripTags
- * @static
- * @since 1.1.0
- * @memberOf Strip
- * @param {string} [subject=''] The string to strip from.
- * @param {string|Array} [allowableTags] The string `''` or array `['tag1', 'tag2']` of tags that should not be stripped.
- * @param {string} [replacement=''] The string to replace the stripped tag.
- * @return {string} Returns the stripped string.
- * @example
- *
- * v.stripTags('Summer is nice');
- * // => 'Summer is nice'
- *
- * v.stripTags('Winter is cold', ['b', 'i']);
- * // => 'Winter is cold'
- *
- * v.stripTags('Sun
set', '', '-');
- * // => 'Sun-set'
- */
-
- function trim$2(subject, allowableTags, replacement) {
- subject = coerceToString(subject);
-
- if (subject === '') {
- return '';
- }
-
- if (!Array.isArray(allowableTags)) {
- var allowableTagsString = coerceToString(allowableTags);
- allowableTags = allowableTagsString === '' ? [] : parseTagList(allowableTagsString);
- }
-
- var replacementString = coerceToString(replacement);
- var length = subject.length;
- var hasAllowableTags = allowableTags.length > 0;
- var hasSubstring = hasSubstringAtIndex.bind(null, subject);
- var state = STATE_OUTPUT;
- var depth = 0;
- var output = '';
- var tagContent = '';
- var quote = null;
-
- for (var index = 0; index < length; index++) {
- var char = subject[index];
- var advance = false;
-
- switch (char) {
- case '<':
- if (quote) {
- break;
- }
-
- if (hasSubstring('< ', index, false)) {
- advance = true;
- break;
- }
-
- if (state === STATE_OUTPUT) {
- advance = true;
- state = STATE_HTML;
- break;
- }
-
- if (state === STATE_HTML) {
- depth++;
- break;
- }
-
- advance = true;
- break;
-
- case '!':
- if (state === STATE_HTML && hasSubstring('':
- if (depth > 0) {
- depth--;
- break;
- }
-
- if (quote) {
- break;
- }
-
- if (state === STATE_HTML) {
- quote = null;
- state = STATE_OUTPUT;
-
- if (hasAllowableTags) {
- tagContent += '>';
- var tagName = parseTagName(tagContent);
-
- if (allowableTags.indexOf(tagName.toLowerCase()) !== -1) {
- output += tagContent;
- } else {
- output += replacementString;
- }
-
- tagContent = '';
- } else {
- output += replacementString;
- }
-
- break;
- }
-
- if (state === STATE_EXCLAMATION || state === STATE_COMMENT && hasSubstring('-->', index)) {
- quote = null;
- state = STATE_OUTPUT;
- tagContent = '';
- break;
- }
-
- advance = true;
- break;
-
- default:
- advance = true;
- }
-
- if (advance) {
- switch (state) {
- case STATE_OUTPUT:
- output += char;
- break;
-
- case STATE_HTML:
- if (hasAllowableTags) {
- tagContent += char;
- }
-
- break;
- }
- }
- }
-
- return output;
- }
-
- var globalObject = null;
-
- function getGlobalObject() {
- if (globalObject !== null) {
- return globalObject;
- }
- /* istanbul ignore next */
- // It's hard to mock the global variables. This code surely works fine. I hope :)
-
-
- if (typeof global === 'object' && global.Object === Object) {
- // NodeJS global object
- globalObject = global;
- } else if (typeof self === 'object' && self.Object === Object) {
- // self property from Window object
- globalObject = self;
- } else {
- // Other cases. Function constructor always has the context as global object
- globalObject = new Function('return this')();
- }
-
- return globalObject;
- }
-
- var globalObject$1 = getGlobalObject();
- var previousV = globalObject$1.v;
- /**
- * Restores `v` variable to previous value and returns Voca library instance.
- *
- * @function noConflict
- * @static
- * @since 1.0.0
- * @memberOf Util
- * @return {Object} Returns Voca library instance.
- * @example
- * var voca = v.noConflict();
- * voca.isAlpha('Hello');
- * // => true
- */
-
- function noConflict() {
- if (this === globalObject$1.v) {
- globalObject$1.v = previousV;
- }
-
- return this;
- }
-
- /**
- * A property that contains the library semantic version number.
- * @name version
- * @static
- * @since 1.0.0
- * @memberOf Util
- * @type string
- * @example
- * v.version
- * // => '1.4.0'
- */
- var version = '1.4.0';
-
- /* eslint sort-imports: "off" */
- var functions = {
- camelCase: camelCase,
- capitalize: capitalize,
- decapitalize: decapitalize,
- kebabCase: kebabCase,
- lowerCase: lowerCase,
- snakeCase: snakeCase,
- swapCase: swapCase,
- titleCase: titleCase,
- upperCase: upperCase,
- count: count,
- countGraphemes: countGrapheme,
- countSubstrings: countSubstrings,
- countWhere: countWhere,
- countWords: countWords,
- escapeHtml: escapeHtml,
- escapeRegExp: escapeRegExp,
- unescapeHtml: unescapeHtml,
- sprintf: sprintf,
- vprintf: vprintf,
- indexOf: indexOf,
- lastIndexOf: lastIndexOf,
- search: search,
- charAt: charAt,
- codePointAt: codePointAt,
- first: first,
- graphemeAt: graphemeAt,
- last: last,
- prune: prune,
- slice: slice,
- substr: substr,
- substring: substring,
- truncate: truncate,
- insert: insert,
- latinise: latinise,
- pad: pad,
- padLeft: padLeft,
- padRight: padRight,
- repeat: repeat,
- replace: replace,
- replaceAll: replaceAll,
- reverse: reverse,
- reverseGrapheme: reverseGrapheme,
- slugify: slugify,
- splice: splice,
- tr: tr,
- trim: trim,
- trimLeft: trimLeft,
- trimRight: trimRight,
- wordWrap: wordWrap,
- endsWith: endsWith,
- includes: includes,
- isAlpha: isAlpha,
- isAlphaDigit: isAlphaDigit,
- isBlank: isBlank,
- isDigit: isDigit,
- isEmpty: isEmpty,
- isLowerCase: isLowerCase,
- isNumeric: isNumeric,
- isString: isString,
- isUpperCase: isUpperCase,
- matches: matches,
- startsWith: startsWith,
- chars: chars,
- codePoints: codePoints,
- graphemes: graphemes,
- split: split,
- words: words,
- stripBom: trim$1,
- stripTags: trim$2,
- noConflict: noConflict,
- version: version
- };
-
- /**
- * The chain wrapper constructor.
- *
- * @ignore
- * @param {string} subject The string to be wrapped.
- * @param {boolean} [explicitChain=false] A boolean that indicates if the chain sequence is explicit or implicit.
- * @return {ChainWrapper} Returns a new instance of `ChainWrapper`
- * @constructor
- */
-
- function ChainWrapper(subject, explicitChain) {
- this._wrappedValue = subject;
- this._explicitChain = explicitChain;
- }
- /**
- * Unwraps the chain sequence wrapped value.
- *
- * @memberof Chain
- * @since 1.0.0
- * @function __proto__value
- * @return {*} Returns the unwrapped value.
- * @example
- * v
- * .chain('Hello world')
- * .replace('Hello', 'Hi')
- * .lowerCase()
- * .slugify()
- * .value()
- * // => 'hi-world'
- *
- * v(' Space travel ')
- * .trim()
- * .truncate(8)
- * .value()
- * // => 'Space...'
- */
-
-
- ChainWrapper.prototype.value = function () {
- return this._wrappedValue;
- };
- /**
- * Override the default object valueOf().
- *
- * @ignore
- * @return {*} Returns the wrapped value.
- */
-
-
- ChainWrapper.prototype.valueOf = function () {
- return this.value();
- };
- /**
- * Returns the wrapped value to be used in JSON.stringify().
- *
- * @ignore
- * @return {*} Returns the wrapped value.
- */
-
-
- ChainWrapper.prototype.toJSON = function () {
- return this.value();
- };
- /**
- * Returns the string representation of the wrapped value.
- *
- * @ignore
- * @return {string} Returns the string representation.
- */
-
-
- ChainWrapper.prototype.toString = function () {
- return String(this.value());
- };
- /**
- * Creates a new chain object that enables explicit chain sequences.
- * Use `v.prototype.value()` to unwrap the result.
- * Does not modify the wrapped value.
- *
- * @memberof Chain
- * @since 1.0.0
- * @function __proto__chain
- * @return {Object} Returns the wrapper in explicit mode.
- * @example
- * v('Back to School')
- * .chain()
- * .lowerCase()
- * .words()
- * .value()
- * // => ['back', 'to', 'school']
- *
- * v(" Back to School ")
- * .chain()
- * .trim()
- * .truncate(7)
- * .value()
- * // => 'Back...'
- */
-
-
- ChainWrapper.prototype.chain = function () {
- return new ChainWrapper(this._wrappedValue, true);
- };
- /**
- * Modifies the wrapped value with the invocation result of `changer` function. The current wrapped value is the
- * argument of `changer` invocation.
- *
- * @memberof Chain
- * @since 1.0.0
- * @function __proto__thru
- * @param {Function} changer The function to invoke.
- * @return {Object} Returns the new wrapper that wraps the invocation result of `changer`.
- * @example
- * v
- * .chain('sun is shining')
- * .words()
- * .thru(function(words) {
- * return words[0];
- * })
- * .value()
- * // => 'sun'
- *
- */
-
-
- ChainWrapper.prototype.thru = function (changer) {
- if (typeof changer === 'function') {
- return new ChainWrapper(changer(this._wrappedValue), this._explicitChain);
- }
-
- return this;
- };
- /**
- * A boolean that indicates if the chain sequence is explicit or implicit.
- * @ignore
- * @type {boolean}
- * @private
- */
-
-
- ChainWrapper.prototype._explicitChain = true;
- /**
- * Make a voca function chainable.
- *
- * @ignore
- * @param {Function} functionInstance The function to make chainable
- * @return {Function} Returns the chainable function
- */
-
- function makeFunctionChainable(functionInstance) {
- return function () {
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
- args[_key] = arguments[_key];
- }
-
- var result = functionInstance.apply(void 0, [this._wrappedValue].concat(args));
-
- if (this._explicitChain || typeof result === 'string') {
- return new ChainWrapper(result, this._explicitChain);
- } else {
- return result;
- }
- };
- }
-
- Object.keys(functions).forEach(function (name) {
- ChainWrapper.prototype[name] = makeFunctionChainable(functions[name]);
- });
-
- /**
- * Creates a chain object that wraps `subject`, enabling explicit chain sequences.
- * Use `v.prototype.value()` to unwrap the result.
- *
- * @memberOf Chain
- * @since 1.0.0
- * @function chain
- * @param {string} subject The string to wrap.
- * @return {Object} Returns the new wrapper object.
- * @example
- * v
- * .chain('Back to School')
- * .lowerCase()
- * .words()
- * .value()
- * // => ['back', 'to', 'school']
- */
-
- function chain(subject) {
- return new ChainWrapper(subject, true);
- }
-
- /**
- * Creates a chain object that wraps `subject`, enabling implicit chain sequences.
- * A function that returns `number`, `boolean` or `array` type terminates the chain sequence and returns the unwrapped value.
- * Otherwise use `v.prototype.value()` to unwrap the result.
- *
- * @memberOf Chain
- * @since 1.0.0
- * @function v
- * @param {string} subject The string to wrap.
- * @return {Object} Returns the new wrapper object.
- * @example
- * v('Back to School')
- * .lowerCase()
- * .words()
- * // => ['back', 'to', 'school']
- *
- * v(" Back to School ")
- * .trim()
- * .truncate(7)
- * .value()
- * // => 'Back...'
- */
-
- function Voca(subject) {
- return new ChainWrapper(subject, false);
- }
-
- _extends(Voca, functions, {
- chain: chain
- });
-
- return Voca;
-
-})));
-
-
/***/ }),
/***/ 3766:
@@ -66333,12 +62036,9 @@ function packageJsonBase(options) {
/***/ }),
/***/ 1490:
-/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
-var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
-};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getCopyrightHeader = getCopyrightHeader;
exports.validateNpmCompliance = validateNpmCompliance;
@@ -66346,7 +62046,6 @@ exports.npmCompliantName = npmCompliantName;
exports.directoryToSpeakingModuleName = directoryToSpeakingModuleName;
exports.directoryToServiceName = directoryToServiceName;
const util_1 = __nccwpck_require__(2343);
-const voca_1 = __importDefault(__nccwpck_require__(5231));
/**
* @returns A copyright header
* @internal
@@ -66412,7 +62111,7 @@ function transformUnscopedName(packageName) {
* @internal
*/
function directoryToSpeakingModuleName(packageName) {
- return voca_1.default.titleCase(packageName.replace(/[-,_]/g, ' '));
+ return (0, util_1.titleFormat)(packageName.replace(/[-,_]/g, ' '));
}
/**
* This is taken from version 2.0
@@ -67636,21 +63335,20 @@ function removeLeadingSlashes(path) {
/***/ }),
/***/ 7841:
-/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+/***/ ((__unused_webpack_module, exports) => {
-var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
-};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.webEOL = exports.unixEOL = void 0;
+exports.capitalize = capitalize;
+exports.decapitalize = decapitalize;
exports.upperCaseSnakeCase = upperCaseSnakeCase;
exports.camelCase = camelCase;
exports.titleFormat = titleFormat;
exports.pascalCase = pascalCase;
exports.kebabCase = kebabCase;
+exports.titleCase = titleCase;
exports.formatJson = formatJson;
-const voca_1 = __importDefault(__nccwpck_require__(5231));
/**
* Within all files generated by the SDK we use the unix style end of line delimiter.
* We do not consider if the generator is executed on windows or unix systems.
@@ -67663,13 +63361,114 @@ exports.unixEOL = '\n';
* @deprecated Since v4.6.0. Use '\r\n' directly instead.
*/
exports.webEOL = '\r\n';
+// Uses Unicode property escapes to correctly classify non-ASCII letters and digits
+// (e.g. accented uppercase Ü, Arabic-Indic digits ٣). Caseless letters (CJK, Arabic,
+// titlecase like Dž) are treated as lowercase so they are included in words rather than dropped.
+function charKind(c) {
+ if (/^\p{Lu}$/u.test(c)) {
+ return 'upper';
+ }
+ if (/^\p{L}$/u.test(c)) {
+ return 'lower';
+ } // covers Ll, Lt, Lm, Lo
+ if (/^\p{N}$/u.test(c)) {
+ return 'digit';
+ }
+ return 'separator';
+}
+// Splits an identifier string into words, handling common case conventions:
+// camelCase → ['camel', 'Case']
+// XMLParser → ['XML', 'Parser']
+// field_name → ['field', 'name']
+// Field13Name → ['Field', '13', 'Name']
+function words(str) {
+ if (!str) {
+ return [];
+ }
+ const result = [];
+ let word = '';
+ let state = 'none';
+ const pushWord = () => {
+ if (word) {
+ result.push(word);
+ }
+ word = '';
+ };
+ for (const char of str) {
+ const kind = charKind(char);
+ if (state === 'none' || state === 'separator') {
+ if (kind !== 'separator') {
+ word = char;
+ state = kind;
+ }
+ }
+ else if (kind === 'separator') {
+ pushWord();
+ state = 'separator';
+ }
+ else if (kind === 'lower' && state === 'upper') {
+ // Transition into a lowercase run: if we accumulated multiple uppers,
+ // the last one belongs to this new word (e.g. XMLParser → XML + Parser)
+ if (word.length > 1) {
+ const last = word.slice(-1);
+ word = word.slice(0, -1);
+ pushWord();
+ word = last;
+ }
+ word += char;
+ state = 'lower';
+ }
+ else if (kind !== state) {
+ // Any other kind change (lower→digit, digit→upper, etc.) starts a new word
+ pushWord();
+ word = char;
+ state = kind;
+ }
+ else {
+ word += char;
+ }
+ }
+ pushWord();
+ return result;
+}
+// Applies transforms to the first Unicode code point and the remainder.
+// Uses codePointAt to correctly handle surrogate pairs (e.g. emoji, some CJK).
+function transformInitialLetter(str, headTransform, tailTransform = s => s) {
+ if (!str?.length) {
+ return str ?? '';
+ }
+ // A surrogate pair occupies 2 UTF-16 code units; all BMP chars occupy 1.
+ const firstCharLength = str.codePointAt(0) > 0xffff ? 2 : 1;
+ return (headTransform(str.slice(0, firstCharLength)) +
+ tailTransform(str.slice(firstCharLength)));
+}
+/**
+ * Uppercase the first character of a string, leaving the rest unchanged.
+ * @param str - The string to capitalize.
+ * @returns The string with the first character uppercased.
+ */
+function capitalize(str) {
+ return transformInitialLetter(str, c => c.toUpperCase());
+}
+/**
+ * Lowercase the first character of a string, leaving the rest unchanged.
+ * @param str - The string to decapitalize.
+ * @returns The string with the first character lowercased.
+ * @internal
+ */
+function decapitalize(str) {
+ return transformInitialLetter(str, c => c.toLowerCase());
+}
+function capitalizeWord(str) {
+ return transformInitialLetter(str, c => c.toUpperCase(), s => s.toLowerCase());
+}
/**
* Convert a string to the uppercase snake case. This format is used e.g. for static properties on entity classes.
* @param str - The string to be transformed.
* @returns The input string in the case used by static methods on entity-classes.
*/
function upperCaseSnakeCase(str) {
- return voca_1.default.upperCase(voca_1.default.snakeCase(str));
+ return words(str).join('_').toUpperCase();
}
/**
* Convert a string to camelCase. This format used e.g. for properties on entity class instances.
@@ -67677,7 +63476,11 @@ function upperCaseSnakeCase(str) {
* @returns The transformed string.
*/
function camelCase(str) {
- return voca_1.default.camelCase(str);
+ const parts = words(str);
+ if (!parts.length) {
+ return '';
+ }
+ return parts[0].toLowerCase() + parts.slice(1).map(capitalizeWord).join('');
}
/**
* Convert a string to a human readable format, e.g. it transforms `to_BusinessPartner` to `To Business Partner`.
@@ -67685,7 +63488,7 @@ function camelCase(str) {
* @returns The transformed string.
*/
function titleFormat(str) {
- return voca_1.default.titleCase(voca_1.default.words(str).join(' '));
+ return words(str).map(capitalizeWord).join(' ');
}
/**
* Convert a string to pascal case. This format is used e.g. for types.
@@ -67693,10 +63496,7 @@ function titleFormat(str) {
* @returns The transformed string.
*/
function pascalCase(str) {
- return voca_1.default
- .words(str)
- .map(word => voca_1.default.capitalize(word))
- .join('');
+ return words(str).map(capitalize).join('');
}
/**
* Convert a string to kebab case. This format is used e.g. for file names.
@@ -67704,7 +63504,16 @@ function pascalCase(str) {
* @returns The transformed string.
*/
function kebabCase(str) {
- return voca_1.default.kebabCase(str);
+ return words(str).join('-').toLowerCase();
+}
+/**
+ * Uppercase the first character after each word boundary, leaving separators intact.
+ * E.g. `content-type` → `Content-Type`, `if-match` → `If-Match`.
+ * @param str - The string to transform.
+ * @returns The string with each word's first character uppercased.
+ */
+function titleCase(str) {
+ return str.replace(/\b\w/g, c => c.toUpperCase());
}
/**
* Convert a JSON object to a string using formatting in line with the prettier with indentation and new line at the end.
diff --git a/packages/connectivity/package.json b/packages/connectivity/package.json
index c61436c3d8..1b1616c038 100644
--- a/packages/connectivity/package.json
+++ b/packages/connectivity/package.json
@@ -44,10 +44,9 @@
"@sap-cloud-sdk/util": "workspace:^",
"@sap/xsenv": "^6.2.0",
"@sap/xssec": "^4.13.0",
- "async-retry": "^1.3.3",
"axios": "^1.15.0",
"jks-js": "^1.1.6",
- "jsonwebtoken": "^9.0.3",
+ "jwt-decode": "^4.0.0",
"safe-stable-stringify": "^2.5.0"
},
"devDependencies": {
diff --git a/packages/connectivity/src/scp-cf/destination/destination-accessor-failure-cases.spec.ts b/packages/connectivity/src/scp-cf/destination/destination-accessor-failure-cases.spec.ts
index 437d2ba53e..ee21368d08 100644
--- a/packages/connectivity/src/scp-cf/destination/destination-accessor-failure-cases.spec.ts
+++ b/packages/connectivity/src/scp-cf/destination/destination-accessor-failure-cases.spec.ts
@@ -51,9 +51,10 @@ describe('Failure cases', () => {
jwt: 'fails',
cacheVerificationKeys: false
})
- ).rejects.toThrowErrorMatchingInlineSnapshot(
- '"JwtError: The given jwt payload does not encode valid JSON."'
- );
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
+"JwtError: The given jwt payload does not encode valid JSON.
+Cause: Invalid JWT format."
+`);
});
it('throws an error if the subaccount/instance destinations call fails', async () => {
diff --git a/packages/connectivity/src/scp-cf/destination/destination-service.spec.ts b/packages/connectivity/src/scp-cf/destination/destination-service.spec.ts
index 6101921d95..93870e2ad1 100644
--- a/packages/connectivity/src/scp-cf/destination/destination-service.spec.ts
+++ b/packages/connectivity/src/scp-cf/destination/destination-service.spec.ts
@@ -355,6 +355,7 @@ describe('destination service', () => {
describe('fetchDestinationByToken', () => {
afterEach(() => {
+ jest.useRealTimers();
jest.restoreAllMocks();
});
@@ -619,7 +620,22 @@ describe('destination service', () => {
}
);
expect(actual).toEqual(parseDestination(response));
- });
+ }, 10000);
+
+ it('stops retrying after the configured number of attempts for 500 errors', async () => {
+ const mock = nock(destinationServiceUri)
+ .get('/destination-configuration/v1/destinations/HTTP-BASIC')
+ .times(3)
+ .reply(500);
+
+ await expect(
+ fetchDestinationWithTokenRetrieval(destinationServiceUri, jwt, {
+ destinationName: 'HTTP-BASIC',
+ retry: true
+ })
+ ).rejects.toThrow();
+ expect(mock.isDone()).toBe(true);
+ }, 15000);
it('does no retry if request fails with 401 error', async () => {
const response = {
@@ -688,7 +704,7 @@ describe('destination service', () => {
}
);
expect(actual).toMatchObject(parseDestination(responseValidToken));
- });
+ }, 10000);
it('does a retry if auth tokens are failing but returns the destination with errors in the end', async () => {
const response = {
@@ -723,7 +739,7 @@ describe('destination service', () => {
);
expect(actual.authTokens![0].error).toEqual('ERROR');
expect(mock.isDone()).toBe(true);
- }, 10000);
+ }, 15000);
it('fetches a destination and returns 200 but authTokens are failing', async () => {
const destinationName = 'FINAL-DESTINATION';
diff --git a/packages/connectivity/src/scp-cf/destination/destination-service.ts b/packages/connectivity/src/scp-cf/destination/destination-service.ts
index c6f007e269..0dcfbbc1a9 100644
--- a/packages/connectivity/src/scp-cf/destination/destination-service.ts
+++ b/packages/connectivity/src/scp-cf/destination/destination-service.ts
@@ -7,7 +7,6 @@ import {
import axios from 'axios';
import { executeWithMiddleware } from '@sap-cloud-sdk/resilience/internal';
import { resilience } from '@sap-cloud-sdk/resilience';
-import asyncRetry from 'async-retry';
import { decodeJwt, getTenantId, wrapJwtInHeader } from '../jwt';
import { urlAndAgent } from '../../http-agent';
import { buildAuthorizationHeaders } from '../authorization-header';
@@ -303,6 +302,58 @@ function errorMessageFromResponse(
: '';
}
+/**
+ * @internal
+ * Retries a function with exponential backoff.
+ * @param fn - The function to retry.
+ * @param options - Options for retrying.
+ * @param options.retries - The maximum number of retries. Default is 3.
+ * @param options.onRetry - A callback function that is called before each retry with the error and the current attempt number.
+ * @param options.randomize - Whether to randomize the backoff time by a factor of 1-2. Default is true.
+ * @returns The result of the function.
+ */
+const sleep = (ms: number): Promise =>
+ new Promise(resolve => setTimeout(resolve, ms));
+
+async function withRetry(
+ fn: (bail: (err: Error) => never, attempt: number) => Promise,
+ options: {
+ retries?: number;
+ onRetry?: (err: Error, attempt: number) => void;
+ randomize?: boolean;
+ } = {}
+): Promise {
+ const maxRetries = options.retries ?? 3;
+ const randomize = Number(options.randomize ?? true);
+
+ class BailError extends Error {
+ constructor(readonly cause: Error) {
+ super(cause.message);
+ }
+ }
+
+ for (let attempt = 0; attempt < maxRetries - 1; attempt++) {
+ try {
+ return await fn(err => {
+ throw new BailError(err);
+ }, attempt);
+ } catch (error) {
+ if (error instanceof BailError) {
+ throw error.cause;
+ }
+ options.onRetry?.(error as Error, attempt + 1);
+ // Exponential backoff with optional randomization (factor 1-2x)
+ const scalingFactor = 1 + randomize * Math.random();
+ const backoff = Math.round(scalingFactor * 1000 * 2 ** attempt);
+ await sleep(backoff);
+ }
+ }
+
+ return fn(err => {
+ throw new BailError(err);
+ }, maxRetries - 1);
+}
+
function retryDestination(
destinationName: string
): Middleware<
@@ -311,20 +362,21 @@ function retryDestination(
MiddlewareContext
> {
return options => arg => {
- let retryCount = 1;
- return asyncRetry(
- async bail => {
+ const maxRetries = 3;
+ return withRetry(
+ async (bail, attempt) => {
try {
const destination = await options.fn(arg);
- if (retryCount < 3) {
- retryCount++;
+ if (attempt < maxRetries - 1) {
// this will throw if the destination does not contain valid auth headers and a second try is done to get a destination with valid tokens.
await buildAuthorizationHeaders(parseDestination(destination.data));
}
return destination;
} catch (error) {
- const status = error?.response?.status;
- if (status.toString().startsWith('4')) {
+ const status = axios.isAxiosError(error)
+ ? error.response?.status
+ : undefined;
+ if (status?.toString().startsWith('4')) {
bail(
new ErrorWithCause(
`Request failed with status code ${status}`,
@@ -338,7 +390,7 @@ function retryDestination(
}
},
{
- retries: 3,
+ retries: maxRetries,
onRetry: (err: Error) =>
logger.warn(
`Failed to retrieve destination ${destinationName} - doing a retry. Original Error ${err.message}`
diff --git a/packages/connectivity/src/scp-cf/jwt/jwt.ts b/packages/connectivity/src/scp-cf/jwt/jwt.ts
index 069f415db9..35899e56c9 100644
--- a/packages/connectivity/src/scp-cf/jwt/jwt.ts
+++ b/packages/connectivity/src/scp-cf/jwt/jwt.ts
@@ -1,9 +1,9 @@
import { createLogger, pickValueIgnoreCase } from '@sap-cloud-sdk/util';
-import { decode } from 'jsonwebtoken';
+import { jwtDecode } from 'jwt-decode';
import { Cache } from '../cache';
import { getIssuerSubdomain } from '../subdomain-replacer';
import type {
- Jwt,
+ JwtHeader,
JwtPayload,
JwtWithPayloadObject
} from '../jsonwebtoken-type';
@@ -136,7 +136,7 @@ function audiencesFromAud({ aud }: JwtPayload): string[] {
}
function audiencesFromScope({ scope }: JwtPayload): string[] {
- return makeArray(scope).reduce(
+ return makeArray(scope).reduce(
(aud, s) => (s.includes('.') ? [...aud, s.split('.')[0]] : aud),
[]
);
@@ -148,7 +148,20 @@ function audiencesFromScope({ scope }: JwtPayload): string[] {
* @returns Decoded payload.
*/
export function decodeJwt(token: string | JwtPayload): JwtPayload {
- return typeof token === 'string' ? decodeJwtComplete(token).payload : token;
+ if (typeof token !== 'string') {
+ return token;
+ }
+ try {
+ validateJwtFormat(token);
+ return decodeJwtPart(token);
+ } catch (error) {
+ throw new Error(
+ 'JwtError: The given jwt payload does not encode valid JSON.',
+ {
+ cause: error
+ }
+ );
+ }
}
/**
@@ -158,13 +171,21 @@ export function decodeJwt(token: string | JwtPayload): JwtPayload {
* @internal
*/
export function decodeJwtComplete(token: string): JwtWithPayloadObject {
- const decodedToken = decode(token, { complete: true, json: true });
- if (decodedToken !== null && isJwtWithPayloadObject(decodedToken)) {
- return decodedToken;
+ try {
+ const signature = validateJwtFormat(token);
+ return {
+ header: decodeJwtPart(token, { header: true }),
+ payload: decodeJwtPart(token),
+ signature
+ };
+ } catch (error) {
+ throw new Error(
+ 'JwtError: The given jwt payload does not encode valid JSON.',
+ {
+ cause: error
+ }
+ );
}
- throw new Error(
- 'JwtError: The given jwt payload does not encode valid JSON.'
- );
}
/**
@@ -274,6 +295,40 @@ export function isUserToken(token: JwtPair | undefined): token is JwtPair {
return !(keys.length === 1 && keys[0] === 'iss');
}
-function isJwtWithPayloadObject(decoded: Jwt): decoded is JwtWithPayloadObject {
- return typeof decoded.payload !== 'string';
+/**
+ * Validate the format of the given JWT and return the signature part if valid.
+ * @returns The signature part of the JWT if the format is valid.
+ * @throws An error if the JWT format is invalid.
+ * @internal
+ */
+function validateJwtFormat(token: string): string {
+ const [encodedHeader, encodedPayload, signature, ...rest] = token.split('.');
+
+ if (!encodedHeader || !encodedPayload || rest.length > 0) {
+ throw new Error('Invalid JWT format.');
+ }
+
+ return signature;
+}
+
+/**
+ * Decodes part of a JWT (header or payload) and ensures that the decoded value is an object.
+ * @param token - The JWT to decode.
+ * @param options - Options for decoding, e.g. whether to decode the header or payload.
+ * @param options.header - If true, decodes the header; otherwise, decodes the payload.
+ * @returns The decoded JWT part as an object.
+ * @throws An error if the decoded value is not an object.
+ * @internal
+ */
+function decodeJwtPart(
+ token: string,
+ options?: { header?: boolean }
+): T {
+ const decoded = jwtDecode(token, options);
+
+ if (typeof decoded !== 'object' || decoded === null) {
+ throw new Error('Invalid JWT content.');
+ }
+
+ return decoded;
}
diff --git a/packages/generator-common/package.json b/packages/generator-common/package.json
index a65beb3b7f..1f2971030a 100644
--- a/packages/generator-common/package.json
+++ b/packages/generator-common/package.json
@@ -42,10 +42,8 @@
"dependencies": {
"@sap-cloud-sdk/util": "workspace:^",
"fast-levenshtein": "~3.0.0",
- "fs-extra": "^11.3.4",
"glob": "^13.0.6",
"prettier": "^3.8.1",
- "voca": "^1.4.1",
"yargs": "^17.7.2"
},
"devDependencies": {
diff --git a/packages/generator-common/src/sdk-metadata/util.ts b/packages/generator-common/src/sdk-metadata/util.ts
index 89b1c42de6..29aa5f696e 100644
--- a/packages/generator-common/src/sdk-metadata/util.ts
+++ b/packages/generator-common/src/sdk-metadata/util.ts
@@ -1,5 +1,5 @@
import { resolve } from 'path';
-import { readFile } from 'fs-extra';
+import { readFile } from 'node:fs/promises';
/**
* Get the current SDK version from the package json.
diff --git a/packages/generator-common/src/util.ts b/packages/generator-common/src/util.ts
index 78bd0ee0aa..dd3c71c6cb 100644
--- a/packages/generator-common/src/util.ts
+++ b/packages/generator-common/src/util.ts
@@ -1,5 +1,4 @@
-import { codeBlock, createLogger } from '@sap-cloud-sdk/util';
-import voca from 'voca';
+import { codeBlock, createLogger, titleFormat } from '@sap-cloud-sdk/util';
/**
* @returns A copyright header
@@ -80,7 +79,7 @@ function transformUnscopedName(packageName: string) {
* @internal
*/
export function directoryToSpeakingModuleName(packageName: string): string {
- return voca.titleCase(packageName.replace(/[-,_]/g, ' '));
+ return titleFormat(packageName.replace(/[-,_]/g, ' '));
}
/**
diff --git a/packages/generator/package.json b/packages/generator/package.json
index d05102d43f..a4850ba2f1 100644
--- a/packages/generator/package.json
+++ b/packages/generator/package.json
@@ -48,12 +48,9 @@
"@sap-cloud-sdk/odata-v2": "workspace:^",
"@sap-cloud-sdk/odata-v4": "workspace:^",
"@sap-cloud-sdk/util": "workspace:^",
- "@types/fs-extra": "^11.0.4",
"fast-xml-parser": "^5.5.9",
- "fs-extra": "^11.3.4",
"ts-morph": "^28.0.0",
"typescript": "~5.9.3",
- "voca": "^1.4.1",
"winston": "^3.19.0"
},
"devDependencies": {
diff --git a/packages/generator/src/edmx-parser/v4/edmx-parser.ts b/packages/generator/src/edmx-parser/v4/edmx-parser.ts
index 57f356f7e6..8725c98f66 100644
--- a/packages/generator/src/edmx-parser/v4/edmx-parser.ts
+++ b/packages/generator/src/edmx-parser/v4/edmx-parser.ts
@@ -1,4 +1,4 @@
-import voca from 'voca';
+import { capitalize } from '@sap-cloud-sdk/util';
import {
getMergedPropertyWithNamespace,
getPropertyFromEntityContainer,
@@ -133,13 +133,11 @@ export function parseOperationImports(
root: any,
operationType: 'function' | 'action'
): EdmxOperationImport[] {
- const operations = getPropertyFromEntityContainer(
- root,
- `${voca.capitalize(operationType)}Import`
- );
+ const capType = capitalize(operationType);
+ const operations = getPropertyFromEntityContainer(root, `${capType}Import`);
return operations.map(operation => ({
...operation,
- operationName: operation[voca.capitalize(operationType)],
+ operationName: operation[capType],
operationType
}));
}
diff --git a/packages/generator/src/edmx-to-vdm/common/operation-return-type.ts b/packages/generator/src/edmx-to-vdm/common/operation-return-type.ts
index be382f6032..0dd81b5315 100644
--- a/packages/generator/src/edmx-to-vdm/common/operation-return-type.ts
+++ b/packages/generator/src/edmx-to-vdm/common/operation-return-type.ts
@@ -1,5 +1,4 @@
-import { first } from '@sap-cloud-sdk/util';
-import voca from 'voca';
+import { first, decapitalize } from '@sap-cloud-sdk/util';
import { isNullableProperty } from '../../generator-utils';
// eslint-disable-next-line import-x/no-internal-modules
import { getApiName } from '../../generator-without-ts-morph/service';
@@ -134,9 +133,7 @@ function getEntityReturnType(
? {
returnTypeCategory: 'entity',
returnType: first(entities)!.className,
- builderFunction: `${voca.decapitalize(
- serviceName
- )}(deSerializers).${getApiName(first(entities)!.className)}`,
+ builderFunction: `${decapitalize(serviceName)}(deSerializers).${getApiName(first(entities)!.className)}`,
isNullable,
isCollection
}
diff --git a/packages/generator/src/generator-without-ts-morph/service/class.ts b/packages/generator/src/generator-without-ts-morph/service/class.ts
index e7cdd6e036..77f094fb5b 100644
--- a/packages/generator/src/generator-without-ts-morph/service/class.ts
+++ b/packages/generator/src/generator-without-ts-morph/service/class.ts
@@ -1,5 +1,4 @@
-import { codeBlock } from '@sap-cloud-sdk/util';
-import voca from 'voca';
+import { codeBlock, decapitalize } from '@sap-cloud-sdk/util';
// eslint-disable-next-line import-x/no-internal-modules
import { matchEntity } from '../entity-api/match-entity';
import {
@@ -18,9 +17,7 @@ export function serviceBuilder(
oDataVersion: ODataVersion
): string {
return codeBlock`
- export function ${voca.decapitalize(
- serviceName
- )}<${getGenericTypesWithDefault(oDataVersion)}>(
+ export function ${decapitalize(serviceName)}<${getGenericTypesWithDefault(oDataVersion)}>(
deSerializers: Partial> = defaultDeSerializers as any
@@ -142,5 +139,5 @@ function getApiInitializer(entityClassName: string): string {
* @returns apiName e.g. testEntityApi if the entity is called TestEntity.
*/
export function getApiName(entityName: string): string {
- return `${voca.decapitalize(entityName)}Api`;
+ return `${decapitalize(entityName)}Api`;
}
diff --git a/packages/generator/src/generator.ts b/packages/generator/src/generator.ts
index d2d50ef5cd..b6bf47f70b 100644
--- a/packages/generator/src/generator.ts
+++ b/packages/generator/src/generator.ts
@@ -1,4 +1,9 @@
-import { existsSync, promises as fsPromises } from 'fs';
+import {
+ existsSync,
+ readdirSync,
+ rmSync,
+ promises as fsPromises
+} from 'node:fs';
import { dirname, join, resolve } from 'path';
import {
copyFiles,
@@ -20,7 +25,6 @@ import {
setLogLevel,
splitInChunks
} from '@sap-cloud-sdk/util';
-import { emptyDirSync } from 'fs-extra';
import {
IndentationText,
ModuleKind,
@@ -178,7 +182,12 @@ export async function generateProject(
const services = await parseServices(options);
if (options.clearOutputDir) {
- emptyDirSync(options.outputDir.toString());
+ for (const entry of readdirSync(options.outputDir.toString())) {
+ rmSync(join(options.outputDir.toString(), entry), {
+ recursive: true,
+ force: true
+ });
+ }
}
const project = new Project(
diff --git a/packages/generator/src/name-formatting-strategies.ts b/packages/generator/src/name-formatting-strategies.ts
index 93c3f2e2b8..d01171d365 100644
--- a/packages/generator/src/name-formatting-strategies.ts
+++ b/packages/generator/src/name-formatting-strategies.ts
@@ -1,12 +1,10 @@
-import voca from 'voca';
+import { capitalize } from '@sap-cloud-sdk/util';
import { reservedJsKeywords } from '@sap-cloud-sdk/generator-common/internal';
const applyPrefixOnJSReservedWords =
(prefix: string) =>
(param: string): string =>
- reservedJsKeywords.includes(param)
- ? prefix + voca.capitalize(param)
- : param;
+ reservedJsKeywords.includes(param) ? prefix + capitalize(param) : param;
/**
* @internal
diff --git a/packages/generator/src/operations/import.ts b/packages/generator/src/operations/import.ts
index 5c9c50bf40..43dfba6911 100644
--- a/packages/generator/src/operations/import.ts
+++ b/packages/generator/src/operations/import.ts
@@ -1,5 +1,5 @@
import { StructureKind } from 'ts-morph';
-import voca from 'voca';
+import { decapitalize } from '@sap-cloud-sdk/util';
import {
odataImportDeclarationTsMorph,
propertyTypeImportNames,
@@ -121,7 +121,7 @@ export function operationDeclarations(
: [
{
kind: StructureKind.ImportDeclaration,
- namedImports: [voca.decapitalize(className)],
+ namedImports: [decapitalize(className)],
moduleSpecifier: `./service${extension}`
}
];
diff --git a/packages/generator/src/sdk-metadata/code-samples.ts b/packages/generator/src/sdk-metadata/code-samples.ts
index eab9a3dc35..1ae078ed65 100644
--- a/packages/generator/src/sdk-metadata/code-samples.ts
+++ b/packages/generator/src/sdk-metadata/code-samples.ts
@@ -1,5 +1,4 @@
-import { codeBlock } from '@sap-cloud-sdk/util';
-import voca from 'voca';
+import { codeBlock, decapitalize } from '@sap-cloud-sdk/util';
import { getApiName } from '../generator-without-ts-morph';
import { getOperationParams } from './code-sample-util';
import type { VdmOperation } from '../vdm-types';
@@ -14,11 +13,9 @@ export function entityCodeSample(
directoryName: string
): MultiLineText {
return codeBlock`
-import { ${voca.decapitalize(
- serviceName
- )} } from './generated/${directoryName}';
+import { ${decapitalize(serviceName)} } from './generated/${directoryName}';
-const { ${getApiName(entityName)} } = ${voca.decapitalize(serviceName)}();
+const { ${getApiName(entityName)} } = ${decapitalize(serviceName)}();
const resultPromise = ${getApiName(
entityName
)}.requestBuilder().getAll().top(5).execute({ destinationName: 'myDestinationName' });
diff --git a/packages/generator/src/sdk-metadata/generation-and-usage.spec.ts b/packages/generator/src/sdk-metadata/generation-and-usage.spec.ts
index f032183287..f106460ab0 100644
--- a/packages/generator/src/sdk-metadata/generation-and-usage.spec.ts
+++ b/packages/generator/src/sdk-metadata/generation-and-usage.spec.ts
@@ -1,5 +1,6 @@
import { resolve } from 'path';
-import { writeFile, readFile, removeSync } from 'fs-extra';
+import { writeFile, readFile } from 'node:fs/promises';
+import { rmSync } from 'node:fs';
import execa from 'execa';
import { getApiSpecificUsage } from './generation-and-usage';
import { entityCodeSample } from './code-samples';
@@ -110,6 +111,8 @@ describe('generation-and-usage', () => {
'commonjs'
]);
await expect(readFile(resolve(__dirname, jsFile))).resolves.toBeDefined();
- [tsFile, jsFile].map(file => removeSync(resolve(__dirname, file)));
+ [tsFile, jsFile].map(file =>
+ rmSync(resolve(__dirname, file), { recursive: true, force: true })
+ );
}, 60000);
});
diff --git a/packages/generator/src/service-name-formatter.ts b/packages/generator/src/service-name-formatter.ts
index ed948350db..f05e187805 100644
--- a/packages/generator/src/service-name-formatter.ts
+++ b/packages/generator/src/service-name-formatter.ts
@@ -1,10 +1,11 @@
import {
camelCase,
createLogger,
+ kebabCase,
+ pascalCase,
UniqueNameGenerator,
upperCaseSnakeCase
} from '@sap-cloud-sdk/util';
-import voca from 'voca';
import { stripPrefix } from './internal-prefix';
import { applyPrefixOnJsConflictFunctionImports } from './name-formatting-strategies';
import {
@@ -21,7 +22,7 @@ export class ServiceNameFormatter {
let formattedName = name.replace(/\.|\//g, '_');
formattedName = stripAPIUnderscore(formattedName);
formattedName = stripUnderscoreSrv(formattedName);
- formattedName = voca.kebabCase(formattedName);
+ formattedName = kebabCase(formattedName);
return formattedName.endsWith('service')
? formattedName
: `${formattedName}-service`;
@@ -151,7 +152,7 @@ If you are ok with this change execute the generator with the '--skipValidation'
}
originalToOperationName(originalName: string): string {
- const transformedName = voca.camelCase(stripPrefix(originalName));
+ const transformedName = camelCase(stripPrefix(originalName));
const newName =
this.serviceWideNameGenerator.generateAndSaveUniqueName(transformedName);
@@ -197,7 +198,7 @@ If you are ok with this change execute the generator with the '--skipValidation'
entitySetName: string,
originalPropertyName: string
): string {
- const transformedName = voca.camelCase(originalPropertyName);
+ const transformedName = camelCase(originalPropertyName);
const generator = this.getOrInitGenerator(
this.instancePropertyNameGenerators,
@@ -218,7 +219,7 @@ If you are ok with this change execute the generator with the '--skipValidation'
originalFunctionImportName: string,
originalParameterName: string
): string {
- const transformedName = voca.camelCase(originalParameterName);
+ const transformedName = camelCase(originalParameterName);
const generator = this.getOrInitGenerator(
this.parameterNameGenerators,
@@ -241,7 +242,7 @@ If you are ok with this change execute the generator with the '--skipValidation'
originalFunctionImportName: string,
originalParameterName: string
): string {
- const transformedName = voca.camelCase(originalParameterName);
+ const transformedName = camelCase(originalParameterName);
const generator = this.getOrInitGenerator(
this.parameterNameGenerators,
@@ -265,7 +266,7 @@ If you are ok with this change execute the generator with the '--skipValidation'
transformedName = stripCollection(entitySetName);
}
- transformedName = stripAUnderscore(voca.titleCase(transformedName));
+ transformedName = pascalCase(stripAUnderscore(transformedName));
const uniqueName =
this.serviceWideNameGenerator.generateAndSaveUniqueNamesWithSuffixes(
@@ -287,9 +288,10 @@ If you are ok with this change execute the generator with the '--skipValidation'
originalName: string,
originalContainerTypeName: string
): string {
- const transformedName = stripAUnderscore(
- voca.titleCase(originalName)
- ).replace('_', '');
+ const transformedName = pascalCase(stripAUnderscore(originalName)).replace(
+ '_',
+ ''
+ );
const uniqueName = this.serviceWideNameGenerator.generateAndSaveUniqueName(
transformedName,
diff --git a/packages/odata-common/package.json b/packages/odata-common/package.json
index 2080fb14d5..1e1894b29a 100644
--- a/packages/odata-common/package.json
+++ b/packages/odata-common/package.json
@@ -44,8 +44,7 @@
"@sap-cloud-sdk/http-client": "workspace:^",
"@sap-cloud-sdk/util": "workspace:^",
"bignumber.js": "^11.0.0",
- "moment": "^2.30.1",
- "voca": "^1.4.1"
+ "moment": "^2.30.1"
},
"devDependencies": {
"@sap-cloud-sdk/test-services-odata-common": "workspace:^",
diff --git a/packages/odata-common/src/request-builder/batch/batch-request-serializer.ts b/packages/odata-common/src/request-builder/batch/batch-request-serializer.ts
index 43157c2da4..37dfc4948e 100644
--- a/packages/odata-common/src/request-builder/batch/batch-request-serializer.ts
+++ b/packages/odata-common/src/request-builder/batch/batch-request-serializer.ts
@@ -1,4 +1,4 @@
-import voca from 'voca';
+import { titleCase } from '@sap-cloud-sdk/util';
import { ODataRequest } from '../../request';
import { BatchChangeSet } from './batch-change-set';
import type { ODataRequestConfig, WithBatchReference } from '../../request';
@@ -55,7 +55,7 @@ export function serializeRequest(
...odataRequest.customHeaders()
};
const requestHeaders = Object.entries(headers).map(
- ([key, value]) => `${voca.titleCase(key)}: ${value}`
+ ([key, value]) => `${titleCase(key)}: ${value}`
);
const method = request.requestConfig.method.toUpperCase();
diff --git a/packages/util/package.json b/packages/util/package.json
index 8aa5e04bd0..8338d510b4 100644
--- a/packages/util/package.json
+++ b/packages/util/package.json
@@ -40,7 +40,6 @@
"dependencies": {
"axios": "^1.15.0",
"logform": "^2.7.0",
- "voca": "^1.4.1",
"winston": "^3.19.0",
"winston-transport": "^4.9.0"
},
diff --git a/packages/util/src/string-formatter.ts b/packages/util/src/string-formatter.ts
index 117bcd0efb..3ff9f74307 100644
--- a/packages/util/src/string-formatter.ts
+++ b/packages/util/src/string-formatter.ts
@@ -1,5 +1,3 @@
-import voca from 'voca';
-
/**
* Within all files generated by the SDK we use the unix style end of line delimiter.
* We do not consider if the generator is executed on windows or unix systems.
@@ -14,13 +12,133 @@ export const unixEOL = '\n';
*/
export const webEOL = '\r\n';
+type CharKind = 'upper' | 'lower' | 'digit' | 'separator';
+
+// Uses Unicode property escapes to correctly classify non-ASCII letters and digits
+// (e.g. accented uppercase Ü, Arabic-Indic digits ٣). Caseless letters (CJK, Arabic,
+// titlecase like Dž) are treated as lowercase so they are included in words rather than dropped.
+function charKind(c: string): CharKind {
+ if (/^\p{Lu}$/u.test(c)) {
+ return 'upper';
+ }
+ if (/^\p{L}$/u.test(c)) {
+ return 'lower';
+ } // covers Ll, Lt, Lm, Lo
+ if (/^\p{N}$/u.test(c)) {
+ return 'digit';
+ }
+ return 'separator';
+}
+
+// Splits an identifier string into words, handling common case conventions:
+// camelCase → ['camel', 'Case']
+// XMLParser → ['XML', 'Parser']
+// field_name → ['field', 'name']
+// Field13Name → ['Field', '13', 'Name']
+function words(str: string): string[] {
+ if (!str) {
+ return [];
+ }
+ const result: string[] = [];
+ let word = '';
+ let state: CharKind | 'none' = 'none';
+
+ const pushWord = () => {
+ if (word) {
+ result.push(word);
+ }
+ word = '';
+ };
+
+ for (const char of str) {
+ const kind = charKind(char);
+
+ if (state === 'none' || state === 'separator') {
+ if (kind !== 'separator') {
+ word = char;
+ state = kind;
+ }
+ } else if (kind === 'separator') {
+ pushWord();
+ state = 'separator';
+ } else if (kind === 'lower' && state === 'upper') {
+ // Transition into a lowercase run: if we accumulated multiple uppers,
+ // the last one belongs to this new word (e.g. XMLParser → XML + Parser)
+ if (word.length > 1) {
+ const last = word.slice(-1);
+ word = word.slice(0, -1);
+ pushWord();
+ word = last;
+ }
+ word += char;
+ state = 'lower';
+ } else if (kind !== state) {
+ // Any other kind change (lower→digit, digit→upper, etc.) starts a new word
+ pushWord();
+ word = char;
+ state = kind;
+ } else {
+ word += char;
+ }
+ }
+
+ pushWord();
+
+ return result;
+}
+
+// Applies transforms to the first Unicode code point and the remainder.
+// Uses codePointAt to correctly handle surrogate pairs (e.g. emoji, some CJK).
+function transformInitialLetter(
+ str: string,
+ headTransform: (c: string) => string,
+ tailTransform: (s: string) => string = s => s
+): string {
+ if (!str?.length) {
+ return str ?? '';
+ }
+ // A surrogate pair occupies 2 UTF-16 code units; all BMP chars occupy 1.
+ const firstCharLength = str.codePointAt(0)! > 0xffff ? 2 : 1;
+ return (
+ headTransform(str.slice(0, firstCharLength)) +
+ tailTransform(str.slice(firstCharLength))
+ );
+}
+
+/**
+ * Uppercase the first character of a string, leaving the rest unchanged.
+ * @param str - The string to capitalize.
+ * @returns The string with the first character uppercased.
+ */
+export function capitalize(str: string): string {
+ return transformInitialLetter(str, c => c.toUpperCase());
+}
+
+/**
+ * Lowercase the first character of a string, leaving the rest unchanged.
+ * @param str - The string to decapitalize.
+ * @returns The string with the first character lowercased.
+ * @internal
+ */
+export function decapitalize(str: string): string {
+ return transformInitialLetter(str, c => c.toLowerCase());
+}
+
+function capitalizeWord(str: string): string {
+ return transformInitialLetter(
+ str,
+ c => c.toUpperCase(),
+ s => s.toLowerCase()
+ );
+}
+
/**
* Convert a string to the uppercase snake case. This format is used e.g. for static properties on entity classes.
* @param str - The string to be transformed.
* @returns The input string in the case used by static methods on entity-classes.
*/
export function upperCaseSnakeCase(str: string): string {
- return voca.upperCase(voca.snakeCase(str));
+ return words(str).join('_').toUpperCase();
}
/**
@@ -29,7 +147,11 @@ export function upperCaseSnakeCase(str: string): string {
* @returns The transformed string.
*/
export function camelCase(str: string): string {
- return voca.camelCase(str);
+ const parts = words(str);
+ if (!parts.length) {
+ return '';
+ }
+ return parts[0].toLowerCase() + parts.slice(1).map(capitalizeWord).join('');
}
/**
@@ -38,7 +160,7 @@ export function camelCase(str: string): string {
* @returns The transformed string.
*/
export function titleFormat(str: string): string {
- return voca.titleCase(voca.words(str).join(' '));
+ return words(str).map(capitalizeWord).join(' ');
}
/**
@@ -47,10 +169,7 @@ export function titleFormat(str: string): string {
* @returns The transformed string.
*/
export function pascalCase(str: string): string {
- return voca
- .words(str)
- .map(word => voca.capitalize(word))
- .join('');
+ return words(str).map(capitalize).join('');
}
/**
@@ -59,7 +178,17 @@ export function pascalCase(str: string): string {
* @returns The transformed string.
*/
export function kebabCase(str: string): string {
- return voca.kebabCase(str);
+ return words(str).join('-').toLowerCase();
+}
+
+/**
+ * Uppercase the first character after each word boundary, leaving separators intact.
+ * E.g. `content-type` → `Content-Type`, `if-match` → `If-Match`.
+ * @param str - The string to transform.
+ * @returns The string with each word's first character uppercased.
+ */
+export function titleCase(str: string): string {
+ return str.replace(/\b\w/g, c => c.toUpperCase());
}
/**
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 36ffe92679..4b10ca266a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -321,18 +321,15 @@ importers:
'@sap/xssec':
specifier: ^4.13.0
version: 4.13.0
- async-retry:
- specifier: ^1.3.3
- version: 1.3.3
axios:
specifier: ^1.15.0
version: 1.16.0
jks-js:
specifier: ^1.1.6
version: 1.1.7
- jsonwebtoken:
- specifier: ^9.0.3
- version: 9.0.3
+ jwt-decode:
+ specifier: ^4.0.0
+ version: 4.0.0
safe-stable-stringify:
specifier: ^2.5.0
version: 2.5.0
@@ -412,24 +409,15 @@ importers:
'@sap-cloud-sdk/util':
specifier: workspace:^
version: link:../util
- '@types/fs-extra':
- specifier: ^11.0.4
- version: 11.0.4
fast-xml-parser:
specifier: ^5.5.9
version: 5.7.3
- fs-extra:
- specifier: ^11.3.4
- version: 11.3.5
ts-morph:
specifier: ^28.0.0
version: 28.0.0
typescript:
specifier: ~5.9.3
version: 5.9.3
- voca:
- specifier: ^1.4.1
- version: 1.4.1
winston:
specifier: ^3.19.0
version: 3.19.0
@@ -461,18 +449,12 @@ importers:
fast-levenshtein:
specifier: ~3.0.0
version: 3.0.0
- fs-extra:
- specifier: ^11.3.4
- version: 11.3.5
glob:
specifier: ^13.0.6
version: 13.0.6
prettier:
specifier: ^3.8.1
version: 3.8.3
- voca:
- specifier: ^1.4.1
- version: 1.4.1
yargs:
specifier: ^17.7.2
version: 17.7.2
@@ -550,9 +532,6 @@ importers:
moment:
specifier: ^2.30.1
version: 2.30.1
- voca:
- specifier: ^1.4.1
- version: 1.4.1
devDependencies:
'@sap-cloud-sdk/test-services-odata-common':
specifier: workspace:^
@@ -825,9 +804,6 @@ importers:
logform:
specifier: ^2.7.0
version: 2.7.0
- voca:
- specifier: ^1.4.1
- version: 1.4.1
winston:
specifier: ^3.19.0
version: 3.19.0
@@ -2210,9 +2186,6 @@ packages:
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
- '@types/fs-extra@11.0.4':
- resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
-
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
@@ -2240,9 +2213,6 @@ packages:
'@types/json5@0.0.29':
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
- '@types/jsonfile@6.1.4':
- resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
-
'@types/jsonwebtoken@9.0.10':
resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==}
@@ -6081,9 +6051,6 @@ packages:
resolution: {integrity: sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==}
engines: {node: '>=4.0'}
- voca@1.4.1:
- resolution: {integrity: sha512-NJC/BzESaHT1p4B5k4JykxedeltmNbau4cummStd4RjFojgq/kLew5TzYge9N2geeWyI2w8T30wUET5v+F7ZHA==}
-
walkdir@0.4.1:
resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==}
engines: {node: '>=6.0.0'}
@@ -7573,11 +7540,6 @@ snapshots:
'@types/estree@1.0.8': {}
- '@types/fs-extra@11.0.4':
- dependencies:
- '@types/jsonfile': 6.1.4
- '@types/node': 22.19.15
-
'@types/hast@3.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -7608,10 +7570,6 @@ snapshots:
'@types/json5@0.0.29':
optional: true
- '@types/jsonfile@6.1.4':
- dependencies:
- '@types/node': 22.19.15
-
'@types/jsonwebtoken@9.0.10':
dependencies:
'@types/ms': 2.1.0
@@ -12126,8 +12084,6 @@ snapshots:
ini: 1.3.8
js-git: 0.7.8
- voca@1.4.1: {}
-
walkdir@0.4.1: {}
walker@1.0.8: