path-to-regexp
Advanced tools
+5
-0
@@ -0,1 +1,6 @@ | ||
| 1.7.0 / 2016-11-08 | ||
| ================== | ||
| * Allow a `delimiter` option to be passed in with `tokensToRegExp` which will be used for "non-ending" token match situations | ||
| 1.6.0 / 2016-10-03 | ||
@@ -2,0 +7,0 @@ ================== |
+14
-11
@@ -1,10 +0,4 @@ | ||
| declare function pathToRegexp (path: pathToRegexp.Path, options?: pathToRegexp.RegExpOptions & pathToRegexp.ParseOptions): pathToRegexp.PathRegExp; | ||
| declare function pathToRegexp (path: pathToRegexp.Path, keys?: pathToRegexp.Key[], options?: pathToRegexp.RegExpOptions & pathToRegexp.ParseOptions): pathToRegexp.PathRegExp; | ||
| declare function pathToRegexp (path: pathToRegexp.Path, keys?: pathToRegexp.Key[], options?: pathToRegexp.RegExpOptions & pathToRegexp.ParseOptions): RegExp; | ||
| declare namespace pathToRegexp { | ||
| export interface PathRegExp extends RegExp { | ||
| // An array to be populated with the keys found in the path. | ||
| keys: Key[]; | ||
| } | ||
| export interface RegExpOptions { | ||
@@ -27,2 +21,6 @@ /** | ||
| delimiter?: string; | ||
| /** | ||
| * List of characters that can also be "end" characters. | ||
| */ | ||
| endsWith?: string | string[]; | ||
| } | ||
@@ -35,2 +33,6 @@ | ||
| delimiter?: string; | ||
| /** | ||
| * List of valid delimiter characters. (default: `'./'`) | ||
| */ | ||
| delimiters?: string | string[]; | ||
| } | ||
@@ -56,4 +58,3 @@ | ||
| */ | ||
| export function tokensToRegExp (tokens: Token[], options?: RegExpOptions): PathRegExp; | ||
| export function tokensToRegExp (tokens: Token[], keys?: Key[], options?: RegExpOptions): PathRegExp; | ||
| export function tokensToRegExp (tokens: Token[], keys?: Key[], options?: RegExpOptions): RegExp; | ||
@@ -68,7 +69,9 @@ export interface Key { | ||
| partial: boolean; | ||
| asterisk: boolean; | ||
| } | ||
| interface PathFunctionOptions { | ||
| pretty?: boolean; | ||
| /** | ||
| * Function for encoding input strings for output. | ||
| */ | ||
| encode?: (value: string) => string; | ||
| } | ||
@@ -75,0 +78,0 @@ |
+83
-139
@@ -1,3 +0,1 @@ | ||
| var isarray = require('isarray') | ||
| /** | ||
@@ -24,6 +22,5 @@ * Expose `pathToRegexp`. | ||
| // | ||
| // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined] | ||
| // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined] | ||
| // "/*" => ["/", undefined, undefined, undefined, undefined, "*"] | ||
| '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))' | ||
| // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?"] | ||
| // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined] | ||
| '(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?' | ||
| ].join('|'), 'g') | ||
@@ -43,6 +40,8 @@ | ||
| var path = '' | ||
| var defaultDelimiter = options && options.delimiter || '/' | ||
| var defaultDelimiter = (options && options.delimiter) || '/' | ||
| var delimiters = (options && options.delimiters) || './' | ||
| var pathEscaped = false | ||
| var res | ||
| while ((res = PATH_REGEXP.exec(str)) != null) { | ||
| while ((res = PATH_REGEXP.exec(str)) !== null) { | ||
| var m = res[0] | ||
@@ -57,13 +56,22 @@ var escaped = res[1] | ||
| path += escaped[1] | ||
| pathEscaped = true | ||
| continue | ||
| } | ||
| var prev = '' | ||
| var next = str[index] | ||
| var prefix = res[2] | ||
| var name = res[3] | ||
| var capture = res[4] | ||
| var group = res[5] | ||
| var modifier = res[6] | ||
| var asterisk = res[7] | ||
| var name = res[2] | ||
| var capture = res[3] | ||
| var group = res[4] | ||
| var modifier = res[5] | ||
| if (!pathEscaped && path.length) { | ||
| var k = path.length - 1 | ||
| if (delimiters.indexOf(path[k]) > -1) { | ||
| prev = path[k] | ||
| path = path.slice(0, k) | ||
| } | ||
| } | ||
| // Push the current path onto the tokens. | ||
@@ -73,8 +81,9 @@ if (path) { | ||
| path = '' | ||
| pathEscaped = false | ||
| } | ||
| var partial = prefix != null && next != null && next !== prefix | ||
| var partial = prev !== '' && next !== undefined && next !== prev | ||
| var repeat = modifier === '+' || modifier === '*' | ||
| var optional = modifier === '?' || modifier === '*' | ||
| var delimiter = res[2] || defaultDelimiter | ||
| var delimiter = prev || defaultDelimiter | ||
| var pattern = capture || group | ||
@@ -84,3 +93,3 @@ | ||
| name: name || key++, | ||
| prefix: prefix || '', | ||
| prefix: prev, | ||
| delimiter: delimiter, | ||
@@ -90,17 +99,11 @@ optional: optional, | ||
| partial: partial, | ||
| asterisk: !!asterisk, | ||
| pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?') | ||
| pattern: pattern ? escapeGroup(pattern) : '[^' + escapeString(delimiter) + ']+?' | ||
| }) | ||
| } | ||
| // Match any characters still remaining. | ||
| if (index < str.length) { | ||
| path += str.substr(index) | ||
| // Push any remaining characters. | ||
| if (path || index < str.length) { | ||
| tokens.push(path + str.substr(index)) | ||
| } | ||
| // If the path exists, push it onto the end. | ||
| if (path) { | ||
| tokens.push(path) | ||
| } | ||
| return tokens | ||
@@ -121,26 +124,2 @@ } | ||
| /** | ||
| * Prettier encoding of URI path segments. | ||
| * | ||
| * @param {string} | ||
| * @return {string} | ||
| */ | ||
| function encodeURIComponentPretty (str) { | ||
| return encodeURI(str).replace(/[\/?#]/g, function (c) { | ||
| return '%' + c.charCodeAt(0).toString(16).toUpperCase() | ||
| }) | ||
| } | ||
| /** | ||
| * Encode the asterisk parameter. Similar to `pretty`, but allows slashes. | ||
| * | ||
| * @param {string} | ||
| * @return {string} | ||
| */ | ||
| function encodeAsterisk (str) { | ||
| return encodeURI(str).replace(/[?#]/g, function (c) { | ||
| return '%' + c.charCodeAt(0).toString(16).toUpperCase() | ||
| }) | ||
| } | ||
| /** | ||
| * Expose a method for transforming tokens into the path function. | ||
@@ -159,7 +138,5 @@ */ | ||
| return function (obj, opts) { | ||
| return function (data, options) { | ||
| var path = '' | ||
| var data = obj || {} | ||
| var options = opts || {} | ||
| var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent | ||
| var encode = (options && options.encode) || encodeURIComponent | ||
@@ -171,33 +148,17 @@ for (var i = 0; i < tokens.length; i++) { | ||
| path += token | ||
| continue | ||
| } | ||
| var value = data[token.name] | ||
| var value = data ? data[token.name] : undefined | ||
| var segment | ||
| if (value == null) { | ||
| if (token.optional) { | ||
| // Prepend partial segment prefixes. | ||
| if (token.partial) { | ||
| path += token.prefix | ||
| } | ||
| continue | ||
| } else { | ||
| throw new TypeError('Expected "' + token.name + '" to be defined') | ||
| } | ||
| } | ||
| if (isarray(value)) { | ||
| if (Array.isArray(value)) { | ||
| if (!token.repeat) { | ||
| throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') | ||
| throw new TypeError('Expected "' + token.name + '" to not repeat, but got array') | ||
| } | ||
| if (value.length === 0) { | ||
| if (token.optional) { | ||
| continue | ||
| } else { | ||
| throw new TypeError('Expected "' + token.name + '" to not be empty') | ||
| } | ||
| if (token.optional) continue | ||
| throw new TypeError('Expected "' + token.name + '" to not be empty') | ||
| } | ||
@@ -209,3 +170,3 @@ | ||
| if (!matches[i].test(segment)) { | ||
| throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`') | ||
| throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '"') | ||
| } | ||
@@ -219,9 +180,21 @@ | ||
| segment = token.asterisk ? encodeAsterisk(value) : encode(value) | ||
| if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { | ||
| segment = encode(String(value)) | ||
| if (!matches[i].test(segment)) { | ||
| throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') | ||
| if (!matches[i].test(segment)) { | ||
| throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but got "' + segment + '"') | ||
| } | ||
| path += token.prefix + segment | ||
| continue | ||
| } | ||
| path += token.prefix + segment | ||
| if (token.optional) { | ||
| // Prepend partial segment prefixes. | ||
| if (token.partial) path += token.prefix | ||
| continue | ||
| } | ||
| throw new TypeError('Expected "' + token.name + '" to be ' + (token.repeat ? 'an array' : 'a string')) | ||
| } | ||
@@ -240,3 +213,3 @@ | ||
| function escapeString (str) { | ||
| return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1') | ||
| return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1') | ||
| } | ||
@@ -251,18 +224,6 @@ | ||
| function escapeGroup (group) { | ||
| return group.replace(/([=!:$\/()])/g, '\\$1') | ||
| return group.replace(/([=!:$/()])/g, '\\$1') | ||
| } | ||
| /** | ||
| * Attach the keys as a property of the regexp. | ||
| * | ||
| * @param {!RegExp} re | ||
| * @param {Array} keys | ||
| * @return {!RegExp} | ||
| */ | ||
| function attachKeys (re, keys) { | ||
| re.keys = keys | ||
| return re | ||
| } | ||
| /** | ||
| * Get the flags for a regexp from the options. | ||
@@ -274,3 +235,3 @@ * | ||
| function flags (options) { | ||
| return options.sensitive ? '' : 'i' | ||
| return options && options.sensitive ? '' : 'i' | ||
| } | ||
@@ -282,6 +243,8 @@ | ||
| * @param {!RegExp} path | ||
| * @param {!Array} keys | ||
| * @param {Array=} keys | ||
| * @return {!RegExp} | ||
| */ | ||
| function regexpToRegexp (path, keys) { | ||
| if (!keys) return path | ||
| // Use a negative lookahead to match only capturing groups. | ||
@@ -299,3 +262,2 @@ var groups = path.source.match(/\((?!\?)/g) | ||
| partial: false, | ||
| asterisk: false, | ||
| pattern: null | ||
@@ -306,3 +268,3 @@ }) | ||
| return attachKeys(path, keys) | ||
| return path | ||
| } | ||
@@ -314,4 +276,4 @@ | ||
| * @param {!Array} path | ||
| * @param {Array} keys | ||
| * @param {!Object} options | ||
| * @param {Array=} keys | ||
| * @param {Object=} options | ||
| * @return {!RegExp} | ||
@@ -326,5 +288,3 @@ */ | ||
| var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options)) | ||
| return attachKeys(regexp, keys) | ||
| return new RegExp('(?:' + parts.join('|') + ')', flags(options)) | ||
| } | ||
@@ -336,4 +296,4 @@ | ||
| * @param {string} path | ||
| * @param {!Array} keys | ||
| * @param {!Object} options | ||
| * @param {Array=} keys | ||
| * @param {Object=} options | ||
| * @return {!RegExp} | ||
@@ -348,13 +308,8 @@ */ | ||
| * | ||
| * @param {!Array} tokens | ||
| * @param {(Array|Object)=} keys | ||
| * @param {Object=} options | ||
| * @param {!Array} tokens | ||
| * @param {Array=} keys | ||
| * @param {Object=} options | ||
| * @return {!RegExp} | ||
| */ | ||
| function tokensToRegExp (tokens, keys, options) { | ||
| if (!isarray(keys)) { | ||
| options = /** @type {!Object} */ (keys || options) | ||
| keys = [] | ||
| } | ||
| options = options || {} | ||
@@ -364,2 +319,4 @@ | ||
| var end = options.end !== false | ||
| var delimiter = escapeString(options.delimiter || '/') | ||
| var endsWith = [].concat(options.endsWith || []).map(escapeString).concat('$').join('|') | ||
| var route = '' | ||
@@ -377,3 +334,3 @@ | ||
| keys.push(token) | ||
| if (keys) keys.push(token) | ||
@@ -398,22 +355,16 @@ if (token.repeat) { | ||
| var delimiter = escapeString(options.delimiter || '/') | ||
| var endsWithDelimiter = route.slice(-delimiter.length) === delimiter | ||
| // In non-strict mode we allow a slash at the end of match. If the path to | ||
| // match already ends with a slash, we remove it for consistency. The slash | ||
| // is valid at the end of a path match, not in the middle. This is important | ||
| // in non-ending mode, where "/test/" shouldn't match "/test//route". | ||
| // In non-strict mode we allow a delimiter at the end of a match. | ||
| if (!strict) { | ||
| route = (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) + '(?:' + delimiter + '(?=$))?' | ||
| route += '(?:' + delimiter + '(?=' + endsWith + '))?' | ||
| } | ||
| if (end) { | ||
| route += '$' | ||
| route += endsWith === '$' ? endsWith : '(?=' + endsWith + ')' | ||
| } else { | ||
| // In non-ending mode, we need the capturing groups to match as much as | ||
| // possible by using a positive lookahead to the end or next path segment. | ||
| route += strict && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)' | ||
| route += '(?=' + delimiter + '|' + endsWith + ')' | ||
| } | ||
| return attachKeys(new RegExp('^' + route, flags(options)), keys) | ||
| return new RegExp('^' + route, flags(options)) | ||
| } | ||
@@ -429,3 +380,3 @@ | ||
| * @param {(string|RegExp|Array)} path | ||
| * @param {(Array|Object)=} keys | ||
| * @param {Array=} keys | ||
| * @param {Object=} options | ||
@@ -435,18 +386,11 @@ * @return {!RegExp} | ||
| function pathToRegexp (path, keys, options) { | ||
| if (!isarray(keys)) { | ||
| options = /** @type {!Object} */ (keys || options) | ||
| keys = [] | ||
| } | ||
| options = options || {} | ||
| if (path instanceof RegExp) { | ||
| return regexpToRegexp(path, /** @type {!Array} */ (keys)) | ||
| return regexpToRegexp(path, keys) | ||
| } | ||
| if (isarray(path)) { | ||
| return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options) | ||
| if (Array.isArray(path)) { | ||
| return arrayToRegexp(/** @type {!Array} */ (path), keys, options) | ||
| } | ||
| return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options) | ||
| return stringToRegexp(/** @type {string} */ (path), keys, options) | ||
| } |
+10
-12
| { | ||
| "name": "path-to-regexp", | ||
| "description": "Express style path to RegExp utility", | ||
| "version": "1.7.0", | ||
| "version": "2.0.0", | ||
| "main": "index.js", | ||
@@ -16,3 +16,2 @@ "typings": "index.d.ts", | ||
| "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require ts-node/register -R spec test.ts", | ||
| "prepublish": "typings install", | ||
| "test": "npm run lint && npm run test-cov" | ||
@@ -37,13 +36,12 @@ }, | ||
| "devDependencies": { | ||
| "chai": "^2.3.0", | ||
| "istanbul": "~0.3.0", | ||
| "mocha": "~2.2.4", | ||
| "standard": "~3.7.3", | ||
| "ts-node": "^0.5.5", | ||
| "typescript": "^1.8.7", | ||
| "typings": "^1.0.4" | ||
| }, | ||
| "dependencies": { | ||
| "isarray": "0.0.1" | ||
| "@types/chai": "^4.0.4", | ||
| "@types/mocha": "^2.2.42", | ||
| "@types/node": "^8.0.24", | ||
| "chai": "^4.1.1", | ||
| "istanbul": "^0.4.5", | ||
| "mocha": "^3.5.0", | ||
| "standard": "^10.0.3", | ||
| "ts-node": "^3.3.0", | ||
| "typescript": "^2.4.2" | ||
| } | ||
| } |
+27
-36
| # Path-to-RegExp | ||
| > Turn an Express-style path string such as `/user/:name` into a regular expression. | ||
| > Turn a path string such as `/user/:name` into a regular expression. | ||
@@ -23,3 +23,3 @@ [![NPM version][npm-image]][npm-url] | ||
| // pathToRegexp(path, keys, options) | ||
| // pathToRegexp(path, keys?, options?) | ||
| // pathToRegexp.parse(path) | ||
@@ -29,3 +29,3 @@ // pathToRegexp.compile(path) | ||
| - **path** An Express-style string, an array of strings, or a regular expression. | ||
| - **path** A string, array of strings, or a regular expression. | ||
| - **keys** An array to be populated with the keys found in the path. | ||
@@ -36,3 +36,6 @@ - **options** | ||
| - **end** When `false` the path will match at the beginning. (default: `true`) | ||
| - **delimiter** Set the default delimiter for repeat parameters. (default: `'/'`) | ||
| - Advanced options (use for non-pathname strings, e.g. host names): | ||
| - **delimiter** The default delimiter for segments. (default: `'/'`) | ||
| - **endsWith** Optional character, or list of characters, to treat as "end" characters. | ||
| - **delimiters** List of characters to consider delimiters when parsing. (default: `'./'`) | ||
@@ -46,7 +49,7 @@ ```javascript | ||
| **Please note:** The `RegExp` returned by `path-to-regexp` is intended for use with pathnames or hostnames. It can not handle the query strings or fragments of a URL. | ||
| **Please note:** The `RegExp` returned by `path-to-regexp` is intended for ordered data (e.g. pathnames, hostnames). It does not handle arbitrary data (e.g. query strings, URL fragments, JSON, etc). | ||
| ### Parameters | ||
| The path string can be used to define parameters and populate the keys. | ||
| The path argument is used to define parameters and populate the list of keys. | ||
@@ -58,3 +61,3 @@ #### Named Parameters | ||
| ```js | ||
| var re = pathToRegexp('/:foo/:bar', keys) | ||
| var re = pathToRegexp('/:foo/:bar') | ||
| // keys = [{ name: 'foo', prefix: '/', ... }, { name: 'bar', prefix: '/', ... }] | ||
@@ -69,3 +72,3 @@ | ||
| ```js | ||
| var re = pathToRegexp('/(apple-)?icon-:res(\\d+).png', keys) | ||
| var re = pathToRegexp('/(apple-)?icon-:res(\\d+).png') | ||
| // keys = [{ name: 0, prefix: '/', ... }, { name: 'res', prefix: '', ... }] | ||
@@ -77,10 +80,10 @@ | ||
| #### Modified Parameters | ||
| #### Parameter Modifiers | ||
| ##### Optional | ||
| Parameters can be suffixed with a question mark (`?`) to make the parameter optional. This will also make the prefix optional. | ||
| Parameters can be suffixed with a question mark (`?`) to make the parameter optional. | ||
| ```js | ||
| var re = pathToRegexp('/:foo/:bar?', keys) | ||
| var re = pathToRegexp('/:foo/:bar?') | ||
| // keys = [{ name: 'foo', ... }, { name: 'bar', delimiter: '/', optional: true, repeat: false }] | ||
@@ -95,2 +98,4 @@ | ||
| **Tip:** If the parameter is the _only_ value in the segment, the prefix is also optional. | ||
| ##### Zero or more | ||
@@ -101,3 +106,3 @@ | ||
| ```js | ||
| var re = pathToRegexp('/:foo*', keys) | ||
| var re = pathToRegexp('/:foo*') | ||
| // keys = [{ name: 'foo', delimiter: '/', optional: true, repeat: true }] | ||
@@ -117,3 +122,3 @@ | ||
| ```js | ||
| var re = pathToRegexp('/:foo+', keys) | ||
| var re = pathToRegexp('/:foo+') | ||
| // keys = [{ name: 'foo', delimiter: '/', optional: false, repeat: true }] | ||
@@ -133,3 +138,3 @@ | ||
| ```js | ||
| var re = pathToRegexp('/:foo(\\d+)', keys) | ||
| var re = pathToRegexp('/:foo(\\d+)') | ||
| // keys = [{ name: 'foo', ... }] | ||
@@ -151,3 +156,3 @@ | ||
| ```js | ||
| var re = pathToRegexp('/:foo/(.*)', keys) | ||
| var re = pathToRegexp('/:foo/(.*)') | ||
| // keys = [{ name: 'foo', ... }, { name: 0, ... }] | ||
@@ -159,14 +164,2 @@ | ||
| #### Asterisk | ||
| An asterisk can be used for matching everything. It is equivalent to an unnamed matching group of `(.*)`. | ||
| ```js | ||
| var re = pathToRegexp('/foo/*', keys) | ||
| // keys = [{ name: '0', ... }] | ||
| re.exec('/foo/bar/baz') | ||
| //=> ['/foo/bar/baz', 'bar/baz'] | ||
| ``` | ||
| ### Parse | ||
@@ -189,7 +182,7 @@ | ||
| **Note:** This method only works with Express-style strings. | ||
| **Note:** This method only works with strings. | ||
| ### Compile ("Reverse" Path-To-RegExp) | ||
| Path-To-RegExp exposes a compile function for transforming an Express-style path into a valid path. | ||
| Path-To-RegExp exposes a compile function for transforming a string into a valid path. | ||
@@ -203,4 +196,4 @@ ```js | ||
| toPath({ id: ':' }) //=> "/user/%3A" | ||
| toPath({ id: ':' }, { pretty: true }) //=> "/user/:" | ||
| toPath({ id: ':/' }) //=> "/user/%3A%2F" | ||
| toPath({ id: ':/' }, { encode: (x) => x }) //=> "/user/:/" | ||
@@ -237,3 +230,2 @@ var toPathRepeated = pathToRegexp.compile('/:segment+') | ||
| * `pattern` The RegExp used to match this token (`string`) | ||
| * `asterisk` Indicates the token is an `*` match (`boolean`) | ||
@@ -244,7 +236,6 @@ ## Compatibility with Express <= 4.x | ||
| * No longer a direct conversion to a RegExp with sugar on top - it's a path matcher with named and unnamed matching groups | ||
| * It's unlikely you previously abused this feature, it's rare and you could always use a RegExp instead | ||
| * All matching RegExp special characters can be used in a matching group. E.g. `/:user(.*)` | ||
| * Other RegExp features are not support - no nested matching groups, non-capturing groups or look aheads | ||
| * RegExp special characters can only be used in a parameter | ||
| * Express.js 4.x used all `RegExp` special characters regardless of position - this considered a bug | ||
| * Parameters have suffixes that augment meaning - `*`, `+` and `?`. E.g. `/:user*` | ||
| * No wildcard asterisk (`*`) - use parameters instead (`(.*)`) | ||
@@ -251,0 +242,0 @@ ## TypeScript |
0
-100%25584
-6.22%9
28.57%382
-10.75%249
-3.49%- Removed
- Removed