From db8a40917b2eaced5c676896e851a89e735f3dff Mon Sep 17 00:00:00 2001
From: Wojciech Kowasz <46456945+mastahdocent@users.noreply.github.com>
Date: Thu, 15 Jun 2023 13:42:11 +0200
Subject: [PATCH] Bump TypeScript to 4.9.5 and fix types (#215)
---
dist/universal-router-sync.js.map | 2 +-
dist/universal-router-sync.min.js.map | 2 +-
dist/universal-router.js.map | 2 +-
dist/universal-router.min.js.map | 2 +-
package-lock.json | 12 +++++------
package.json | 3 ++-
src/UniversalRouter.test.ts | 27 ++++++++++++-----------
src/UniversalRouter.ts | 2 +-
src/UniversalRouterSync.test.ts | 31 ++++++++++++++-------------
src/UniversalRouterSync.ts | 4 ++--
10 files changed, 45 insertions(+), 42 deletions(-)
diff --git a/dist/universal-router-sync.js.map b/dist/universal-router-sync.js.map
index 82a655a..01b56a4 100644
--- a/dist/universal-router-sync.js.map
+++ b/dist/universal-router-sync.js.map
@@ -1 +1 @@
-{"version":3,"file":"universal-router-sync.js","sources":["../node_modules/path-to-regexp/src/index.ts","src/UniversalRouterSync.ts"],"sourcesContent":["/**\n * Tokenizer results.\n */\ninterface LexToken {\n type:\n | \"OPEN\"\n | \"CLOSE\"\n | \"PATTERN\"\n | \"NAME\"\n | \"CHAR\"\n | \"ESCAPED_CHAR\"\n | \"MODIFIER\"\n | \"END\";\n index: number;\n value: string;\n}\n\n/**\n * Tokenize input string.\n */\nfunction lexer(str: string): LexToken[] {\n const tokens: LexToken[] = [];\n let i = 0;\n\n while (i < str.length) {\n const char = str[i];\n\n if (char === \"*\" || char === \"+\" || char === \"?\") {\n tokens.push({ type: \"MODIFIER\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"\\\\\") {\n tokens.push({ type: \"ESCAPED_CHAR\", index: i++, value: str[i++] });\n continue;\n }\n\n if (char === \"{\") {\n tokens.push({ type: \"OPEN\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"}\") {\n tokens.push({ type: \"CLOSE\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \":\") {\n let name = \"\";\n let j = i + 1;\n\n while (j < str.length) {\n const code = str.charCodeAt(j);\n\n if (\n // `0-9`\n (code >= 48 && code <= 57) ||\n // `A-Z`\n (code >= 65 && code <= 90) ||\n // `a-z`\n (code >= 97 && code <= 122) ||\n // `_`\n code === 95\n ) {\n name += str[j++];\n continue;\n }\n\n break;\n }\n\n if (!name) throw new TypeError(`Missing parameter name at ${i}`);\n\n tokens.push({ type: \"NAME\", index: i, value: name });\n i = j;\n continue;\n }\n\n if (char === \"(\") {\n let count = 1;\n let pattern = \"\";\n let j = i + 1;\n\n if (str[j] === \"?\") {\n throw new TypeError(`Pattern cannot start with \"?\" at ${j}`);\n }\n\n while (j < str.length) {\n if (str[j] === \"\\\\\") {\n pattern += str[j++] + str[j++];\n continue;\n }\n\n if (str[j] === \")\") {\n count--;\n if (count === 0) {\n j++;\n break;\n }\n } else if (str[j] === \"(\") {\n count++;\n if (str[j + 1] !== \"?\") {\n throw new TypeError(`Capturing groups are not allowed at ${j}`);\n }\n }\n\n pattern += str[j++];\n }\n\n if (count) throw new TypeError(`Unbalanced pattern at ${i}`);\n if (!pattern) throw new TypeError(`Missing pattern at ${i}`);\n\n tokens.push({ type: \"PATTERN\", index: i, value: pattern });\n i = j;\n continue;\n }\n\n tokens.push({ type: \"CHAR\", index: i, value: str[i++] });\n }\n\n tokens.push({ type: \"END\", index: i, value: \"\" });\n\n return tokens;\n}\n\nexport interface ParseOptions {\n /**\n * Set the default delimiter for repeat parameters. (default: `'/'`)\n */\n delimiter?: string;\n /**\n * List of characters to automatically consider prefixes when parsing.\n */\n prefixes?: string;\n}\n\n/**\n * Parse a string for the raw tokens.\n */\nexport function parse(str: string, options: ParseOptions = {}): Token[] {\n const tokens = lexer(str);\n const { prefixes = \"./\" } = options;\n const defaultPattern = `[^${escapeString(options.delimiter || \"/#?\")}]+?`;\n const result: Token[] = [];\n let key = 0;\n let i = 0;\n let path = \"\";\n\n const tryConsume = (type: LexToken[\"type\"]): string | undefined => {\n if (i < tokens.length && tokens[i].type === type) return tokens[i++].value;\n };\n\n const mustConsume = (type: LexToken[\"type\"]): string => {\n const value = tryConsume(type);\n if (value !== undefined) return value;\n const { type: nextType, index } = tokens[i];\n throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}`);\n };\n\n const consumeText = (): string => {\n let result = \"\";\n let value: string | undefined;\n // tslint:disable-next-line\n while ((value = tryConsume(\"CHAR\") || tryConsume(\"ESCAPED_CHAR\"))) {\n result += value;\n }\n return result;\n };\n\n while (i < tokens.length) {\n const char = tryConsume(\"CHAR\");\n const name = tryConsume(\"NAME\");\n const pattern = tryConsume(\"PATTERN\");\n\n if (name || pattern) {\n let prefix = char || \"\";\n\n if (prefixes.indexOf(prefix) === -1) {\n path += prefix;\n prefix = \"\";\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n result.push({\n name: name || key++,\n prefix,\n suffix: \"\",\n pattern: pattern || defaultPattern,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n const value = char || tryConsume(\"ESCAPED_CHAR\");\n if (value) {\n path += value;\n continue;\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n const open = tryConsume(\"OPEN\");\n if (open) {\n const prefix = consumeText();\n const name = tryConsume(\"NAME\") || \"\";\n const pattern = tryConsume(\"PATTERN\") || \"\";\n const suffix = consumeText();\n\n mustConsume(\"CLOSE\");\n\n result.push({\n name: name || (pattern ? key++ : \"\"),\n pattern: name && !pattern ? defaultPattern : pattern,\n prefix,\n suffix,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n mustConsume(\"END\");\n }\n\n return result;\n}\n\nexport interface TokensToFunctionOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * Function for encoding input strings for output.\n */\n encode?: (value: string, token: Key) => string;\n /**\n * When `false` the function can produce an invalid (unmatched) path. (default: `true`)\n */\n validate?: boolean;\n}\n\n/**\n * Compile a string to a template function for the path.\n */\nexport function compile
(\n str: string,\n options?: ParseOptions & TokensToFunctionOptions\n) {\n return tokensToFunction
(parse(str, options), options);\n}\n\nexport type PathFunction
= (data?: P) => string;\n\n/**\n * Expose a method for transforming tokens into the path function.\n */\nexport function tokensToFunction
(\n tokens: Token[],\n options: TokensToFunctionOptions = {}\n): PathFunction
{\n const reFlags = flags(options);\n const { encode = (x: string) => x, validate = true } = options;\n\n // Compile all the tokens into regexps.\n const matches = tokens.map(token => {\n if (typeof token === \"object\") {\n return new RegExp(`^(?:${token.pattern})$`, reFlags);\n }\n });\n\n return (data: Record | null | undefined) => {\n let path = \"\";\n\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n\n if (typeof token === \"string\") {\n path += token;\n continue;\n }\n\n const value = data ? data[token.name] : undefined;\n const optional = token.modifier === \"?\" || token.modifier === \"*\";\n const repeat = token.modifier === \"*\" || token.modifier === \"+\";\n\n if (Array.isArray(value)) {\n if (!repeat) {\n throw new TypeError(\n `Expected \"${token.name}\" to not repeat, but got an array`\n );\n }\n\n if (value.length === 0) {\n if (optional) continue;\n\n throw new TypeError(`Expected \"${token.name}\" to not be empty`);\n }\n\n for (let j = 0; j < value.length; j++) {\n const segment = encode(value[j], token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected all \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n }\n\n continue;\n }\n\n if (typeof value === \"string\" || typeof value === \"number\") {\n const segment = encode(String(value), token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n continue;\n }\n\n if (optional) continue;\n\n const typeOfMessage = repeat ? \"an array\" : \"a string\";\n throw new TypeError(`Expected \"${token.name}\" to be ${typeOfMessage}`);\n }\n\n return path;\n };\n}\n\nexport interface RegexpToFunctionOptions {\n /**\n * Function for decoding strings for params.\n */\n decode?: (value: string, token: Key) => string;\n}\n\n/**\n * A match result contains data about the path match.\n */\nexport interface MatchResult {\n path: string;\n index: number;\n params: P;\n}\n\n/**\n * A match is either `false` (no match) or a match result.\n */\nexport type Match
= false | MatchResult
;\n\n/**\n * The match function takes a string and returns whether it matched the path.\n */\nexport type MatchFunction
= (\n path: string\n) => Match
;\n\n/**\n * Create path match function from `path-to-regexp` spec.\n */\nexport function match
(\n str: Path,\n options?: ParseOptions & TokensToRegexpOptions & RegexpToFunctionOptions\n) {\n const keys: Key[] = [];\n const re = pathToRegexp(str, keys, options);\n return regexpToFunction
(re, keys, options);\n}\n\n/**\n * Create a path match function from `path-to-regexp` output.\n */\nexport function regexpToFunction
(\n re: RegExp,\n keys: Key[],\n options: RegexpToFunctionOptions = {}\n): MatchFunction
{\n const { decode = (x: string) => x } = options;\n\n return function(pathname: string) {\n const m = re.exec(pathname);\n if (!m) return false;\n\n const { 0: path, index } = m;\n const params = Object.create(null);\n\n for (let i = 1; i < m.length; i++) {\n // tslint:disable-next-line\n if (m[i] === undefined) continue;\n\n const key = keys[i - 1];\n\n if (key.modifier === \"*\" || key.modifier === \"+\") {\n params[key.name] = m[i].split(key.prefix + key.suffix).map(value => {\n return decode(value, key);\n });\n } else {\n params[key.name] = decode(m[i], key);\n }\n }\n\n return { path, index, params };\n };\n}\n\n/**\n * Escape a regular expression string.\n */\nfunction escapeString(str: string) {\n return str.replace(/([.+*?=^!:${}()[\\]|/\\\\])/g, \"\\\\$1\");\n}\n\n/**\n * Get the flags for a regexp from the options.\n */\nfunction flags(options?: { sensitive?: boolean }) {\n return options && options.sensitive ? \"\" : \"i\";\n}\n\n/**\n * Metadata about a key.\n */\nexport interface Key {\n name: string | number;\n prefix: string;\n suffix: string;\n pattern: string;\n modifier: string;\n}\n\n/**\n * A token is a string (nothing special) or key metadata (capture group).\n */\nexport type Token = string | Key;\n\n/**\n * Pull out keys from a regexp.\n */\nfunction regexpToRegexp(path: RegExp, keys?: Key[]): RegExp {\n if (!keys) return path;\n\n const groupsRegex = /\\((?:\\?<(.*?)>)?(?!\\?)/g;\n\n let index = 0;\n let execResult = groupsRegex.exec(path.source);\n while (execResult) {\n keys.push({\n // Use parenthesized substring match if available, index otherwise\n name: execResult[1] || index++,\n prefix: \"\",\n suffix: \"\",\n modifier: \"\",\n pattern: \"\"\n });\n execResult = groupsRegex.exec(path.source);\n }\n\n return path;\n}\n\n/**\n * Transform an array into a regexp.\n */\nfunction arrayToRegexp(\n paths: Array,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n): RegExp {\n const parts = paths.map(path => pathToRegexp(path, keys, options).source);\n return new RegExp(`(?:${parts.join(\"|\")})`, flags(options));\n}\n\n/**\n * Create a path regexp from string input.\n */\nfunction stringToRegexp(\n path: string,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n return tokensToRegexp(parse(path, options), keys, options);\n}\n\nexport interface TokensToRegexpOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * When `true` the regexp won't allow an optional trailing delimiter to match. (default: `false`)\n */\n strict?: boolean;\n /**\n * When `true` the regexp will match to the end of the string. (default: `true`)\n */\n end?: boolean;\n /**\n * When `true` the regexp will match from the beginning of the string. (default: `true`)\n */\n start?: boolean;\n /**\n * Sets the final character for non-ending optimistic matches. (default: `/`)\n */\n delimiter?: string;\n /**\n * List of characters that can also be \"end\" characters.\n */\n endsWith?: string;\n /**\n * Encode path tokens for use in the `RegExp`.\n */\n encode?: (value: string) => string;\n}\n\n/**\n * Expose a function for taking tokens and returning a RegExp.\n */\nexport function tokensToRegexp(\n tokens: Token[],\n keys?: Key[],\n options: TokensToRegexpOptions = {}\n) {\n const {\n strict = false,\n start = true,\n end = true,\n encode = (x: string) => x\n } = options;\n const endsWith = `[${escapeString(options.endsWith || \"\")}]|$`;\n const delimiter = `[${escapeString(options.delimiter || \"/#?\")}]`;\n let route = start ? \"^\" : \"\";\n\n // Iterate over the tokens and create our regexp string.\n for (const token of tokens) {\n if (typeof token === \"string\") {\n route += escapeString(encode(token));\n } else {\n const prefix = escapeString(encode(token.prefix));\n const suffix = escapeString(encode(token.suffix));\n\n if (token.pattern) {\n if (keys) keys.push(token);\n\n if (prefix || suffix) {\n if (token.modifier === \"+\" || token.modifier === \"*\") {\n const mod = token.modifier === \"*\" ? \"?\" : \"\";\n route += `(?:${prefix}((?:${token.pattern})(?:${suffix}${prefix}(?:${token.pattern}))*)${suffix})${mod}`;\n } else {\n route += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`;\n }\n } else {\n route += `(${token.pattern})${token.modifier}`;\n }\n } else {\n route += `(?:${prefix}${suffix})${token.modifier}`;\n }\n }\n }\n\n if (end) {\n if (!strict) route += `${delimiter}?`;\n\n route += !options.endsWith ? \"$\" : `(?=${endsWith})`;\n } else {\n const endToken = tokens[tokens.length - 1];\n const isEndDelimited =\n typeof endToken === \"string\"\n ? delimiter.indexOf(endToken[endToken.length - 1]) > -1\n : // tslint:disable-next-line\n endToken === undefined;\n\n if (!strict) {\n route += `(?:${delimiter}(?=${endsWith}))?`;\n }\n\n if (!isEndDelimited) {\n route += `(?=${delimiter}|${endsWith})`;\n }\n }\n\n return new RegExp(route, flags(options));\n}\n\n/**\n * Supported `path-to-regexp` input types.\n */\nexport type Path = string | RegExp | Array;\n\n/**\n * Normalize the given path string, returning a regular expression.\n *\n * An empty array can be passed in for the keys, which will hold the\n * placeholder key descriptions. For example, using `/user/:id`, `keys` will\n * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.\n */\nexport function pathToRegexp(\n path: Path,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n if (path instanceof RegExp) return regexpToRegexp(path, keys);\n if (Array.isArray(path)) return arrayToRegexp(path, keys, options);\n return stringToRegexp(path, keys, options);\n}\n","/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE.txt file in the root directory of this source tree.\n */\n\nimport {\n match,\n Path,\n Match,\n MatchFunction,\n ParseOptions,\n TokensToRegexpOptions,\n RegexpToFunctionOptions,\n} from 'path-to-regexp'\n\n/**\n * In addition to a URL path string, any arbitrary data can be passed to\n * the `router.resolve()` method, that becomes available inside action functions.\n */\nexport interface RouterContext {\n [propName: string]: any\n}\n\nexport interface ResolveContext extends RouterContext {\n /**\n * URL which was transmitted to `router.resolve()`.\n */\n pathname: string\n}\n\n/**\n * Params is a key/value object that represents extracted URL parameters.\n */\nexport interface RouteParams {\n [paramName: string]: string | string[]\n}\n\nexport type RouteResultSync = T | null | undefined\n\nexport interface RouteContext\n extends ResolveContext {\n /**\n * Current router instance.\n */\n router: UniversalRouterSync\n /**\n * Matched route object.\n */\n route: Route\n /**\n * Base URL path relative to the path of the current route.\n */\n baseUrl: string\n /**\n * Matched path.\n */\n path: string\n /**\n * Matched path params.\n */\n params: RouteParams\n /**\n * Middleware style function which can continue resolving.\n */\n next: (resume?: boolean) => RouteResultSync\n}\n\n/**\n * A Route is a singular route in your application. It contains a path, an\n * action function, and optional children which are an array of Route.\n * @template C User context that is made union with RouterContext.\n * @template R Result that every action function resolves to.\n */\nexport interface Route {\n /**\n * A string, array of strings, or a regular expression. Defaults to an empty string.\n */\n path?: Path\n /**\n * A unique string that can be used to generate the route URL.\n */\n name?: string\n /**\n * The link to the parent route is automatically populated by the router. Useful for breadcrumbs.\n */\n parent?: Route | null\n /**\n * An array of Route objects. Nested routes are perfect to be used in middleware routes.\n */\n children?: Routes | null\n /**\n * Action method should return anything except `null` or `undefined` to be resolved by router\n * otherwise router will throw `Page not found` error if all matched routes returned nothing.\n */\n action?: (context: RouteContext, params: RouteParams) => RouteResultSync\n /**\n * The route path match function. Used for internal caching.\n */\n match?: MatchFunction\n}\n\n/**\n * Routes is an array of type Route.\n * @template C User context that is made union with RouterContext.\n * @template R Result that every action function resolves to.\n */\nexport type Routes = Array>\n\nexport type ResolveRoute = (\n context: RouteContext,\n params: RouteParams,\n) => RouteResultSync\n\nexport type RouteError = Error & { status?: number }\n\nexport type ErrorHandler = (\n error: RouteError,\n context: ResolveContext,\n) => RouteResultSync\n\nexport interface RouterOptions\n extends ParseOptions,\n TokensToRegexpOptions,\n RegexpToFunctionOptions {\n context?: C\n baseUrl?: string\n resolveRoute?: ResolveRoute\n errorHandler?: ErrorHandler\n}\n\nexport interface RouteMatch {\n route: Route\n baseUrl: string\n path: string\n params: RouteParams\n}\n\nfunction decode(val: string): string {\n try {\n return decodeURIComponent(val)\n } catch (err) {\n return val\n }\n}\n\nfunction matchRoute(\n route: Route,\n baseUrl: string,\n options: RouterOptions,\n pathname: string,\n parentParams?: RouteParams,\n): Iterator, false, Route | false> {\n let matchResult: Match\n let childMatches: Iterator, false, Route | false> | null\n let childIndex = 0\n\n return {\n next(routeToSkip: Route | false): IteratorResult, false> {\n if (route === routeToSkip) {\n return { done: true, value: false }\n }\n\n if (!matchResult) {\n const rt = route\n const end = !rt.children\n if (!rt.match) {\n rt.match = match(rt.path || '', { end, ...options })\n }\n matchResult = rt.match(pathname)\n\n if (matchResult) {\n const { path } = matchResult\n matchResult.path = !end && path.charAt(path.length - 1) === '/' ? path.substr(1) : path\n matchResult.params = { ...parentParams, ...matchResult.params }\n return {\n done: false,\n value: {\n route,\n baseUrl,\n path: matchResult.path,\n params: matchResult.params,\n },\n }\n }\n }\n\n if (matchResult && route.children) {\n while (childIndex < route.children.length) {\n if (!childMatches) {\n const childRoute = route.children[childIndex]\n childRoute.parent = route\n\n childMatches = matchRoute(\n childRoute,\n baseUrl + matchResult.path,\n options,\n pathname.substr(matchResult.path.length),\n matchResult.params,\n )\n }\n\n const childMatch = childMatches.next(routeToSkip)\n if (!childMatch.done) {\n return {\n done: false,\n value: childMatch.value,\n }\n }\n\n childMatches = null\n childIndex++\n }\n }\n\n return { done: true, value: false }\n },\n }\n}\n\nfunction resolveRoute(\n context: RouteContext,\n params: RouteParams,\n): RouteResultSync {\n if (typeof context.route.action === 'function') {\n return context.route.action(context, params)\n }\n return undefined\n}\n\nfunction isChildRoute(\n parentRoute: Route | false,\n childRoute: Route,\n): boolean {\n let route: Route | null | undefined = childRoute\n while (route) {\n route = route.parent\n if (route === parentRoute) {\n return true\n }\n }\n return false\n}\n\nclass UniversalRouterSync {\n root: Route\n\n baseUrl: string\n\n options: RouterOptions\n\n constructor(routes: Routes | Route, options?: RouterOptions) {\n if (!routes || typeof routes !== 'object') {\n throw new TypeError('Invalid routes')\n }\n\n this.options = { decode, ...options }\n this.baseUrl = this.options.baseUrl || ''\n this.root = Array.isArray(routes) ? { path: '', children: routes, parent: null } : routes\n this.root.parent = null\n }\n\n /**\n * Traverses the list of routes in the order they are defined until it finds\n * the first route that matches provided URL path string and whose action function\n * returns anything other than `null` or `undefined`.\n */\n resolve(pathnameOrContext: string | ResolveContext): RouteResultSync {\n const context: ResolveContext = {\n router: this,\n ...this.options.context,\n ...(typeof pathnameOrContext === 'string'\n ? { pathname: pathnameOrContext }\n : pathnameOrContext),\n }\n const matchResult = matchRoute(\n this.root,\n this.baseUrl,\n this.options,\n context.pathname.substr(this.baseUrl.length),\n )\n const resolve = this.options.resolveRoute || resolveRoute\n let matches: IteratorResult, false>\n let nextMatches: IteratorResult, false> | null\n let currentContext = context\n\n function next(\n resume: boolean,\n parent: Route | false = !matches.done && matches.value.route,\n prevResult?: RouteResultSync,\n ): RouteResultSync {\n const routeToSkip = prevResult === null && !matches.done && matches.value.route\n matches = nextMatches || matchResult.next(routeToSkip)\n nextMatches = null\n\n if (!resume) {\n if (matches.done || !isChildRoute(parent, matches.value.route)) {\n nextMatches = matches\n return null\n }\n }\n\n if (matches.done) {\n const error: RouteError = new Error('Route not found')\n error.status = 404\n throw error\n }\n\n currentContext = { ...context, ...matches.value }\n\n const result = resolve(currentContext as RouteContext, matches.value.params)\n if (result !== null && result !== undefined) {\n return result\n }\n return next(resume, parent, result)\n }\n\n context.next = next\n\n try {\n return next(true, this.root)\n } catch (error) {\n if (this.options.errorHandler) {\n return this.options.errorHandler(error, currentContext)\n }\n throw error\n }\n }\n}\n\nexport default UniversalRouterSync\n"],"names":[],"mappings":";;;;;;;;wBA0CsB;;;;oBAKV;uBACE,EAAA;;iBAGR,sBAAqB;;IACb,QAAA,IAAI;iBAAmB,EAAE;;;;;;;;IAM7B,QAAA;;;;;;;;;IAMA,QAAA,IAAI;;;;;;;;gBAOR;;;;;;;;iBAIA;;;;iCAKiB;uBACR,GAAG,CAAC,UAAJ,EAAA;;wBAMF,cAAc;;;;;;;;;;;;;;;;;;iBAuBrB,KAAK;;;sBAGC;;;IAIR,cAAM,aAAA,CAAc,wCAAoC,CAAlD,CAAN;;;qBAKW;;;;;;gCAiBoB;;;;;;IAK1B;IACF;mBACM;;sBAEK;IACV,sBAAU,qDAAV;;;;IAIJ,QAAA,mBAAA;;;qBAEkC,MAAM,aAAA,6BAAA,CAAN;;;;;;;;;;;;IAS1B,MAAA,IAAI;;;;;;;;;;;;;;aA2BR;;;;;;;;;;gBAME;;;;;YAMN,IAAI,MAAM,OAAV;;;;+BAKuB;YACvB,UAAU;iBACoB;;;;;;;;;;iCAUpB,OAAA;oBACN;;;;;;uBAMO,CAAC;;;IAGd,eAAa,kBAAb;;;;;;+BAyBuB,YAAe;;;;;IAQlC,cAAA,EAAU;;IAIR,QAAA,OAAO,EAAP;;;sBAIU;IACd,QAAA,IAAI,eADU;0BAAA;sBAAA;8CAAA;;IAAA;;;;;;;;;;;;;;;;;;;;;oCAkCM,CAAC,SAAD;;;;;+BAYP;;;oBAIT,4BAA8B;;;;;oBAS9B;;;;;;cAoQF;;;;;;;;;;;;;;;IA0BJ,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBCnSQ;;;;;;IAMT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
\ No newline at end of file
+{"version":3,"file":"universal-router-sync.js","sources":["../node_modules/path-to-regexp/src/index.ts","src/UniversalRouterSync.ts"],"sourcesContent":["/**\n * Tokenizer results.\n */\ninterface LexToken {\n type:\n | \"OPEN\"\n | \"CLOSE\"\n | \"PATTERN\"\n | \"NAME\"\n | \"CHAR\"\n | \"ESCAPED_CHAR\"\n | \"MODIFIER\"\n | \"END\";\n index: number;\n value: string;\n}\n\n/**\n * Tokenize input string.\n */\nfunction lexer(str: string): LexToken[] {\n const tokens: LexToken[] = [];\n let i = 0;\n\n while (i < str.length) {\n const char = str[i];\n\n if (char === \"*\" || char === \"+\" || char === \"?\") {\n tokens.push({ type: \"MODIFIER\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"\\\\\") {\n tokens.push({ type: \"ESCAPED_CHAR\", index: i++, value: str[i++] });\n continue;\n }\n\n if (char === \"{\") {\n tokens.push({ type: \"OPEN\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"}\") {\n tokens.push({ type: \"CLOSE\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \":\") {\n let name = \"\";\n let j = i + 1;\n\n while (j < str.length) {\n const code = str.charCodeAt(j);\n\n if (\n // `0-9`\n (code >= 48 && code <= 57) ||\n // `A-Z`\n (code >= 65 && code <= 90) ||\n // `a-z`\n (code >= 97 && code <= 122) ||\n // `_`\n code === 95\n ) {\n name += str[j++];\n continue;\n }\n\n break;\n }\n\n if (!name) throw new TypeError(`Missing parameter name at ${i}`);\n\n tokens.push({ type: \"NAME\", index: i, value: name });\n i = j;\n continue;\n }\n\n if (char === \"(\") {\n let count = 1;\n let pattern = \"\";\n let j = i + 1;\n\n if (str[j] === \"?\") {\n throw new TypeError(`Pattern cannot start with \"?\" at ${j}`);\n }\n\n while (j < str.length) {\n if (str[j] === \"\\\\\") {\n pattern += str[j++] + str[j++];\n continue;\n }\n\n if (str[j] === \")\") {\n count--;\n if (count === 0) {\n j++;\n break;\n }\n } else if (str[j] === \"(\") {\n count++;\n if (str[j + 1] !== \"?\") {\n throw new TypeError(`Capturing groups are not allowed at ${j}`);\n }\n }\n\n pattern += str[j++];\n }\n\n if (count) throw new TypeError(`Unbalanced pattern at ${i}`);\n if (!pattern) throw new TypeError(`Missing pattern at ${i}`);\n\n tokens.push({ type: \"PATTERN\", index: i, value: pattern });\n i = j;\n continue;\n }\n\n tokens.push({ type: \"CHAR\", index: i, value: str[i++] });\n }\n\n tokens.push({ type: \"END\", index: i, value: \"\" });\n\n return tokens;\n}\n\nexport interface ParseOptions {\n /**\n * Set the default delimiter for repeat parameters. (default: `'/'`)\n */\n delimiter?: string;\n /**\n * List of characters to automatically consider prefixes when parsing.\n */\n prefixes?: string;\n}\n\n/**\n * Parse a string for the raw tokens.\n */\nexport function parse(str: string, options: ParseOptions = {}): Token[] {\n const tokens = lexer(str);\n const { prefixes = \"./\" } = options;\n const defaultPattern = `[^${escapeString(options.delimiter || \"/#?\")}]+?`;\n const result: Token[] = [];\n let key = 0;\n let i = 0;\n let path = \"\";\n\n const tryConsume = (type: LexToken[\"type\"]): string | undefined => {\n if (i < tokens.length && tokens[i].type === type) return tokens[i++].value;\n };\n\n const mustConsume = (type: LexToken[\"type\"]): string => {\n const value = tryConsume(type);\n if (value !== undefined) return value;\n const { type: nextType, index } = tokens[i];\n throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}`);\n };\n\n const consumeText = (): string => {\n let result = \"\";\n let value: string | undefined;\n // tslint:disable-next-line\n while ((value = tryConsume(\"CHAR\") || tryConsume(\"ESCAPED_CHAR\"))) {\n result += value;\n }\n return result;\n };\n\n while (i < tokens.length) {\n const char = tryConsume(\"CHAR\");\n const name = tryConsume(\"NAME\");\n const pattern = tryConsume(\"PATTERN\");\n\n if (name || pattern) {\n let prefix = char || \"\";\n\n if (prefixes.indexOf(prefix) === -1) {\n path += prefix;\n prefix = \"\";\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n result.push({\n name: name || key++,\n prefix,\n suffix: \"\",\n pattern: pattern || defaultPattern,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n const value = char || tryConsume(\"ESCAPED_CHAR\");\n if (value) {\n path += value;\n continue;\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n const open = tryConsume(\"OPEN\");\n if (open) {\n const prefix = consumeText();\n const name = tryConsume(\"NAME\") || \"\";\n const pattern = tryConsume(\"PATTERN\") || \"\";\n const suffix = consumeText();\n\n mustConsume(\"CLOSE\");\n\n result.push({\n name: name || (pattern ? key++ : \"\"),\n pattern: name && !pattern ? defaultPattern : pattern,\n prefix,\n suffix,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n mustConsume(\"END\");\n }\n\n return result;\n}\n\nexport interface TokensToFunctionOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * Function for encoding input strings for output.\n */\n encode?: (value: string, token: Key) => string;\n /**\n * When `false` the function can produce an invalid (unmatched) path. (default: `true`)\n */\n validate?: boolean;\n}\n\n/**\n * Compile a string to a template function for the path.\n */\nexport function compile(\n str: string,\n options?: ParseOptions & TokensToFunctionOptions\n) {\n return tokensToFunction
(parse(str, options), options);\n}\n\nexport type PathFunction
= (data?: P) => string;\n\n/**\n * Expose a method for transforming tokens into the path function.\n */\nexport function tokensToFunction
(\n tokens: Token[],\n options: TokensToFunctionOptions = {}\n): PathFunction
{\n const reFlags = flags(options);\n const { encode = (x: string) => x, validate = true } = options;\n\n // Compile all the tokens into regexps.\n const matches = tokens.map(token => {\n if (typeof token === \"object\") {\n return new RegExp(`^(?:${token.pattern})$`, reFlags);\n }\n });\n\n return (data: Record | null | undefined) => {\n let path = \"\";\n\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n\n if (typeof token === \"string\") {\n path += token;\n continue;\n }\n\n const value = data ? data[token.name] : undefined;\n const optional = token.modifier === \"?\" || token.modifier === \"*\";\n const repeat = token.modifier === \"*\" || token.modifier === \"+\";\n\n if (Array.isArray(value)) {\n if (!repeat) {\n throw new TypeError(\n `Expected \"${token.name}\" to not repeat, but got an array`\n );\n }\n\n if (value.length === 0) {\n if (optional) continue;\n\n throw new TypeError(`Expected \"${token.name}\" to not be empty`);\n }\n\n for (let j = 0; j < value.length; j++) {\n const segment = encode(value[j], token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected all \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n }\n\n continue;\n }\n\n if (typeof value === \"string\" || typeof value === \"number\") {\n const segment = encode(String(value), token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n continue;\n }\n\n if (optional) continue;\n\n const typeOfMessage = repeat ? \"an array\" : \"a string\";\n throw new TypeError(`Expected \"${token.name}\" to be ${typeOfMessage}`);\n }\n\n return path;\n };\n}\n\nexport interface RegexpToFunctionOptions {\n /**\n * Function for decoding strings for params.\n */\n decode?: (value: string, token: Key) => string;\n}\n\n/**\n * A match result contains data about the path match.\n */\nexport interface MatchResult {\n path: string;\n index: number;\n params: P;\n}\n\n/**\n * A match is either `false` (no match) or a match result.\n */\nexport type Match
= false | MatchResult
;\n\n/**\n * The match function takes a string and returns whether it matched the path.\n */\nexport type MatchFunction
= (\n path: string\n) => Match
;\n\n/**\n * Create path match function from `path-to-regexp` spec.\n */\nexport function match
(\n str: Path,\n options?: ParseOptions & TokensToRegexpOptions & RegexpToFunctionOptions\n) {\n const keys: Key[] = [];\n const re = pathToRegexp(str, keys, options);\n return regexpToFunction
(re, keys, options);\n}\n\n/**\n * Create a path match function from `path-to-regexp` output.\n */\nexport function regexpToFunction
(\n re: RegExp,\n keys: Key[],\n options: RegexpToFunctionOptions = {}\n): MatchFunction
{\n const { decode = (x: string) => x } = options;\n\n return function(pathname: string) {\n const m = re.exec(pathname);\n if (!m) return false;\n\n const { 0: path, index } = m;\n const params = Object.create(null);\n\n for (let i = 1; i < m.length; i++) {\n // tslint:disable-next-line\n if (m[i] === undefined) continue;\n\n const key = keys[i - 1];\n\n if (key.modifier === \"*\" || key.modifier === \"+\") {\n params[key.name] = m[i].split(key.prefix + key.suffix).map(value => {\n return decode(value, key);\n });\n } else {\n params[key.name] = decode(m[i], key);\n }\n }\n\n return { path, index, params };\n };\n}\n\n/**\n * Escape a regular expression string.\n */\nfunction escapeString(str: string) {\n return str.replace(/([.+*?=^!:${}()[\\]|/\\\\])/g, \"\\\\$1\");\n}\n\n/**\n * Get the flags for a regexp from the options.\n */\nfunction flags(options?: { sensitive?: boolean }) {\n return options && options.sensitive ? \"\" : \"i\";\n}\n\n/**\n * Metadata about a key.\n */\nexport interface Key {\n name: string | number;\n prefix: string;\n suffix: string;\n pattern: string;\n modifier: string;\n}\n\n/**\n * A token is a string (nothing special) or key metadata (capture group).\n */\nexport type Token = string | Key;\n\n/**\n * Pull out keys from a regexp.\n */\nfunction regexpToRegexp(path: RegExp, keys?: Key[]): RegExp {\n if (!keys) return path;\n\n const groupsRegex = /\\((?:\\?<(.*?)>)?(?!\\?)/g;\n\n let index = 0;\n let execResult = groupsRegex.exec(path.source);\n while (execResult) {\n keys.push({\n // Use parenthesized substring match if available, index otherwise\n name: execResult[1] || index++,\n prefix: \"\",\n suffix: \"\",\n modifier: \"\",\n pattern: \"\"\n });\n execResult = groupsRegex.exec(path.source);\n }\n\n return path;\n}\n\n/**\n * Transform an array into a regexp.\n */\nfunction arrayToRegexp(\n paths: Array,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n): RegExp {\n const parts = paths.map(path => pathToRegexp(path, keys, options).source);\n return new RegExp(`(?:${parts.join(\"|\")})`, flags(options));\n}\n\n/**\n * Create a path regexp from string input.\n */\nfunction stringToRegexp(\n path: string,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n return tokensToRegexp(parse(path, options), keys, options);\n}\n\nexport interface TokensToRegexpOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * When `true` the regexp won't allow an optional trailing delimiter to match. (default: `false`)\n */\n strict?: boolean;\n /**\n * When `true` the regexp will match to the end of the string. (default: `true`)\n */\n end?: boolean;\n /**\n * When `true` the regexp will match from the beginning of the string. (default: `true`)\n */\n start?: boolean;\n /**\n * Sets the final character for non-ending optimistic matches. (default: `/`)\n */\n delimiter?: string;\n /**\n * List of characters that can also be \"end\" characters.\n */\n endsWith?: string;\n /**\n * Encode path tokens for use in the `RegExp`.\n */\n encode?: (value: string) => string;\n}\n\n/**\n * Expose a function for taking tokens and returning a RegExp.\n */\nexport function tokensToRegexp(\n tokens: Token[],\n keys?: Key[],\n options: TokensToRegexpOptions = {}\n) {\n const {\n strict = false,\n start = true,\n end = true,\n encode = (x: string) => x\n } = options;\n const endsWith = `[${escapeString(options.endsWith || \"\")}]|$`;\n const delimiter = `[${escapeString(options.delimiter || \"/#?\")}]`;\n let route = start ? \"^\" : \"\";\n\n // Iterate over the tokens and create our regexp string.\n for (const token of tokens) {\n if (typeof token === \"string\") {\n route += escapeString(encode(token));\n } else {\n const prefix = escapeString(encode(token.prefix));\n const suffix = escapeString(encode(token.suffix));\n\n if (token.pattern) {\n if (keys) keys.push(token);\n\n if (prefix || suffix) {\n if (token.modifier === \"+\" || token.modifier === \"*\") {\n const mod = token.modifier === \"*\" ? \"?\" : \"\";\n route += `(?:${prefix}((?:${token.pattern})(?:${suffix}${prefix}(?:${token.pattern}))*)${suffix})${mod}`;\n } else {\n route += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`;\n }\n } else {\n route += `(${token.pattern})${token.modifier}`;\n }\n } else {\n route += `(?:${prefix}${suffix})${token.modifier}`;\n }\n }\n }\n\n if (end) {\n if (!strict) route += `${delimiter}?`;\n\n route += !options.endsWith ? \"$\" : `(?=${endsWith})`;\n } else {\n const endToken = tokens[tokens.length - 1];\n const isEndDelimited =\n typeof endToken === \"string\"\n ? delimiter.indexOf(endToken[endToken.length - 1]) > -1\n : // tslint:disable-next-line\n endToken === undefined;\n\n if (!strict) {\n route += `(?:${delimiter}(?=${endsWith}))?`;\n }\n\n if (!isEndDelimited) {\n route += `(?=${delimiter}|${endsWith})`;\n }\n }\n\n return new RegExp(route, flags(options));\n}\n\n/**\n * Supported `path-to-regexp` input types.\n */\nexport type Path = string | RegExp | Array;\n\n/**\n * Normalize the given path string, returning a regular expression.\n *\n * An empty array can be passed in for the keys, which will hold the\n * placeholder key descriptions. For example, using `/user/:id`, `keys` will\n * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.\n */\nexport function pathToRegexp(\n path: Path,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n if (path instanceof RegExp) return regexpToRegexp(path, keys);\n if (Array.isArray(path)) return arrayToRegexp(path, keys, options);\n return stringToRegexp(path, keys, options);\n}\n","/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE.txt file in the root directory of this source tree.\n */\n\nimport {\n match,\n Path,\n Match,\n MatchFunction,\n ParseOptions,\n TokensToRegexpOptions,\n RegexpToFunctionOptions,\n} from 'path-to-regexp'\n\n/**\n * In addition to a URL path string, any arbitrary data can be passed to\n * the `router.resolve()` method, that becomes available inside action functions.\n */\nexport interface RouterContext {\n [propName: string]: any\n}\n\nexport interface ResolveContext extends RouterContext {\n /**\n * URL which was transmitted to `router.resolve()`.\n */\n pathname: string\n}\n\n/**\n * Params is a key/value object that represents extracted URL parameters.\n */\nexport interface RouteParams {\n [paramName: string]: string | string[]\n}\n\nexport type RouteResultSync = T | null | undefined\n\nexport interface RouteContext\n extends ResolveContext {\n /**\n * Current router instance.\n */\n router: UniversalRouterSync\n /**\n * Matched route object.\n */\n route: Route\n /**\n * Base URL path relative to the path of the current route.\n */\n baseUrl: string\n /**\n * Matched path.\n */\n path: string\n /**\n * Matched path params.\n */\n params: RouteParams\n /**\n * Middleware style function which can continue resolving.\n */\n next: (resume?: boolean) => RouteResultSync\n}\n\n/**\n * A Route is a singular route in your application. It contains a path, an\n * action function, and optional children which are an array of Route.\n * @template C User context that is made union with RouterContext.\n * @template R Result that every action function resolves to.\n */\nexport interface Route {\n /**\n * A string, array of strings, or a regular expression. Defaults to an empty string.\n */\n path?: Path\n /**\n * A unique string that can be used to generate the route URL.\n */\n name?: string\n /**\n * The link to the parent route is automatically populated by the router. Useful for breadcrumbs.\n */\n parent?: Route | null\n /**\n * An array of Route objects. Nested routes are perfect to be used in middleware routes.\n */\n children?: Routes | null\n /**\n * Action method should return anything except `null` or `undefined` to be resolved by router\n * otherwise router will throw `Page not found` error if all matched routes returned nothing.\n */\n action?: (context: RouteContext, params: RouteParams) => RouteResultSync\n /**\n * The route path match function. Used for internal caching.\n */\n match?: MatchFunction\n}\n\n/**\n * Routes is an array of type Route.\n * @template C User context that is made union with RouterContext.\n * @template R Result that every action function resolves to.\n */\nexport type Routes = Array>\n\nexport type ResolveRoute = (\n context: RouteContext,\n params: RouteParams,\n) => RouteResultSync\n\nexport type RouteError = Error & { status?: number }\n\nexport type ErrorHandler = (\n error: RouteError,\n context: ResolveContext,\n) => RouteResultSync\n\nexport interface RouterOptions\n extends ParseOptions,\n TokensToRegexpOptions,\n RegexpToFunctionOptions {\n context?: C\n baseUrl?: string\n resolveRoute?: ResolveRoute\n errorHandler?: ErrorHandler\n}\n\nexport interface RouteMatch {\n route: Route\n baseUrl: string\n path: string\n params: RouteParams\n}\n\nfunction decode(val: string): string {\n try {\n return decodeURIComponent(val)\n } catch (err) {\n return val\n }\n}\n\nfunction matchRoute(\n route: Route,\n baseUrl: string,\n options: RouterOptions,\n pathname: string,\n parentParams?: RouteParams,\n): Iterator, false, Route | false> {\n let matchResult: Match\n let childMatches: Iterator, false, Route | false> | null\n let childIndex = 0\n\n return {\n next(routeToSkip: Route | false): IteratorResult, false> {\n if (route === routeToSkip) {\n return { done: true, value: false }\n }\n\n if (!matchResult) {\n const rt = route\n const end = !rt.children\n if (!rt.match) {\n rt.match = match(rt.path || '', { end, ...options })\n }\n matchResult = rt.match(pathname)\n\n if (matchResult) {\n const { path } = matchResult\n matchResult.path = !end && path.charAt(path.length - 1) === '/' ? path.substr(1) : path\n matchResult.params = { ...parentParams, ...matchResult.params }\n return {\n done: false,\n value: {\n route,\n baseUrl,\n path: matchResult.path,\n params: matchResult.params,\n },\n }\n }\n }\n\n if (matchResult && route.children) {\n while (childIndex < route.children.length) {\n if (!childMatches) {\n const childRoute = route.children[childIndex]\n childRoute.parent = route\n\n childMatches = matchRoute(\n childRoute,\n baseUrl + matchResult.path,\n options,\n pathname.substr(matchResult.path.length),\n matchResult.params,\n )\n }\n\n const childMatch = childMatches.next(routeToSkip)\n if (!childMatch.done) {\n return {\n done: false,\n value: childMatch.value,\n }\n }\n\n childMatches = null\n childIndex++\n }\n }\n\n return { done: true, value: false }\n },\n }\n}\n\nfunction resolveRoute(\n context: RouteContext,\n params: RouteParams,\n): RouteResultSync {\n if (typeof context.route.action === 'function') {\n return context.route.action(context, params)\n }\n return undefined\n}\n\nfunction isChildRoute(\n parentRoute: Route | false,\n childRoute: Route,\n): boolean {\n let route: Route | null | undefined = childRoute\n while (route) {\n route = route.parent\n if (route === parentRoute) {\n return true\n }\n }\n return false\n}\n\nclass UniversalRouterSync {\n root: Route\n\n baseUrl: string\n\n options: RouterOptions\n\n constructor(routes: Routes | Route, options?: RouterOptions) {\n if (!routes || typeof routes !== 'object') {\n throw new TypeError('Invalid routes')\n }\n\n this.options = { decode, ...options }\n this.baseUrl = this.options.baseUrl || ''\n this.root = Array.isArray(routes) ? { path: '', children: routes, parent: null } : routes\n this.root.parent = null\n }\n\n /**\n * Traverses the list of routes in the order they are defined until it finds\n * the first route that matches provided URL path string and whose action function\n * returns anything other than `null` or `undefined`.\n */\n resolve(pathnameOrContext: string | ResolveContext): RouteResultSync {\n const context: ResolveContext = {\n router: this,\n ...this.options.context,\n ...(typeof pathnameOrContext === 'string'\n ? { pathname: pathnameOrContext }\n : pathnameOrContext),\n }\n const matchResult = matchRoute(\n this.root,\n this.baseUrl,\n this.options,\n context.pathname.substr(this.baseUrl.length),\n )\n const resolve = this.options.resolveRoute || resolveRoute\n let matches: IteratorResult, false>\n let nextMatches: IteratorResult, false> | null\n let currentContext = context\n\n function next(\n resume: boolean,\n parent: Route | false = !matches.done && matches.value.route,\n prevResult?: RouteResultSync,\n ): RouteResultSync {\n const routeToSkip = prevResult === null && !matches.done && matches.value.route\n matches = nextMatches || matchResult.next(routeToSkip)\n nextMatches = null\n\n if (!resume) {\n if (matches.done || !isChildRoute(parent, matches.value.route)) {\n nextMatches = matches\n return null\n }\n }\n\n if (matches.done) {\n const error: RouteError = new Error('Route not found')\n error.status = 404\n throw error\n }\n\n currentContext = { ...context, ...matches.value }\n\n const result = resolve(currentContext as RouteContext, matches.value.params)\n if (result !== null && result !== undefined) {\n return result\n }\n return next(resume, parent, result)\n }\n\n context.next = next\n\n try {\n return next(true, this.root)\n } catch (error) {\n if (this.options.errorHandler) {\n return this.options.errorHandler(error as RouteError, currentContext)\n }\n throw error\n }\n }\n}\n\nexport default UniversalRouterSync\n"],"names":[],"mappings":";;;;;;;;wBA0CsB;;;;oBAKV;uBACE,EAAA;;iBAGR,sBAAqB;;IACb,QAAA,IAAI;iBAAmB,EAAE;;;;;;;;IAM7B,QAAA;;;;;;;;;IAMA,QAAA,IAAI;;;;;;;;gBAOR;;;;;;;;iBAIA;;;;iCAKiB;uBACR,GAAG,CAAC,UAAJ,EAAA;;wBAMF,cAAc;;;;;;;;;;;;;;;;;;iBAuBrB,KAAK;;;sBAGC;;;IAIR,cAAM,aAAA,CAAc,wCAAoC,CAAlD,CAAN;;;qBAKW;;;;;;gCAiBoB;;;;;;IAK1B;IACF;mBACM;;sBAEK;IACV,sBAAU,qDAAV;;;;IAIJ,QAAA,mBAAA;;;qBAEkC,MAAM,aAAA,6BAAA,CAAN;;;;;;;;;;;;IAS1B,MAAA,IAAI;;;;;;;;;;;;;;aA2BR;;;;;;;;;;gBAME;;;;;YAMN,IAAI,MAAM,OAAV;;;;+BAKuB;YACvB,UAAU;iBACoB;;;;;;;;;;iCAUpB,OAAA;oBACN;;;;;;uBAMO,CAAC;;;IAGd,eAAa,kBAAb;;;;;;+BAyBuB,YAAe;;;;;IAQlC,cAAA,EAAU;;IAIR,QAAA,OAAO,EAAP;;;sBAIU;IACd,QAAA,IAAI,eADU;0BAAA;sBAAA;8CAAA;;IAAA;;;;;;;;;;;;;;;;;;;;;oCAkCM,CAAC,SAAD;;;;;+BAYP;;;oBAIT,4BAA8B;;;;;oBAS9B;;;;;;cAoQF;;;;;;;;;;;;;;;IA0BJ,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBCnSQ;;;;;;IAMT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
\ No newline at end of file
diff --git a/dist/universal-router-sync.min.js.map b/dist/universal-router-sync.min.js.map
index 1ddd084..04b5978 100644
--- a/dist/universal-router-sync.min.js.map
+++ b/dist/universal-router-sync.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"universal-router-sync.min.js","sources":["../node_modules/path-to-regexp/src/index.ts","src/UniversalRouterSync.ts"],"sourcesContent":["/**\n * Tokenizer results.\n */\ninterface LexToken {\n type:\n | \"OPEN\"\n | \"CLOSE\"\n | \"PATTERN\"\n | \"NAME\"\n | \"CHAR\"\n | \"ESCAPED_CHAR\"\n | \"MODIFIER\"\n | \"END\";\n index: number;\n value: string;\n}\n\n/**\n * Tokenize input string.\n */\nfunction lexer(str: string): LexToken[] {\n const tokens: LexToken[] = [];\n let i = 0;\n\n while (i < str.length) {\n const char = str[i];\n\n if (char === \"*\" || char === \"+\" || char === \"?\") {\n tokens.push({ type: \"MODIFIER\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"\\\\\") {\n tokens.push({ type: \"ESCAPED_CHAR\", index: i++, value: str[i++] });\n continue;\n }\n\n if (char === \"{\") {\n tokens.push({ type: \"OPEN\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"}\") {\n tokens.push({ type: \"CLOSE\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \":\") {\n let name = \"\";\n let j = i + 1;\n\n while (j < str.length) {\n const code = str.charCodeAt(j);\n\n if (\n // `0-9`\n (code >= 48 && code <= 57) ||\n // `A-Z`\n (code >= 65 && code <= 90) ||\n // `a-z`\n (code >= 97 && code <= 122) ||\n // `_`\n code === 95\n ) {\n name += str[j++];\n continue;\n }\n\n break;\n }\n\n if (!name) throw new TypeError(`Missing parameter name at ${i}`);\n\n tokens.push({ type: \"NAME\", index: i, value: name });\n i = j;\n continue;\n }\n\n if (char === \"(\") {\n let count = 1;\n let pattern = \"\";\n let j = i + 1;\n\n if (str[j] === \"?\") {\n throw new TypeError(`Pattern cannot start with \"?\" at ${j}`);\n }\n\n while (j < str.length) {\n if (str[j] === \"\\\\\") {\n pattern += str[j++] + str[j++];\n continue;\n }\n\n if (str[j] === \")\") {\n count--;\n if (count === 0) {\n j++;\n break;\n }\n } else if (str[j] === \"(\") {\n count++;\n if (str[j + 1] !== \"?\") {\n throw new TypeError(`Capturing groups are not allowed at ${j}`);\n }\n }\n\n pattern += str[j++];\n }\n\n if (count) throw new TypeError(`Unbalanced pattern at ${i}`);\n if (!pattern) throw new TypeError(`Missing pattern at ${i}`);\n\n tokens.push({ type: \"PATTERN\", index: i, value: pattern });\n i = j;\n continue;\n }\n\n tokens.push({ type: \"CHAR\", index: i, value: str[i++] });\n }\n\n tokens.push({ type: \"END\", index: i, value: \"\" });\n\n return tokens;\n}\n\nexport interface ParseOptions {\n /**\n * Set the default delimiter for repeat parameters. (default: `'/'`)\n */\n delimiter?: string;\n /**\n * List of characters to automatically consider prefixes when parsing.\n */\n prefixes?: string;\n}\n\n/**\n * Parse a string for the raw tokens.\n */\nexport function parse(str: string, options: ParseOptions = {}): Token[] {\n const tokens = lexer(str);\n const { prefixes = \"./\" } = options;\n const defaultPattern = `[^${escapeString(options.delimiter || \"/#?\")}]+?`;\n const result: Token[] = [];\n let key = 0;\n let i = 0;\n let path = \"\";\n\n const tryConsume = (type: LexToken[\"type\"]): string | undefined => {\n if (i < tokens.length && tokens[i].type === type) return tokens[i++].value;\n };\n\n const mustConsume = (type: LexToken[\"type\"]): string => {\n const value = tryConsume(type);\n if (value !== undefined) return value;\n const { type: nextType, index } = tokens[i];\n throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}`);\n };\n\n const consumeText = (): string => {\n let result = \"\";\n let value: string | undefined;\n // tslint:disable-next-line\n while ((value = tryConsume(\"CHAR\") || tryConsume(\"ESCAPED_CHAR\"))) {\n result += value;\n }\n return result;\n };\n\n while (i < tokens.length) {\n const char = tryConsume(\"CHAR\");\n const name = tryConsume(\"NAME\");\n const pattern = tryConsume(\"PATTERN\");\n\n if (name || pattern) {\n let prefix = char || \"\";\n\n if (prefixes.indexOf(prefix) === -1) {\n path += prefix;\n prefix = \"\";\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n result.push({\n name: name || key++,\n prefix,\n suffix: \"\",\n pattern: pattern || defaultPattern,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n const value = char || tryConsume(\"ESCAPED_CHAR\");\n if (value) {\n path += value;\n continue;\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n const open = tryConsume(\"OPEN\");\n if (open) {\n const prefix = consumeText();\n const name = tryConsume(\"NAME\") || \"\";\n const pattern = tryConsume(\"PATTERN\") || \"\";\n const suffix = consumeText();\n\n mustConsume(\"CLOSE\");\n\n result.push({\n name: name || (pattern ? key++ : \"\"),\n pattern: name && !pattern ? defaultPattern : pattern,\n prefix,\n suffix,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n mustConsume(\"END\");\n }\n\n return result;\n}\n\nexport interface TokensToFunctionOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * Function for encoding input strings for output.\n */\n encode?: (value: string, token: Key) => string;\n /**\n * When `false` the function can produce an invalid (unmatched) path. (default: `true`)\n */\n validate?: boolean;\n}\n\n/**\n * Compile a string to a template function for the path.\n */\nexport function compile(\n str: string,\n options?: ParseOptions & TokensToFunctionOptions\n) {\n return tokensToFunction
(parse(str, options), options);\n}\n\nexport type PathFunction
= (data?: P) => string;\n\n/**\n * Expose a method for transforming tokens into the path function.\n */\nexport function tokensToFunction
(\n tokens: Token[],\n options: TokensToFunctionOptions = {}\n): PathFunction
{\n const reFlags = flags(options);\n const { encode = (x: string) => x, validate = true } = options;\n\n // Compile all the tokens into regexps.\n const matches = tokens.map(token => {\n if (typeof token === \"object\") {\n return new RegExp(`^(?:${token.pattern})$`, reFlags);\n }\n });\n\n return (data: Record | null | undefined) => {\n let path = \"\";\n\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n\n if (typeof token === \"string\") {\n path += token;\n continue;\n }\n\n const value = data ? data[token.name] : undefined;\n const optional = token.modifier === \"?\" || token.modifier === \"*\";\n const repeat = token.modifier === \"*\" || token.modifier === \"+\";\n\n if (Array.isArray(value)) {\n if (!repeat) {\n throw new TypeError(\n `Expected \"${token.name}\" to not repeat, but got an array`\n );\n }\n\n if (value.length === 0) {\n if (optional) continue;\n\n throw new TypeError(`Expected \"${token.name}\" to not be empty`);\n }\n\n for (let j = 0; j < value.length; j++) {\n const segment = encode(value[j], token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected all \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n }\n\n continue;\n }\n\n if (typeof value === \"string\" || typeof value === \"number\") {\n const segment = encode(String(value), token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n continue;\n }\n\n if (optional) continue;\n\n const typeOfMessage = repeat ? \"an array\" : \"a string\";\n throw new TypeError(`Expected \"${token.name}\" to be ${typeOfMessage}`);\n }\n\n return path;\n };\n}\n\nexport interface RegexpToFunctionOptions {\n /**\n * Function for decoding strings for params.\n */\n decode?: (value: string, token: Key) => string;\n}\n\n/**\n * A match result contains data about the path match.\n */\nexport interface MatchResult {\n path: string;\n index: number;\n params: P;\n}\n\n/**\n * A match is either `false` (no match) or a match result.\n */\nexport type Match
= false | MatchResult
;\n\n/**\n * The match function takes a string and returns whether it matched the path.\n */\nexport type MatchFunction
= (\n path: string\n) => Match
;\n\n/**\n * Create path match function from `path-to-regexp` spec.\n */\nexport function match
(\n str: Path,\n options?: ParseOptions & TokensToRegexpOptions & RegexpToFunctionOptions\n) {\n const keys: Key[] = [];\n const re = pathToRegexp(str, keys, options);\n return regexpToFunction
(re, keys, options);\n}\n\n/**\n * Create a path match function from `path-to-regexp` output.\n */\nexport function regexpToFunction
(\n re: RegExp,\n keys: Key[],\n options: RegexpToFunctionOptions = {}\n): MatchFunction
{\n const { decode = (x: string) => x } = options;\n\n return function(pathname: string) {\n const m = re.exec(pathname);\n if (!m) return false;\n\n const { 0: path, index } = m;\n const params = Object.create(null);\n\n for (let i = 1; i < m.length; i++) {\n // tslint:disable-next-line\n if (m[i] === undefined) continue;\n\n const key = keys[i - 1];\n\n if (key.modifier === \"*\" || key.modifier === \"+\") {\n params[key.name] = m[i].split(key.prefix + key.suffix).map(value => {\n return decode(value, key);\n });\n } else {\n params[key.name] = decode(m[i], key);\n }\n }\n\n return { path, index, params };\n };\n}\n\n/**\n * Escape a regular expression string.\n */\nfunction escapeString(str: string) {\n return str.replace(/([.+*?=^!:${}()[\\]|/\\\\])/g, \"\\\\$1\");\n}\n\n/**\n * Get the flags for a regexp from the options.\n */\nfunction flags(options?: { sensitive?: boolean }) {\n return options && options.sensitive ? \"\" : \"i\";\n}\n\n/**\n * Metadata about a key.\n */\nexport interface Key {\n name: string | number;\n prefix: string;\n suffix: string;\n pattern: string;\n modifier: string;\n}\n\n/**\n * A token is a string (nothing special) or key metadata (capture group).\n */\nexport type Token = string | Key;\n\n/**\n * Pull out keys from a regexp.\n */\nfunction regexpToRegexp(path: RegExp, keys?: Key[]): RegExp {\n if (!keys) return path;\n\n const groupsRegex = /\\((?:\\?<(.*?)>)?(?!\\?)/g;\n\n let index = 0;\n let execResult = groupsRegex.exec(path.source);\n while (execResult) {\n keys.push({\n // Use parenthesized substring match if available, index otherwise\n name: execResult[1] || index++,\n prefix: \"\",\n suffix: \"\",\n modifier: \"\",\n pattern: \"\"\n });\n execResult = groupsRegex.exec(path.source);\n }\n\n return path;\n}\n\n/**\n * Transform an array into a regexp.\n */\nfunction arrayToRegexp(\n paths: Array,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n): RegExp {\n const parts = paths.map(path => pathToRegexp(path, keys, options).source);\n return new RegExp(`(?:${parts.join(\"|\")})`, flags(options));\n}\n\n/**\n * Create a path regexp from string input.\n */\nfunction stringToRegexp(\n path: string,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n return tokensToRegexp(parse(path, options), keys, options);\n}\n\nexport interface TokensToRegexpOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * When `true` the regexp won't allow an optional trailing delimiter to match. (default: `false`)\n */\n strict?: boolean;\n /**\n * When `true` the regexp will match to the end of the string. (default: `true`)\n */\n end?: boolean;\n /**\n * When `true` the regexp will match from the beginning of the string. (default: `true`)\n */\n start?: boolean;\n /**\n * Sets the final character for non-ending optimistic matches. (default: `/`)\n */\n delimiter?: string;\n /**\n * List of characters that can also be \"end\" characters.\n */\n endsWith?: string;\n /**\n * Encode path tokens for use in the `RegExp`.\n */\n encode?: (value: string) => string;\n}\n\n/**\n * Expose a function for taking tokens and returning a RegExp.\n */\nexport function tokensToRegexp(\n tokens: Token[],\n keys?: Key[],\n options: TokensToRegexpOptions = {}\n) {\n const {\n strict = false,\n start = true,\n end = true,\n encode = (x: string) => x\n } = options;\n const endsWith = `[${escapeString(options.endsWith || \"\")}]|$`;\n const delimiter = `[${escapeString(options.delimiter || \"/#?\")}]`;\n let route = start ? \"^\" : \"\";\n\n // Iterate over the tokens and create our regexp string.\n for (const token of tokens) {\n if (typeof token === \"string\") {\n route += escapeString(encode(token));\n } else {\n const prefix = escapeString(encode(token.prefix));\n const suffix = escapeString(encode(token.suffix));\n\n if (token.pattern) {\n if (keys) keys.push(token);\n\n if (prefix || suffix) {\n if (token.modifier === \"+\" || token.modifier === \"*\") {\n const mod = token.modifier === \"*\" ? \"?\" : \"\";\n route += `(?:${prefix}((?:${token.pattern})(?:${suffix}${prefix}(?:${token.pattern}))*)${suffix})${mod}`;\n } else {\n route += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`;\n }\n } else {\n route += `(${token.pattern})${token.modifier}`;\n }\n } else {\n route += `(?:${prefix}${suffix})${token.modifier}`;\n }\n }\n }\n\n if (end) {\n if (!strict) route += `${delimiter}?`;\n\n route += !options.endsWith ? \"$\" : `(?=${endsWith})`;\n } else {\n const endToken = tokens[tokens.length - 1];\n const isEndDelimited =\n typeof endToken === \"string\"\n ? delimiter.indexOf(endToken[endToken.length - 1]) > -1\n : // tslint:disable-next-line\n endToken === undefined;\n\n if (!strict) {\n route += `(?:${delimiter}(?=${endsWith}))?`;\n }\n\n if (!isEndDelimited) {\n route += `(?=${delimiter}|${endsWith})`;\n }\n }\n\n return new RegExp(route, flags(options));\n}\n\n/**\n * Supported `path-to-regexp` input types.\n */\nexport type Path = string | RegExp | Array;\n\n/**\n * Normalize the given path string, returning a regular expression.\n *\n * An empty array can be passed in for the keys, which will hold the\n * placeholder key descriptions. For example, using `/user/:id`, `keys` will\n * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.\n */\nexport function pathToRegexp(\n path: Path,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n if (path instanceof RegExp) return regexpToRegexp(path, keys);\n if (Array.isArray(path)) return arrayToRegexp(path, keys, options);\n return stringToRegexp(path, keys, options);\n}\n","/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE.txt file in the root directory of this source tree.\n */\n\nimport {\n match,\n Path,\n Match,\n MatchFunction,\n ParseOptions,\n TokensToRegexpOptions,\n RegexpToFunctionOptions,\n} from 'path-to-regexp'\n\n/**\n * In addition to a URL path string, any arbitrary data can be passed to\n * the `router.resolve()` method, that becomes available inside action functions.\n */\nexport interface RouterContext {\n [propName: string]: any\n}\n\nexport interface ResolveContext extends RouterContext {\n /**\n * URL which was transmitted to `router.resolve()`.\n */\n pathname: string\n}\n\n/**\n * Params is a key/value object that represents extracted URL parameters.\n */\nexport interface RouteParams {\n [paramName: string]: string | string[]\n}\n\nexport type RouteResultSync = T | null | undefined\n\nexport interface RouteContext\n extends ResolveContext {\n /**\n * Current router instance.\n */\n router: UniversalRouterSync\n /**\n * Matched route object.\n */\n route: Route\n /**\n * Base URL path relative to the path of the current route.\n */\n baseUrl: string\n /**\n * Matched path.\n */\n path: string\n /**\n * Matched path params.\n */\n params: RouteParams\n /**\n * Middleware style function which can continue resolving.\n */\n next: (resume?: boolean) => RouteResultSync\n}\n\n/**\n * A Route is a singular route in your application. It contains a path, an\n * action function, and optional children which are an array of Route.\n * @template C User context that is made union with RouterContext.\n * @template R Result that every action function resolves to.\n */\nexport interface Route {\n /**\n * A string, array of strings, or a regular expression. Defaults to an empty string.\n */\n path?: Path\n /**\n * A unique string that can be used to generate the route URL.\n */\n name?: string\n /**\n * The link to the parent route is automatically populated by the router. Useful for breadcrumbs.\n */\n parent?: Route | null\n /**\n * An array of Route objects. Nested routes are perfect to be used in middleware routes.\n */\n children?: Routes | null\n /**\n * Action method should return anything except `null` or `undefined` to be resolved by router\n * otherwise router will throw `Page not found` error if all matched routes returned nothing.\n */\n action?: (context: RouteContext, params: RouteParams) => RouteResultSync\n /**\n * The route path match function. Used for internal caching.\n */\n match?: MatchFunction\n}\n\n/**\n * Routes is an array of type Route.\n * @template C User context that is made union with RouterContext.\n * @template R Result that every action function resolves to.\n */\nexport type Routes = Array>\n\nexport type ResolveRoute = (\n context: RouteContext,\n params: RouteParams,\n) => RouteResultSync\n\nexport type RouteError = Error & { status?: number }\n\nexport type ErrorHandler = (\n error: RouteError,\n context: ResolveContext,\n) => RouteResultSync\n\nexport interface RouterOptions\n extends ParseOptions,\n TokensToRegexpOptions,\n RegexpToFunctionOptions {\n context?: C\n baseUrl?: string\n resolveRoute?: ResolveRoute\n errorHandler?: ErrorHandler\n}\n\nexport interface RouteMatch {\n route: Route\n baseUrl: string\n path: string\n params: RouteParams\n}\n\nfunction decode(val: string): string {\n try {\n return decodeURIComponent(val)\n } catch (err) {\n return val\n }\n}\n\nfunction matchRoute(\n route: Route,\n baseUrl: string,\n options: RouterOptions,\n pathname: string,\n parentParams?: RouteParams,\n): Iterator, false, Route | false> {\n let matchResult: Match\n let childMatches: Iterator, false, Route | false> | null\n let childIndex = 0\n\n return {\n next(routeToSkip: Route | false): IteratorResult, false> {\n if (route === routeToSkip) {\n return { done: true, value: false }\n }\n\n if (!matchResult) {\n const rt = route\n const end = !rt.children\n if (!rt.match) {\n rt.match = match(rt.path || '', { end, ...options })\n }\n matchResult = rt.match(pathname)\n\n if (matchResult) {\n const { path } = matchResult\n matchResult.path = !end && path.charAt(path.length - 1) === '/' ? path.substr(1) : path\n matchResult.params = { ...parentParams, ...matchResult.params }\n return {\n done: false,\n value: {\n route,\n baseUrl,\n path: matchResult.path,\n params: matchResult.params,\n },\n }\n }\n }\n\n if (matchResult && route.children) {\n while (childIndex < route.children.length) {\n if (!childMatches) {\n const childRoute = route.children[childIndex]\n childRoute.parent = route\n\n childMatches = matchRoute(\n childRoute,\n baseUrl + matchResult.path,\n options,\n pathname.substr(matchResult.path.length),\n matchResult.params,\n )\n }\n\n const childMatch = childMatches.next(routeToSkip)\n if (!childMatch.done) {\n return {\n done: false,\n value: childMatch.value,\n }\n }\n\n childMatches = null\n childIndex++\n }\n }\n\n return { done: true, value: false }\n },\n }\n}\n\nfunction resolveRoute(\n context: RouteContext,\n params: RouteParams,\n): RouteResultSync {\n if (typeof context.route.action === 'function') {\n return context.route.action(context, params)\n }\n return undefined\n}\n\nfunction isChildRoute(\n parentRoute: Route | false,\n childRoute: Route,\n): boolean {\n let route: Route | null | undefined = childRoute\n while (route) {\n route = route.parent\n if (route === parentRoute) {\n return true\n }\n }\n return false\n}\n\nclass UniversalRouterSync {\n root: Route\n\n baseUrl: string\n\n options: RouterOptions\n\n constructor(routes: Routes | Route, options?: RouterOptions) {\n if (!routes || typeof routes !== 'object') {\n throw new TypeError('Invalid routes')\n }\n\n this.options = { decode, ...options }\n this.baseUrl = this.options.baseUrl || ''\n this.root = Array.isArray(routes) ? { path: '', children: routes, parent: null } : routes\n this.root.parent = null\n }\n\n /**\n * Traverses the list of routes in the order they are defined until it finds\n * the first route that matches provided URL path string and whose action function\n * returns anything other than `null` or `undefined`.\n */\n resolve(pathnameOrContext: string | ResolveContext): RouteResultSync {\n const context: ResolveContext = {\n router: this,\n ...this.options.context,\n ...(typeof pathnameOrContext === 'string'\n ? { pathname: pathnameOrContext }\n : pathnameOrContext),\n }\n const matchResult = matchRoute(\n this.root,\n this.baseUrl,\n this.options,\n context.pathname.substr(this.baseUrl.length),\n )\n const resolve = this.options.resolveRoute || resolveRoute\n let matches: IteratorResult, false>\n let nextMatches: IteratorResult, false> | null\n let currentContext = context\n\n function next(\n resume: boolean,\n parent: Route | false = !matches.done && matches.value.route,\n prevResult?: RouteResultSync,\n ): RouteResultSync {\n const routeToSkip = prevResult === null && !matches.done && matches.value.route\n matches = nextMatches || matchResult.next(routeToSkip)\n nextMatches = null\n\n if (!resume) {\n if (matches.done || !isChildRoute(parent, matches.value.route)) {\n nextMatches = matches\n return null\n }\n }\n\n if (matches.done) {\n const error: RouteError = new Error('Route not found')\n error.status = 404\n throw error\n }\n\n currentContext = { ...context, ...matches.value }\n\n const result = resolve(currentContext as RouteContext, matches.value.params)\n if (result !== null && result !== undefined) {\n return result\n }\n return next(resume, parent, result)\n }\n\n context.next = next\n\n try {\n return next(true, this.root)\n } catch (error) {\n if (this.options.errorHandler) {\n return this.options.errorHandler(error, currentContext)\n }\n throw error\n }\n }\n}\n\nexport default UniversalRouterSync\n"],"names":["parse","type","j","str","TypeError","pattern","charCodeAt","i","tokens","undefined","value","length","tryConsume","path","name","val"],"mappings":";4PA+LQA,qGA5ImB,iEA2DhB,gBAsDCC,oEAnDA,SAIF,cAAc,oCAAoCC,UAK7CC,iGA0BGD,gBACAE,oDAIdC,qCAEkC,MAAM,8KA3ErB,OACRF,EAAIG,mBAMN,UAAc,2NAvBjBL,gDANAA,0DANIA,sBAAyBM,+IAiJ7B,4BAMNA,EAAIC,mEAKmBP,WACbQ,IAAVC,iBAC8BF,sJAW1BE,gBAMQC,6BAGDC,kFAqCPC,EAAO,WAIG,CACdC,kKAiCqB,uHAgBa,QAS9B,6yDCLEC"}
\ No newline at end of file
+{"version":3,"file":"universal-router-sync.min.js","sources":["../node_modules/path-to-regexp/src/index.ts","src/UniversalRouterSync.ts"],"sourcesContent":["/**\n * Tokenizer results.\n */\ninterface LexToken {\n type:\n | \"OPEN\"\n | \"CLOSE\"\n | \"PATTERN\"\n | \"NAME\"\n | \"CHAR\"\n | \"ESCAPED_CHAR\"\n | \"MODIFIER\"\n | \"END\";\n index: number;\n value: string;\n}\n\n/**\n * Tokenize input string.\n */\nfunction lexer(str: string): LexToken[] {\n const tokens: LexToken[] = [];\n let i = 0;\n\n while (i < str.length) {\n const char = str[i];\n\n if (char === \"*\" || char === \"+\" || char === \"?\") {\n tokens.push({ type: \"MODIFIER\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"\\\\\") {\n tokens.push({ type: \"ESCAPED_CHAR\", index: i++, value: str[i++] });\n continue;\n }\n\n if (char === \"{\") {\n tokens.push({ type: \"OPEN\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"}\") {\n tokens.push({ type: \"CLOSE\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \":\") {\n let name = \"\";\n let j = i + 1;\n\n while (j < str.length) {\n const code = str.charCodeAt(j);\n\n if (\n // `0-9`\n (code >= 48 && code <= 57) ||\n // `A-Z`\n (code >= 65 && code <= 90) ||\n // `a-z`\n (code >= 97 && code <= 122) ||\n // `_`\n code === 95\n ) {\n name += str[j++];\n continue;\n }\n\n break;\n }\n\n if (!name) throw new TypeError(`Missing parameter name at ${i}`);\n\n tokens.push({ type: \"NAME\", index: i, value: name });\n i = j;\n continue;\n }\n\n if (char === \"(\") {\n let count = 1;\n let pattern = \"\";\n let j = i + 1;\n\n if (str[j] === \"?\") {\n throw new TypeError(`Pattern cannot start with \"?\" at ${j}`);\n }\n\n while (j < str.length) {\n if (str[j] === \"\\\\\") {\n pattern += str[j++] + str[j++];\n continue;\n }\n\n if (str[j] === \")\") {\n count--;\n if (count === 0) {\n j++;\n break;\n }\n } else if (str[j] === \"(\") {\n count++;\n if (str[j + 1] !== \"?\") {\n throw new TypeError(`Capturing groups are not allowed at ${j}`);\n }\n }\n\n pattern += str[j++];\n }\n\n if (count) throw new TypeError(`Unbalanced pattern at ${i}`);\n if (!pattern) throw new TypeError(`Missing pattern at ${i}`);\n\n tokens.push({ type: \"PATTERN\", index: i, value: pattern });\n i = j;\n continue;\n }\n\n tokens.push({ type: \"CHAR\", index: i, value: str[i++] });\n }\n\n tokens.push({ type: \"END\", index: i, value: \"\" });\n\n return tokens;\n}\n\nexport interface ParseOptions {\n /**\n * Set the default delimiter for repeat parameters. (default: `'/'`)\n */\n delimiter?: string;\n /**\n * List of characters to automatically consider prefixes when parsing.\n */\n prefixes?: string;\n}\n\n/**\n * Parse a string for the raw tokens.\n */\nexport function parse(str: string, options: ParseOptions = {}): Token[] {\n const tokens = lexer(str);\n const { prefixes = \"./\" } = options;\n const defaultPattern = `[^${escapeString(options.delimiter || \"/#?\")}]+?`;\n const result: Token[] = [];\n let key = 0;\n let i = 0;\n let path = \"\";\n\n const tryConsume = (type: LexToken[\"type\"]): string | undefined => {\n if (i < tokens.length && tokens[i].type === type) return tokens[i++].value;\n };\n\n const mustConsume = (type: LexToken[\"type\"]): string => {\n const value = tryConsume(type);\n if (value !== undefined) return value;\n const { type: nextType, index } = tokens[i];\n throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}`);\n };\n\n const consumeText = (): string => {\n let result = \"\";\n let value: string | undefined;\n // tslint:disable-next-line\n while ((value = tryConsume(\"CHAR\") || tryConsume(\"ESCAPED_CHAR\"))) {\n result += value;\n }\n return result;\n };\n\n while (i < tokens.length) {\n const char = tryConsume(\"CHAR\");\n const name = tryConsume(\"NAME\");\n const pattern = tryConsume(\"PATTERN\");\n\n if (name || pattern) {\n let prefix = char || \"\";\n\n if (prefixes.indexOf(prefix) === -1) {\n path += prefix;\n prefix = \"\";\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n result.push({\n name: name || key++,\n prefix,\n suffix: \"\",\n pattern: pattern || defaultPattern,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n const value = char || tryConsume(\"ESCAPED_CHAR\");\n if (value) {\n path += value;\n continue;\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n const open = tryConsume(\"OPEN\");\n if (open) {\n const prefix = consumeText();\n const name = tryConsume(\"NAME\") || \"\";\n const pattern = tryConsume(\"PATTERN\") || \"\";\n const suffix = consumeText();\n\n mustConsume(\"CLOSE\");\n\n result.push({\n name: name || (pattern ? key++ : \"\"),\n pattern: name && !pattern ? defaultPattern : pattern,\n prefix,\n suffix,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n mustConsume(\"END\");\n }\n\n return result;\n}\n\nexport interface TokensToFunctionOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * Function for encoding input strings for output.\n */\n encode?: (value: string, token: Key) => string;\n /**\n * When `false` the function can produce an invalid (unmatched) path. (default: `true`)\n */\n validate?: boolean;\n}\n\n/**\n * Compile a string to a template function for the path.\n */\nexport function compile(\n str: string,\n options?: ParseOptions & TokensToFunctionOptions\n) {\n return tokensToFunction
(parse(str, options), options);\n}\n\nexport type PathFunction
= (data?: P) => string;\n\n/**\n * Expose a method for transforming tokens into the path function.\n */\nexport function tokensToFunction
(\n tokens: Token[],\n options: TokensToFunctionOptions = {}\n): PathFunction
{\n const reFlags = flags(options);\n const { encode = (x: string) => x, validate = true } = options;\n\n // Compile all the tokens into regexps.\n const matches = tokens.map(token => {\n if (typeof token === \"object\") {\n return new RegExp(`^(?:${token.pattern})$`, reFlags);\n }\n });\n\n return (data: Record | null | undefined) => {\n let path = \"\";\n\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n\n if (typeof token === \"string\") {\n path += token;\n continue;\n }\n\n const value = data ? data[token.name] : undefined;\n const optional = token.modifier === \"?\" || token.modifier === \"*\";\n const repeat = token.modifier === \"*\" || token.modifier === \"+\";\n\n if (Array.isArray(value)) {\n if (!repeat) {\n throw new TypeError(\n `Expected \"${token.name}\" to not repeat, but got an array`\n );\n }\n\n if (value.length === 0) {\n if (optional) continue;\n\n throw new TypeError(`Expected \"${token.name}\" to not be empty`);\n }\n\n for (let j = 0; j < value.length; j++) {\n const segment = encode(value[j], token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected all \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n }\n\n continue;\n }\n\n if (typeof value === \"string\" || typeof value === \"number\") {\n const segment = encode(String(value), token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n continue;\n }\n\n if (optional) continue;\n\n const typeOfMessage = repeat ? \"an array\" : \"a string\";\n throw new TypeError(`Expected \"${token.name}\" to be ${typeOfMessage}`);\n }\n\n return path;\n };\n}\n\nexport interface RegexpToFunctionOptions {\n /**\n * Function for decoding strings for params.\n */\n decode?: (value: string, token: Key) => string;\n}\n\n/**\n * A match result contains data about the path match.\n */\nexport interface MatchResult {\n path: string;\n index: number;\n params: P;\n}\n\n/**\n * A match is either `false` (no match) or a match result.\n */\nexport type Match
= false | MatchResult
;\n\n/**\n * The match function takes a string and returns whether it matched the path.\n */\nexport type MatchFunction
= (\n path: string\n) => Match
;\n\n/**\n * Create path match function from `path-to-regexp` spec.\n */\nexport function match
(\n str: Path,\n options?: ParseOptions & TokensToRegexpOptions & RegexpToFunctionOptions\n) {\n const keys: Key[] = [];\n const re = pathToRegexp(str, keys, options);\n return regexpToFunction
(re, keys, options);\n}\n\n/**\n * Create a path match function from `path-to-regexp` output.\n */\nexport function regexpToFunction
(\n re: RegExp,\n keys: Key[],\n options: RegexpToFunctionOptions = {}\n): MatchFunction
{\n const { decode = (x: string) => x } = options;\n\n return function(pathname: string) {\n const m = re.exec(pathname);\n if (!m) return false;\n\n const { 0: path, index } = m;\n const params = Object.create(null);\n\n for (let i = 1; i < m.length; i++) {\n // tslint:disable-next-line\n if (m[i] === undefined) continue;\n\n const key = keys[i - 1];\n\n if (key.modifier === \"*\" || key.modifier === \"+\") {\n params[key.name] = m[i].split(key.prefix + key.suffix).map(value => {\n return decode(value, key);\n });\n } else {\n params[key.name] = decode(m[i], key);\n }\n }\n\n return { path, index, params };\n };\n}\n\n/**\n * Escape a regular expression string.\n */\nfunction escapeString(str: string) {\n return str.replace(/([.+*?=^!:${}()[\\]|/\\\\])/g, \"\\\\$1\");\n}\n\n/**\n * Get the flags for a regexp from the options.\n */\nfunction flags(options?: { sensitive?: boolean }) {\n return options && options.sensitive ? \"\" : \"i\";\n}\n\n/**\n * Metadata about a key.\n */\nexport interface Key {\n name: string | number;\n prefix: string;\n suffix: string;\n pattern: string;\n modifier: string;\n}\n\n/**\n * A token is a string (nothing special) or key metadata (capture group).\n */\nexport type Token = string | Key;\n\n/**\n * Pull out keys from a regexp.\n */\nfunction regexpToRegexp(path: RegExp, keys?: Key[]): RegExp {\n if (!keys) return path;\n\n const groupsRegex = /\\((?:\\?<(.*?)>)?(?!\\?)/g;\n\n let index = 0;\n let execResult = groupsRegex.exec(path.source);\n while (execResult) {\n keys.push({\n // Use parenthesized substring match if available, index otherwise\n name: execResult[1] || index++,\n prefix: \"\",\n suffix: \"\",\n modifier: \"\",\n pattern: \"\"\n });\n execResult = groupsRegex.exec(path.source);\n }\n\n return path;\n}\n\n/**\n * Transform an array into a regexp.\n */\nfunction arrayToRegexp(\n paths: Array,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n): RegExp {\n const parts = paths.map(path => pathToRegexp(path, keys, options).source);\n return new RegExp(`(?:${parts.join(\"|\")})`, flags(options));\n}\n\n/**\n * Create a path regexp from string input.\n */\nfunction stringToRegexp(\n path: string,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n return tokensToRegexp(parse(path, options), keys, options);\n}\n\nexport interface TokensToRegexpOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * When `true` the regexp won't allow an optional trailing delimiter to match. (default: `false`)\n */\n strict?: boolean;\n /**\n * When `true` the regexp will match to the end of the string. (default: `true`)\n */\n end?: boolean;\n /**\n * When `true` the regexp will match from the beginning of the string. (default: `true`)\n */\n start?: boolean;\n /**\n * Sets the final character for non-ending optimistic matches. (default: `/`)\n */\n delimiter?: string;\n /**\n * List of characters that can also be \"end\" characters.\n */\n endsWith?: string;\n /**\n * Encode path tokens for use in the `RegExp`.\n */\n encode?: (value: string) => string;\n}\n\n/**\n * Expose a function for taking tokens and returning a RegExp.\n */\nexport function tokensToRegexp(\n tokens: Token[],\n keys?: Key[],\n options: TokensToRegexpOptions = {}\n) {\n const {\n strict = false,\n start = true,\n end = true,\n encode = (x: string) => x\n } = options;\n const endsWith = `[${escapeString(options.endsWith || \"\")}]|$`;\n const delimiter = `[${escapeString(options.delimiter || \"/#?\")}]`;\n let route = start ? \"^\" : \"\";\n\n // Iterate over the tokens and create our regexp string.\n for (const token of tokens) {\n if (typeof token === \"string\") {\n route += escapeString(encode(token));\n } else {\n const prefix = escapeString(encode(token.prefix));\n const suffix = escapeString(encode(token.suffix));\n\n if (token.pattern) {\n if (keys) keys.push(token);\n\n if (prefix || suffix) {\n if (token.modifier === \"+\" || token.modifier === \"*\") {\n const mod = token.modifier === \"*\" ? \"?\" : \"\";\n route += `(?:${prefix}((?:${token.pattern})(?:${suffix}${prefix}(?:${token.pattern}))*)${suffix})${mod}`;\n } else {\n route += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`;\n }\n } else {\n route += `(${token.pattern})${token.modifier}`;\n }\n } else {\n route += `(?:${prefix}${suffix})${token.modifier}`;\n }\n }\n }\n\n if (end) {\n if (!strict) route += `${delimiter}?`;\n\n route += !options.endsWith ? \"$\" : `(?=${endsWith})`;\n } else {\n const endToken = tokens[tokens.length - 1];\n const isEndDelimited =\n typeof endToken === \"string\"\n ? delimiter.indexOf(endToken[endToken.length - 1]) > -1\n : // tslint:disable-next-line\n endToken === undefined;\n\n if (!strict) {\n route += `(?:${delimiter}(?=${endsWith}))?`;\n }\n\n if (!isEndDelimited) {\n route += `(?=${delimiter}|${endsWith})`;\n }\n }\n\n return new RegExp(route, flags(options));\n}\n\n/**\n * Supported `path-to-regexp` input types.\n */\nexport type Path = string | RegExp | Array;\n\n/**\n * Normalize the given path string, returning a regular expression.\n *\n * An empty array can be passed in for the keys, which will hold the\n * placeholder key descriptions. For example, using `/user/:id`, `keys` will\n * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.\n */\nexport function pathToRegexp(\n path: Path,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n if (path instanceof RegExp) return regexpToRegexp(path, keys);\n if (Array.isArray(path)) return arrayToRegexp(path, keys, options);\n return stringToRegexp(path, keys, options);\n}\n","/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE.txt file in the root directory of this source tree.\n */\n\nimport {\n match,\n Path,\n Match,\n MatchFunction,\n ParseOptions,\n TokensToRegexpOptions,\n RegexpToFunctionOptions,\n} from 'path-to-regexp'\n\n/**\n * In addition to a URL path string, any arbitrary data can be passed to\n * the `router.resolve()` method, that becomes available inside action functions.\n */\nexport interface RouterContext {\n [propName: string]: any\n}\n\nexport interface ResolveContext extends RouterContext {\n /**\n * URL which was transmitted to `router.resolve()`.\n */\n pathname: string\n}\n\n/**\n * Params is a key/value object that represents extracted URL parameters.\n */\nexport interface RouteParams {\n [paramName: string]: string | string[]\n}\n\nexport type RouteResultSync = T | null | undefined\n\nexport interface RouteContext\n extends ResolveContext {\n /**\n * Current router instance.\n */\n router: UniversalRouterSync\n /**\n * Matched route object.\n */\n route: Route\n /**\n * Base URL path relative to the path of the current route.\n */\n baseUrl: string\n /**\n * Matched path.\n */\n path: string\n /**\n * Matched path params.\n */\n params: RouteParams\n /**\n * Middleware style function which can continue resolving.\n */\n next: (resume?: boolean) => RouteResultSync\n}\n\n/**\n * A Route is a singular route in your application. It contains a path, an\n * action function, and optional children which are an array of Route.\n * @template C User context that is made union with RouterContext.\n * @template R Result that every action function resolves to.\n */\nexport interface Route {\n /**\n * A string, array of strings, or a regular expression. Defaults to an empty string.\n */\n path?: Path\n /**\n * A unique string that can be used to generate the route URL.\n */\n name?: string\n /**\n * The link to the parent route is automatically populated by the router. Useful for breadcrumbs.\n */\n parent?: Route | null\n /**\n * An array of Route objects. Nested routes are perfect to be used in middleware routes.\n */\n children?: Routes | null\n /**\n * Action method should return anything except `null` or `undefined` to be resolved by router\n * otherwise router will throw `Page not found` error if all matched routes returned nothing.\n */\n action?: (context: RouteContext, params: RouteParams) => RouteResultSync\n /**\n * The route path match function. Used for internal caching.\n */\n match?: MatchFunction\n}\n\n/**\n * Routes is an array of type Route.\n * @template C User context that is made union with RouterContext.\n * @template R Result that every action function resolves to.\n */\nexport type Routes = Array>\n\nexport type ResolveRoute = (\n context: RouteContext,\n params: RouteParams,\n) => RouteResultSync\n\nexport type RouteError = Error & { status?: number }\n\nexport type ErrorHandler = (\n error: RouteError,\n context: ResolveContext,\n) => RouteResultSync\n\nexport interface RouterOptions\n extends ParseOptions,\n TokensToRegexpOptions,\n RegexpToFunctionOptions {\n context?: C\n baseUrl?: string\n resolveRoute?: ResolveRoute\n errorHandler?: ErrorHandler\n}\n\nexport interface RouteMatch {\n route: Route\n baseUrl: string\n path: string\n params: RouteParams\n}\n\nfunction decode(val: string): string {\n try {\n return decodeURIComponent(val)\n } catch (err) {\n return val\n }\n}\n\nfunction matchRoute(\n route: Route,\n baseUrl: string,\n options: RouterOptions,\n pathname: string,\n parentParams?: RouteParams,\n): Iterator, false, Route | false> {\n let matchResult: Match\n let childMatches: Iterator, false, Route | false> | null\n let childIndex = 0\n\n return {\n next(routeToSkip: Route | false): IteratorResult, false> {\n if (route === routeToSkip) {\n return { done: true, value: false }\n }\n\n if (!matchResult) {\n const rt = route\n const end = !rt.children\n if (!rt.match) {\n rt.match = match(rt.path || '', { end, ...options })\n }\n matchResult = rt.match(pathname)\n\n if (matchResult) {\n const { path } = matchResult\n matchResult.path = !end && path.charAt(path.length - 1) === '/' ? path.substr(1) : path\n matchResult.params = { ...parentParams, ...matchResult.params }\n return {\n done: false,\n value: {\n route,\n baseUrl,\n path: matchResult.path,\n params: matchResult.params,\n },\n }\n }\n }\n\n if (matchResult && route.children) {\n while (childIndex < route.children.length) {\n if (!childMatches) {\n const childRoute = route.children[childIndex]\n childRoute.parent = route\n\n childMatches = matchRoute(\n childRoute,\n baseUrl + matchResult.path,\n options,\n pathname.substr(matchResult.path.length),\n matchResult.params,\n )\n }\n\n const childMatch = childMatches.next(routeToSkip)\n if (!childMatch.done) {\n return {\n done: false,\n value: childMatch.value,\n }\n }\n\n childMatches = null\n childIndex++\n }\n }\n\n return { done: true, value: false }\n },\n }\n}\n\nfunction resolveRoute(\n context: RouteContext,\n params: RouteParams,\n): RouteResultSync {\n if (typeof context.route.action === 'function') {\n return context.route.action(context, params)\n }\n return undefined\n}\n\nfunction isChildRoute(\n parentRoute: Route | false,\n childRoute: Route,\n): boolean {\n let route: Route | null | undefined = childRoute\n while (route) {\n route = route.parent\n if (route === parentRoute) {\n return true\n }\n }\n return false\n}\n\nclass UniversalRouterSync {\n root: Route\n\n baseUrl: string\n\n options: RouterOptions\n\n constructor(routes: Routes | Route, options?: RouterOptions) {\n if (!routes || typeof routes !== 'object') {\n throw new TypeError('Invalid routes')\n }\n\n this.options = { decode, ...options }\n this.baseUrl = this.options.baseUrl || ''\n this.root = Array.isArray(routes) ? { path: '', children: routes, parent: null } : routes\n this.root.parent = null\n }\n\n /**\n * Traverses the list of routes in the order they are defined until it finds\n * the first route that matches provided URL path string and whose action function\n * returns anything other than `null` or `undefined`.\n */\n resolve(pathnameOrContext: string | ResolveContext): RouteResultSync {\n const context: ResolveContext = {\n router: this,\n ...this.options.context,\n ...(typeof pathnameOrContext === 'string'\n ? { pathname: pathnameOrContext }\n : pathnameOrContext),\n }\n const matchResult = matchRoute(\n this.root,\n this.baseUrl,\n this.options,\n context.pathname.substr(this.baseUrl.length),\n )\n const resolve = this.options.resolveRoute || resolveRoute\n let matches: IteratorResult, false>\n let nextMatches: IteratorResult, false> | null\n let currentContext = context\n\n function next(\n resume: boolean,\n parent: Route | false = !matches.done && matches.value.route,\n prevResult?: RouteResultSync,\n ): RouteResultSync {\n const routeToSkip = prevResult === null && !matches.done && matches.value.route\n matches = nextMatches || matchResult.next(routeToSkip)\n nextMatches = null\n\n if (!resume) {\n if (matches.done || !isChildRoute(parent, matches.value.route)) {\n nextMatches = matches\n return null\n }\n }\n\n if (matches.done) {\n const error: RouteError = new Error('Route not found')\n error.status = 404\n throw error\n }\n\n currentContext = { ...context, ...matches.value }\n\n const result = resolve(currentContext as RouteContext, matches.value.params)\n if (result !== null && result !== undefined) {\n return result\n }\n return next(resume, parent, result)\n }\n\n context.next = next\n\n try {\n return next(true, this.root)\n } catch (error) {\n if (this.options.errorHandler) {\n return this.options.errorHandler(error as RouteError, currentContext)\n }\n throw error\n }\n }\n}\n\nexport default UniversalRouterSync\n"],"names":["parse","type","j","str","TypeError","pattern","charCodeAt","i","tokens","undefined","value","length","tryConsume","path","name","val"],"mappings":";4PA+LQA,qGA5ImB,iEA2DhB,gBAsDCC,oEAnDA,SAIF,cAAc,oCAAoCC,UAK7CC,iGA0BGD,gBACAE,oDAIdC,qCAEkC,MAAM,8KA3ErB,OACRF,EAAIG,mBAMN,UAAc,2NAvBjBL,gDANAA,0DANIA,sBAAyBM,+IAiJ7B,4BAMNA,EAAIC,mEAKmBP,WACbQ,IAAVC,iBAC8BF,sJAW1BE,gBAMQC,6BAGDC,kFAqCPC,EAAO,WAIG,CACdC,kKAiCqB,uHAgBa,QAS9B,6yDCLEC"}
\ No newline at end of file
diff --git a/dist/universal-router.js.map b/dist/universal-router.js.map
index e32587d..096c8cd 100644
--- a/dist/universal-router.js.map
+++ b/dist/universal-router.js.map
@@ -1 +1 @@
-{"version":3,"file":"universal-router.js","sources":["../node_modules/path-to-regexp/src/index.ts","src/UniversalRouter.ts"],"sourcesContent":["/**\n * Tokenizer results.\n */\ninterface LexToken {\n type:\n | \"OPEN\"\n | \"CLOSE\"\n | \"PATTERN\"\n | \"NAME\"\n | \"CHAR\"\n | \"ESCAPED_CHAR\"\n | \"MODIFIER\"\n | \"END\";\n index: number;\n value: string;\n}\n\n/**\n * Tokenize input string.\n */\nfunction lexer(str: string): LexToken[] {\n const tokens: LexToken[] = [];\n let i = 0;\n\n while (i < str.length) {\n const char = str[i];\n\n if (char === \"*\" || char === \"+\" || char === \"?\") {\n tokens.push({ type: \"MODIFIER\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"\\\\\") {\n tokens.push({ type: \"ESCAPED_CHAR\", index: i++, value: str[i++] });\n continue;\n }\n\n if (char === \"{\") {\n tokens.push({ type: \"OPEN\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \"}\") {\n tokens.push({ type: \"CLOSE\", index: i, value: str[i++] });\n continue;\n }\n\n if (char === \":\") {\n let name = \"\";\n let j = i + 1;\n\n while (j < str.length) {\n const code = str.charCodeAt(j);\n\n if (\n // `0-9`\n (code >= 48 && code <= 57) ||\n // `A-Z`\n (code >= 65 && code <= 90) ||\n // `a-z`\n (code >= 97 && code <= 122) ||\n // `_`\n code === 95\n ) {\n name += str[j++];\n continue;\n }\n\n break;\n }\n\n if (!name) throw new TypeError(`Missing parameter name at ${i}`);\n\n tokens.push({ type: \"NAME\", index: i, value: name });\n i = j;\n continue;\n }\n\n if (char === \"(\") {\n let count = 1;\n let pattern = \"\";\n let j = i + 1;\n\n if (str[j] === \"?\") {\n throw new TypeError(`Pattern cannot start with \"?\" at ${j}`);\n }\n\n while (j < str.length) {\n if (str[j] === \"\\\\\") {\n pattern += str[j++] + str[j++];\n continue;\n }\n\n if (str[j] === \")\") {\n count--;\n if (count === 0) {\n j++;\n break;\n }\n } else if (str[j] === \"(\") {\n count++;\n if (str[j + 1] !== \"?\") {\n throw new TypeError(`Capturing groups are not allowed at ${j}`);\n }\n }\n\n pattern += str[j++];\n }\n\n if (count) throw new TypeError(`Unbalanced pattern at ${i}`);\n if (!pattern) throw new TypeError(`Missing pattern at ${i}`);\n\n tokens.push({ type: \"PATTERN\", index: i, value: pattern });\n i = j;\n continue;\n }\n\n tokens.push({ type: \"CHAR\", index: i, value: str[i++] });\n }\n\n tokens.push({ type: \"END\", index: i, value: \"\" });\n\n return tokens;\n}\n\nexport interface ParseOptions {\n /**\n * Set the default delimiter for repeat parameters. (default: `'/'`)\n */\n delimiter?: string;\n /**\n * List of characters to automatically consider prefixes when parsing.\n */\n prefixes?: string;\n}\n\n/**\n * Parse a string for the raw tokens.\n */\nexport function parse(str: string, options: ParseOptions = {}): Token[] {\n const tokens = lexer(str);\n const { prefixes = \"./\" } = options;\n const defaultPattern = `[^${escapeString(options.delimiter || \"/#?\")}]+?`;\n const result: Token[] = [];\n let key = 0;\n let i = 0;\n let path = \"\";\n\n const tryConsume = (type: LexToken[\"type\"]): string | undefined => {\n if (i < tokens.length && tokens[i].type === type) return tokens[i++].value;\n };\n\n const mustConsume = (type: LexToken[\"type\"]): string => {\n const value = tryConsume(type);\n if (value !== undefined) return value;\n const { type: nextType, index } = tokens[i];\n throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}`);\n };\n\n const consumeText = (): string => {\n let result = \"\";\n let value: string | undefined;\n // tslint:disable-next-line\n while ((value = tryConsume(\"CHAR\") || tryConsume(\"ESCAPED_CHAR\"))) {\n result += value;\n }\n return result;\n };\n\n while (i < tokens.length) {\n const char = tryConsume(\"CHAR\");\n const name = tryConsume(\"NAME\");\n const pattern = tryConsume(\"PATTERN\");\n\n if (name || pattern) {\n let prefix = char || \"\";\n\n if (prefixes.indexOf(prefix) === -1) {\n path += prefix;\n prefix = \"\";\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n result.push({\n name: name || key++,\n prefix,\n suffix: \"\",\n pattern: pattern || defaultPattern,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n const value = char || tryConsume(\"ESCAPED_CHAR\");\n if (value) {\n path += value;\n continue;\n }\n\n if (path) {\n result.push(path);\n path = \"\";\n }\n\n const open = tryConsume(\"OPEN\");\n if (open) {\n const prefix = consumeText();\n const name = tryConsume(\"NAME\") || \"\";\n const pattern = tryConsume(\"PATTERN\") || \"\";\n const suffix = consumeText();\n\n mustConsume(\"CLOSE\");\n\n result.push({\n name: name || (pattern ? key++ : \"\"),\n pattern: name && !pattern ? defaultPattern : pattern,\n prefix,\n suffix,\n modifier: tryConsume(\"MODIFIER\") || \"\"\n });\n continue;\n }\n\n mustConsume(\"END\");\n }\n\n return result;\n}\n\nexport interface TokensToFunctionOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * Function for encoding input strings for output.\n */\n encode?: (value: string, token: Key) => string;\n /**\n * When `false` the function can produce an invalid (unmatched) path. (default: `true`)\n */\n validate?: boolean;\n}\n\n/**\n * Compile a string to a template function for the path.\n */\nexport function compile(\n str: string,\n options?: ParseOptions & TokensToFunctionOptions\n) {\n return tokensToFunction
(parse(str, options), options);\n}\n\nexport type PathFunction
= (data?: P) => string;\n\n/**\n * Expose a method for transforming tokens into the path function.\n */\nexport function tokensToFunction
(\n tokens: Token[],\n options: TokensToFunctionOptions = {}\n): PathFunction
{\n const reFlags = flags(options);\n const { encode = (x: string) => x, validate = true } = options;\n\n // Compile all the tokens into regexps.\n const matches = tokens.map(token => {\n if (typeof token === \"object\") {\n return new RegExp(`^(?:${token.pattern})$`, reFlags);\n }\n });\n\n return (data: Record | null | undefined) => {\n let path = \"\";\n\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n\n if (typeof token === \"string\") {\n path += token;\n continue;\n }\n\n const value = data ? data[token.name] : undefined;\n const optional = token.modifier === \"?\" || token.modifier === \"*\";\n const repeat = token.modifier === \"*\" || token.modifier === \"+\";\n\n if (Array.isArray(value)) {\n if (!repeat) {\n throw new TypeError(\n `Expected \"${token.name}\" to not repeat, but got an array`\n );\n }\n\n if (value.length === 0) {\n if (optional) continue;\n\n throw new TypeError(`Expected \"${token.name}\" to not be empty`);\n }\n\n for (let j = 0; j < value.length; j++) {\n const segment = encode(value[j], token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected all \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n }\n\n continue;\n }\n\n if (typeof value === \"string\" || typeof value === \"number\") {\n const segment = encode(String(value), token);\n\n if (validate && !(matches[i] as RegExp).test(segment)) {\n throw new TypeError(\n `Expected \"${token.name}\" to match \"${token.pattern}\", but got \"${segment}\"`\n );\n }\n\n path += token.prefix + segment + token.suffix;\n continue;\n }\n\n if (optional) continue;\n\n const typeOfMessage = repeat ? \"an array\" : \"a string\";\n throw new TypeError(`Expected \"${token.name}\" to be ${typeOfMessage}`);\n }\n\n return path;\n };\n}\n\nexport interface RegexpToFunctionOptions {\n /**\n * Function for decoding strings for params.\n */\n decode?: (value: string, token: Key) => string;\n}\n\n/**\n * A match result contains data about the path match.\n */\nexport interface MatchResult {\n path: string;\n index: number;\n params: P;\n}\n\n/**\n * A match is either `false` (no match) or a match result.\n */\nexport type Match
= false | MatchResult
;\n\n/**\n * The match function takes a string and returns whether it matched the path.\n */\nexport type MatchFunction
= (\n path: string\n) => Match
;\n\n/**\n * Create path match function from `path-to-regexp` spec.\n */\nexport function match
(\n str: Path,\n options?: ParseOptions & TokensToRegexpOptions & RegexpToFunctionOptions\n) {\n const keys: Key[] = [];\n const re = pathToRegexp(str, keys, options);\n return regexpToFunction
(re, keys, options);\n}\n\n/**\n * Create a path match function from `path-to-regexp` output.\n */\nexport function regexpToFunction
(\n re: RegExp,\n keys: Key[],\n options: RegexpToFunctionOptions = {}\n): MatchFunction
{\n const { decode = (x: string) => x } = options;\n\n return function(pathname: string) {\n const m = re.exec(pathname);\n if (!m) return false;\n\n const { 0: path, index } = m;\n const params = Object.create(null);\n\n for (let i = 1; i < m.length; i++) {\n // tslint:disable-next-line\n if (m[i] === undefined) continue;\n\n const key = keys[i - 1];\n\n if (key.modifier === \"*\" || key.modifier === \"+\") {\n params[key.name] = m[i].split(key.prefix + key.suffix).map(value => {\n return decode(value, key);\n });\n } else {\n params[key.name] = decode(m[i], key);\n }\n }\n\n return { path, index, params };\n };\n}\n\n/**\n * Escape a regular expression string.\n */\nfunction escapeString(str: string) {\n return str.replace(/([.+*?=^!:${}()[\\]|/\\\\])/g, \"\\\\$1\");\n}\n\n/**\n * Get the flags for a regexp from the options.\n */\nfunction flags(options?: { sensitive?: boolean }) {\n return options && options.sensitive ? \"\" : \"i\";\n}\n\n/**\n * Metadata about a key.\n */\nexport interface Key {\n name: string | number;\n prefix: string;\n suffix: string;\n pattern: string;\n modifier: string;\n}\n\n/**\n * A token is a string (nothing special) or key metadata (capture group).\n */\nexport type Token = string | Key;\n\n/**\n * Pull out keys from a regexp.\n */\nfunction regexpToRegexp(path: RegExp, keys?: Key[]): RegExp {\n if (!keys) return path;\n\n const groupsRegex = /\\((?:\\?<(.*?)>)?(?!\\?)/g;\n\n let index = 0;\n let execResult = groupsRegex.exec(path.source);\n while (execResult) {\n keys.push({\n // Use parenthesized substring match if available, index otherwise\n name: execResult[1] || index++,\n prefix: \"\",\n suffix: \"\",\n modifier: \"\",\n pattern: \"\"\n });\n execResult = groupsRegex.exec(path.source);\n }\n\n return path;\n}\n\n/**\n * Transform an array into a regexp.\n */\nfunction arrayToRegexp(\n paths: Array,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n): RegExp {\n const parts = paths.map(path => pathToRegexp(path, keys, options).source);\n return new RegExp(`(?:${parts.join(\"|\")})`, flags(options));\n}\n\n/**\n * Create a path regexp from string input.\n */\nfunction stringToRegexp(\n path: string,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n return tokensToRegexp(parse(path, options), keys, options);\n}\n\nexport interface TokensToRegexpOptions {\n /**\n * When `true` the regexp will be case sensitive. (default: `false`)\n */\n sensitive?: boolean;\n /**\n * When `true` the regexp won't allow an optional trailing delimiter to match. (default: `false`)\n */\n strict?: boolean;\n /**\n * When `true` the regexp will match to the end of the string. (default: `true`)\n */\n end?: boolean;\n /**\n * When `true` the regexp will match from the beginning of the string. (default: `true`)\n */\n start?: boolean;\n /**\n * Sets the final character for non-ending optimistic matches. (default: `/`)\n */\n delimiter?: string;\n /**\n * List of characters that can also be \"end\" characters.\n */\n endsWith?: string;\n /**\n * Encode path tokens for use in the `RegExp`.\n */\n encode?: (value: string) => string;\n}\n\n/**\n * Expose a function for taking tokens and returning a RegExp.\n */\nexport function tokensToRegexp(\n tokens: Token[],\n keys?: Key[],\n options: TokensToRegexpOptions = {}\n) {\n const {\n strict = false,\n start = true,\n end = true,\n encode = (x: string) => x\n } = options;\n const endsWith = `[${escapeString(options.endsWith || \"\")}]|$`;\n const delimiter = `[${escapeString(options.delimiter || \"/#?\")}]`;\n let route = start ? \"^\" : \"\";\n\n // Iterate over the tokens and create our regexp string.\n for (const token of tokens) {\n if (typeof token === \"string\") {\n route += escapeString(encode(token));\n } else {\n const prefix = escapeString(encode(token.prefix));\n const suffix = escapeString(encode(token.suffix));\n\n if (token.pattern) {\n if (keys) keys.push(token);\n\n if (prefix || suffix) {\n if (token.modifier === \"+\" || token.modifier === \"*\") {\n const mod = token.modifier === \"*\" ? \"?\" : \"\";\n route += `(?:${prefix}((?:${token.pattern})(?:${suffix}${prefix}(?:${token.pattern}))*)${suffix})${mod}`;\n } else {\n route += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`;\n }\n } else {\n route += `(${token.pattern})${token.modifier}`;\n }\n } else {\n route += `(?:${prefix}${suffix})${token.modifier}`;\n }\n }\n }\n\n if (end) {\n if (!strict) route += `${delimiter}?`;\n\n route += !options.endsWith ? \"$\" : `(?=${endsWith})`;\n } else {\n const endToken = tokens[tokens.length - 1];\n const isEndDelimited =\n typeof endToken === \"string\"\n ? delimiter.indexOf(endToken[endToken.length - 1]) > -1\n : // tslint:disable-next-line\n endToken === undefined;\n\n if (!strict) {\n route += `(?:${delimiter}(?=${endsWith}))?`;\n }\n\n if (!isEndDelimited) {\n route += `(?=${delimiter}|${endsWith})`;\n }\n }\n\n return new RegExp(route, flags(options));\n}\n\n/**\n * Supported `path-to-regexp` input types.\n */\nexport type Path = string | RegExp | Array;\n\n/**\n * Normalize the given path string, returning a regular expression.\n *\n * An empty array can be passed in for the keys, which will hold the\n * placeholder key descriptions. For example, using `/user/:id`, `keys` will\n * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.\n */\nexport function pathToRegexp(\n path: Path,\n keys?: Key[],\n options?: TokensToRegexpOptions & ParseOptions\n) {\n if (path instanceof RegExp) return regexpToRegexp(path, keys);\n if (Array.isArray(path)) return arrayToRegexp(path, keys, options);\n return stringToRegexp(path, keys, options);\n}\n","/**\n * Universal Router (https://www.kriasoft.com/universal-router/)\n *\n * Copyright (c) 2015-present Kriasoft.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE.txt file in the root directory of this source tree.\n */\n\nimport {\n match,\n Path,\n Match,\n MatchFunction,\n ParseOptions,\n TokensToRegexpOptions,\n RegexpToFunctionOptions,\n} from 'path-to-regexp'\n\n/**\n * In addition to a URL path string, any arbitrary data can be passed to\n * the `router.resolve()` method, that becomes available inside action functions.\n */\nexport interface RouterContext {\n [propName: string]: any\n}\n\nexport interface ResolveContext extends RouterContext {\n /**\n * URL which was transmitted to `router.resolve()`.\n */\n pathname: string\n}\n\n/**\n * Params is a key/value object that represents extracted URL parameters.\n */\nexport interface RouteParams {\n [paramName: string]: string | string[]\n}\n\nexport type RouteResult = T | null | undefined | Promise\n\nexport interface RouteContext\n extends ResolveContext {\n /**\n * Current router instance.\n */\n router: UniversalRouter\n /**\n * Matched route object.\n */\n route: Route\n /**\n * Base URL path relative to the path of the current route.\n */\n baseUrl: string\n /**\n * Matched path.\n */\n path: string\n /**\n * Matched path params.\n */\n params: RouteParams\n /**\n * Middleware style function which can continue resolving.\n */\n next: (resume?: boolean) => Promise\n}\n\n/**\n * A Route is a singular route in your application. It contains a path, an\n * action function, and optional children which are an array of Route.\n * @template C User context that is made union with RouterContext.\n * @template R Result that every action function resolves to.\n * If the action returns a Promise, R can be the type the Promise resolves to.\n */\nexport interface Route {\n /**\n * A string, array of strings, or a regular expression. Defaults to an empty string.\n */\n path?: Path\n /**\n * A unique string that can be used to generate the route URL.\n */\n name?: string\n /**\n * The link to the parent route is automatically populated by the router. Useful for breadcrumbs.\n */\n parent?: Route | null\n /**\n * An array of Route objects. Nested routes are perfect to be used in middleware routes.\n */\n children?: Routes