2971 lines
		
	
	
		
			112 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2971 lines
		
	
	
		
			112 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/*!
 | 
						|
  * vue-router v4.5.1
 | 
						|
  * (c) 2025 Eduardo San Martin Morote
 | 
						|
  * @license MIT
 | 
						|
  */
 | 
						|
'use strict';
 | 
						|
 | 
						|
var vue = require('vue');
 | 
						|
 | 
						|
const isBrowser = typeof document !== 'undefined';
 | 
						|
 | 
						|
/**
 | 
						|
 * Allows differentiating lazy components from functional components and vue-class-component
 | 
						|
 * @internal
 | 
						|
 *
 | 
						|
 * @param component
 | 
						|
 */
 | 
						|
function isRouteComponent(component) {
 | 
						|
    return (typeof component === 'object' ||
 | 
						|
        'displayName' in component ||
 | 
						|
        'props' in component ||
 | 
						|
        '__vccOpts' in component);
 | 
						|
}
 | 
						|
function isESModule(obj) {
 | 
						|
    return (obj.__esModule ||
 | 
						|
        obj[Symbol.toStringTag] === 'Module' ||
 | 
						|
        // support CF with dynamic imports that do not
 | 
						|
        // add the Module string tag
 | 
						|
        (obj.default && isRouteComponent(obj.default)));
 | 
						|
}
 | 
						|
const assign = Object.assign;
 | 
						|
function applyToParams(fn, params) {
 | 
						|
    const newParams = {};
 | 
						|
    for (const key in params) {
 | 
						|
        const value = params[key];
 | 
						|
        newParams[key] = isArray(value)
 | 
						|
            ? value.map(fn)
 | 
						|
            : fn(value);
 | 
						|
    }
 | 
						|
    return newParams;
 | 
						|
}
 | 
						|
const noop = () => { };
 | 
						|
/**
 | 
						|
 * Typesafe alternative to Array.isArray
 | 
						|
 * https://github.com/microsoft/TypeScript/pull/48228
 | 
						|
 */
 | 
						|
const isArray = Array.isArray;
 | 
						|
 | 
						|
/**
 | 
						|
 * Encoding Rules (␣ = Space)
 | 
						|
 * - Path: ␣ " < > # ? { }
 | 
						|
 * - Query: ␣ " < > # & =
 | 
						|
 * - Hash: ␣ " < > `
 | 
						|
 *
 | 
						|
 * On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2)
 | 
						|
 * defines some extra characters to be encoded. Most browsers do not encode them
 | 
						|
 * in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to
 | 
						|
 * also encode `!'()*`. Leaving un-encoded only ASCII alphanumeric(`a-zA-Z0-9`)
 | 
						|
 * plus `-._~`. This extra safety should be applied to query by patching the
 | 
						|
 * string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\`
 | 
						|
 * should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\`
 | 
						|
 * into a `/` if directly typed in. The _backtick_ (`````) should also be
 | 
						|
 * encoded everywhere because some browsers like FF encode it when directly
 | 
						|
 * written while others don't. Safari and IE don't encode ``"<>{}``` in hash.
 | 
						|
 */
 | 
						|
// const EXTRA_RESERVED_RE = /[!'()*]/g
 | 
						|
// const encodeReservedReplacer = (c: string) => '%' + c.charCodeAt(0).toString(16)
 | 
						|
const HASH_RE = /#/g; // %23
 | 
						|
const AMPERSAND_RE = /&/g; // %26
 | 
						|
const SLASH_RE = /\//g; // %2F
 | 
						|
const EQUAL_RE = /=/g; // %3D
 | 
						|
const IM_RE = /\?/g; // %3F
 | 
						|
const PLUS_RE = /\+/g; // %2B
 | 
						|
/**
 | 
						|
 * NOTE: It's not clear to me if we should encode the + symbol in queries, it
 | 
						|
 * seems to be less flexible than not doing so and I can't find out the legacy
 | 
						|
 * systems requiring this for regular requests like text/html. In the standard,
 | 
						|
 * the encoding of the plus character is only mentioned for
 | 
						|
 * application/x-www-form-urlencoded
 | 
						|
 * (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo
 | 
						|
 * leave the plus character as is in queries. To be more flexible, we allow the
 | 
						|
 * plus character on the query, but it can also be manually encoded by the user.
 | 
						|
 *
 | 
						|
 * Resources:
 | 
						|
 * - https://url.spec.whatwg.org/#urlencoded-parsing
 | 
						|
 * - https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20
 | 
						|
 */
 | 
						|
const ENC_BRACKET_OPEN_RE = /%5B/g; // [
 | 
						|
const ENC_BRACKET_CLOSE_RE = /%5D/g; // ]
 | 
						|
const ENC_CARET_RE = /%5E/g; // ^
 | 
						|
const ENC_BACKTICK_RE = /%60/g; // `
 | 
						|
const ENC_CURLY_OPEN_RE = /%7B/g; // {
 | 
						|
const ENC_PIPE_RE = /%7C/g; // |
 | 
						|
const ENC_CURLY_CLOSE_RE = /%7D/g; // }
 | 
						|
const ENC_SPACE_RE = /%20/g; // }
 | 
						|
/**
 | 
						|
 * Encode characters that need to be encoded on the path, search and hash
 | 
						|
 * sections of the URL.
 | 
						|
 *
 | 
						|
 * @internal
 | 
						|
 * @param text - string to encode
 | 
						|
 * @returns encoded string
 | 
						|
 */
 | 
						|
function commonEncode(text) {
 | 
						|
    return encodeURI('' + text)
 | 
						|
        .replace(ENC_PIPE_RE, '|')
 | 
						|
        .replace(ENC_BRACKET_OPEN_RE, '[')
 | 
						|
        .replace(ENC_BRACKET_CLOSE_RE, ']');
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Encode characters that need to be encoded on the hash section of the URL.
 | 
						|
 *
 | 
						|
 * @param text - string to encode
 | 
						|
 * @returns encoded string
 | 
						|
 */
 | 
						|
function encodeHash(text) {
 | 
						|
    return commonEncode(text)
 | 
						|
        .replace(ENC_CURLY_OPEN_RE, '{')
 | 
						|
        .replace(ENC_CURLY_CLOSE_RE, '}')
 | 
						|
        .replace(ENC_CARET_RE, '^');
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Encode characters that need to be encoded query values on the query
 | 
						|
 * section of the URL.
 | 
						|
 *
 | 
						|
 * @param text - string to encode
 | 
						|
 * @returns encoded string
 | 
						|
 */
 | 
						|
function encodeQueryValue(text) {
 | 
						|
    return (commonEncode(text)
 | 
						|
        // Encode the space as +, encode the + to differentiate it from the space
 | 
						|
        .replace(PLUS_RE, '%2B')
 | 
						|
        .replace(ENC_SPACE_RE, '+')
 | 
						|
        .replace(HASH_RE, '%23')
 | 
						|
        .replace(AMPERSAND_RE, '%26')
 | 
						|
        .replace(ENC_BACKTICK_RE, '`')
 | 
						|
        .replace(ENC_CURLY_OPEN_RE, '{')
 | 
						|
        .replace(ENC_CURLY_CLOSE_RE, '}')
 | 
						|
        .replace(ENC_CARET_RE, '^'));
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Like `encodeQueryValue` but also encodes the `=` character.
 | 
						|
 *
 | 
						|
 * @param text - string to encode
 | 
						|
 */
 | 
						|
function encodeQueryKey(text) {
 | 
						|
    return encodeQueryValue(text).replace(EQUAL_RE, '%3D');
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Encode characters that need to be encoded on the path section of the URL.
 | 
						|
 *
 | 
						|
 * @param text - string to encode
 | 
						|
 * @returns encoded string
 | 
						|
 */
 | 
						|
function encodePath(text) {
 | 
						|
    return commonEncode(text).replace(HASH_RE, '%23').replace(IM_RE, '%3F');
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Encode characters that need to be encoded on the path section of the URL as a
 | 
						|
 * param. This function encodes everything {@link encodePath} does plus the
 | 
						|
 * slash (`/`) character. If `text` is `null` or `undefined`, returns an empty
 | 
						|
 * string instead.
 | 
						|
 *
 | 
						|
 * @param text - string to encode
 | 
						|
 * @returns encoded string
 | 
						|
 */
 | 
						|
function encodeParam(text) {
 | 
						|
    return text == null ? '' : encodePath(text).replace(SLASH_RE, '%2F');
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Decode text using `decodeURIComponent`. Returns the original text if it
 | 
						|
 * fails.
 | 
						|
 *
 | 
						|
 * @param text - string to decode
 | 
						|
 * @returns decoded string
 | 
						|
 */
 | 
						|
function decode(text) {
 | 
						|
    try {
 | 
						|
        return decodeURIComponent('' + text);
 | 
						|
    }
 | 
						|
    catch (err) {
 | 
						|
    }
 | 
						|
    return '' + text;
 | 
						|
}
 | 
						|
 | 
						|
const TRAILING_SLASH_RE = /\/$/;
 | 
						|
const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, '');
 | 
						|
/**
 | 
						|
 * Transforms a URI into a normalized history location
 | 
						|
 *
 | 
						|
 * @param parseQuery
 | 
						|
 * @param location - URI to normalize
 | 
						|
 * @param currentLocation - current absolute location. Allows resolving relative
 | 
						|
 * paths. Must start with `/`. Defaults to `/`
 | 
						|
 * @returns a normalized history location
 | 
						|
 */
 | 
						|
function parseURL(parseQuery, location, currentLocation = '/') {
 | 
						|
    let path, query = {}, searchString = '', hash = '';
 | 
						|
    // Could use URL and URLSearchParams but IE 11 doesn't support it
 | 
						|
    // TODO: move to new URL()
 | 
						|
    const hashPos = location.indexOf('#');
 | 
						|
    let searchPos = location.indexOf('?');
 | 
						|
    // the hash appears before the search, so it's not part of the search string
 | 
						|
    if (hashPos < searchPos && hashPos >= 0) {
 | 
						|
        searchPos = -1;
 | 
						|
    }
 | 
						|
    if (searchPos > -1) {
 | 
						|
        path = location.slice(0, searchPos);
 | 
						|
        searchString = location.slice(searchPos + 1, hashPos > -1 ? hashPos : location.length);
 | 
						|
        query = parseQuery(searchString);
 | 
						|
    }
 | 
						|
    if (hashPos > -1) {
 | 
						|
        path = path || location.slice(0, hashPos);
 | 
						|
        // keep the # character
 | 
						|
        hash = location.slice(hashPos, location.length);
 | 
						|
    }
 | 
						|
    // no search and no query
 | 
						|
    path = resolveRelativePath(path != null ? path : location, currentLocation);
 | 
						|
    // empty path means a relative query or hash `?foo=f`, `#thing`
 | 
						|
    return {
 | 
						|
        fullPath: path + (searchString && '?') + searchString + hash,
 | 
						|
        path,
 | 
						|
        query,
 | 
						|
        hash: decode(hash),
 | 
						|
    };
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Stringifies a URL object
 | 
						|
 *
 | 
						|
 * @param stringifyQuery
 | 
						|
 * @param location
 | 
						|
 */
 | 
						|
function stringifyURL(stringifyQuery, location) {
 | 
						|
    const query = location.query ? stringifyQuery(location.query) : '';
 | 
						|
    return location.path + (query && '?') + query + (location.hash || '');
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Strips off the base from the beginning of a location.pathname in a non-case-sensitive way.
 | 
						|
 *
 | 
						|
 * @param pathname - location.pathname
 | 
						|
 * @param base - base to strip off
 | 
						|
 */
 | 
						|
function stripBase(pathname, base) {
 | 
						|
    // no base or base is not found at the beginning
 | 
						|
    if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase()))
 | 
						|
        return pathname;
 | 
						|
    return pathname.slice(base.length) || '/';
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Checks if two RouteLocation are equal. This means that both locations are
 | 
						|
 * pointing towards the same {@link RouteRecord} and that all `params`, `query`
 | 
						|
 * parameters and `hash` are the same
 | 
						|
 *
 | 
						|
 * @param stringifyQuery - A function that takes a query object of type LocationQueryRaw and returns a string representation of it.
 | 
						|
 * @param a - first {@link RouteLocation}
 | 
						|
 * @param b - second {@link RouteLocation}
 | 
						|
 */
 | 
						|
function isSameRouteLocation(stringifyQuery, a, b) {
 | 
						|
    const aLastIndex = a.matched.length - 1;
 | 
						|
    const bLastIndex = b.matched.length - 1;
 | 
						|
    return (aLastIndex > -1 &&
 | 
						|
        aLastIndex === bLastIndex &&
 | 
						|
        isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) &&
 | 
						|
        isSameRouteLocationParams(a.params, b.params) &&
 | 
						|
        stringifyQuery(a.query) === stringifyQuery(b.query) &&
 | 
						|
        a.hash === b.hash);
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Check if two `RouteRecords` are equal. Takes into account aliases: they are
 | 
						|
 * considered equal to the `RouteRecord` they are aliasing.
 | 
						|
 *
 | 
						|
 * @param a - first {@link RouteRecord}
 | 
						|
 * @param b - second {@link RouteRecord}
 | 
						|
 */
 | 
						|
function isSameRouteRecord(a, b) {
 | 
						|
    // since the original record has an undefined value for aliasOf
 | 
						|
    // but all aliases point to the original record, this will always compare
 | 
						|
    // the original record
 | 
						|
    return (a.aliasOf || a) === (b.aliasOf || b);
 | 
						|
}
 | 
						|
function isSameRouteLocationParams(a, b) {
 | 
						|
    if (Object.keys(a).length !== Object.keys(b).length)
 | 
						|
        return false;
 | 
						|
    for (const key in a) {
 | 
						|
        if (!isSameRouteLocationParamsValue(a[key], b[key]))
 | 
						|
            return false;
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
}
 | 
						|
function isSameRouteLocationParamsValue(a, b) {
 | 
						|
    return isArray(a)
 | 
						|
        ? isEquivalentArray(a, b)
 | 
						|
        : isArray(b)
 | 
						|
            ? isEquivalentArray(b, a)
 | 
						|
            : a === b;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Check if two arrays are the same or if an array with one single entry is the
 | 
						|
 * same as another primitive value. Used to check query and parameters
 | 
						|
 *
 | 
						|
 * @param a - array of values
 | 
						|
 * @param b - array of values or a single value
 | 
						|
 */
 | 
						|
function isEquivalentArray(a, b) {
 | 
						|
    return isArray(b)
 | 
						|
        ? a.length === b.length && a.every((value, i) => value === b[i])
 | 
						|
        : a.length === 1 && a[0] === b;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Resolves a relative path that starts with `.`.
 | 
						|
 *
 | 
						|
 * @param to - path location we are resolving
 | 
						|
 * @param from - currentLocation.path, should start with `/`
 | 
						|
 */
 | 
						|
function resolveRelativePath(to, from) {
 | 
						|
    if (to.startsWith('/'))
 | 
						|
        return to;
 | 
						|
    if (!to)
 | 
						|
        return from;
 | 
						|
    const fromSegments = from.split('/');
 | 
						|
    const toSegments = to.split('/');
 | 
						|
    const lastToSegment = toSegments[toSegments.length - 1];
 | 
						|
    // make . and ./ the same (../ === .., ../../ === ../..)
 | 
						|
    // this is the same behavior as new URL()
 | 
						|
    if (lastToSegment === '..' || lastToSegment === '.') {
 | 
						|
        toSegments.push('');
 | 
						|
    }
 | 
						|
    let position = fromSegments.length - 1;
 | 
						|
    let toPosition;
 | 
						|
    let segment;
 | 
						|
    for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
 | 
						|
        segment = toSegments[toPosition];
 | 
						|
        // we stay on the same position
 | 
						|
        if (segment === '.')
 | 
						|
            continue;
 | 
						|
        // go up in the from array
 | 
						|
        if (segment === '..') {
 | 
						|
            // we can't go below zero, but we still need to increment toPosition
 | 
						|
            if (position > 1)
 | 
						|
                position--;
 | 
						|
            // continue
 | 
						|
        }
 | 
						|
        // we reached a non-relative path, we stop here
 | 
						|
        else
 | 
						|
            break;
 | 
						|
    }
 | 
						|
    return (fromSegments.slice(0, position).join('/') +
 | 
						|
        '/' +
 | 
						|
        toSegments.slice(toPosition).join('/'));
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Initial route location where the router is. Can be used in navigation guards
 | 
						|
 * to differentiate the initial navigation.
 | 
						|
 *
 | 
						|
 * @example
 | 
						|
 * ```js
 | 
						|
 * import { START_LOCATION } from 'vue-router'
 | 
						|
 *
 | 
						|
 * router.beforeEach((to, from) => {
 | 
						|
 *   if (from === START_LOCATION) {
 | 
						|
 *     // initial navigation
 | 
						|
 *   }
 | 
						|
 * })
 | 
						|
 * ```
 | 
						|
 */
 | 
						|
const START_LOCATION_NORMALIZED = {
 | 
						|
    path: '/',
 | 
						|
    // TODO: could we use a symbol in the future?
 | 
						|
    name: undefined,
 | 
						|
    params: {},
 | 
						|
    query: {},
 | 
						|
    hash: '',
 | 
						|
    fullPath: '/',
 | 
						|
    matched: [],
 | 
						|
    meta: {},
 | 
						|
    redirectedFrom: undefined,
 | 
						|
};
 | 
						|
 | 
						|
var NavigationType;
 | 
						|
(function (NavigationType) {
 | 
						|
    NavigationType["pop"] = "pop";
 | 
						|
    NavigationType["push"] = "push";
 | 
						|
})(NavigationType || (NavigationType = {}));
 | 
						|
var NavigationDirection;
 | 
						|
(function (NavigationDirection) {
 | 
						|
    NavigationDirection["back"] = "back";
 | 
						|
    NavigationDirection["forward"] = "forward";
 | 
						|
    NavigationDirection["unknown"] = "";
 | 
						|
})(NavigationDirection || (NavigationDirection = {}));
 | 
						|
/**
 | 
						|
 * Starting location for Histories
 | 
						|
 */
 | 
						|
const START = '';
 | 
						|
// Generic utils
 | 
						|
/**
 | 
						|
 * Normalizes a base by removing any trailing slash and reading the base tag if
 | 
						|
 * present.
 | 
						|
 *
 | 
						|
 * @param base - base to normalize
 | 
						|
 */
 | 
						|
function normalizeBase(base) {
 | 
						|
    if (!base) {
 | 
						|
        if (isBrowser) {
 | 
						|
            // respect <base> tag
 | 
						|
            const baseEl = document.querySelector('base');
 | 
						|
            base = (baseEl && baseEl.getAttribute('href')) || '/';
 | 
						|
            // strip full URL origin
 | 
						|
            base = base.replace(/^\w+:\/\/[^\/]+/, '');
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            base = '/';
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // ensure leading slash when it was removed by the regex above avoid leading
 | 
						|
    // slash with hash because the file could be read from the disk like file://
 | 
						|
    // and the leading slash would cause problems
 | 
						|
    if (base[0] !== '/' && base[0] !== '#')
 | 
						|
        base = '/' + base;
 | 
						|
    // remove the trailing slash so all other method can just do `base + fullPath`
 | 
						|
    // to build an href
 | 
						|
    return removeTrailingSlash(base);
 | 
						|
}
 | 
						|
// remove any character before the hash
 | 
						|
const BEFORE_HASH_RE = /^[^#]+#/;
 | 
						|
function createHref(base, location) {
 | 
						|
    return base.replace(BEFORE_HASH_RE, '#') + location;
 | 
						|
}
 | 
						|
 | 
						|
function getElementPosition(el, offset) {
 | 
						|
    const docRect = document.documentElement.getBoundingClientRect();
 | 
						|
    const elRect = el.getBoundingClientRect();
 | 
						|
    return {
 | 
						|
        behavior: offset.behavior,
 | 
						|
        left: elRect.left - docRect.left - (offset.left || 0),
 | 
						|
        top: elRect.top - docRect.top - (offset.top || 0),
 | 
						|
    };
 | 
						|
}
 | 
						|
const computeScrollPosition = () => ({
 | 
						|
    left: window.scrollX,
 | 
						|
    top: window.scrollY,
 | 
						|
});
 | 
						|
function scrollToPosition(position) {
 | 
						|
    let scrollToOptions;
 | 
						|
    if ('el' in position) {
 | 
						|
        const positionEl = position.el;
 | 
						|
        const isIdSelector = typeof positionEl === 'string' && positionEl.startsWith('#');
 | 
						|
        const el = typeof positionEl === 'string'
 | 
						|
            ? isIdSelector
 | 
						|
                ? document.getElementById(positionEl.slice(1))
 | 
						|
                : document.querySelector(positionEl)
 | 
						|
            : positionEl;
 | 
						|
        if (!el) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        scrollToOptions = getElementPosition(el, position);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        scrollToOptions = position;
 | 
						|
    }
 | 
						|
    if ('scrollBehavior' in document.documentElement.style)
 | 
						|
        window.scrollTo(scrollToOptions);
 | 
						|
    else {
 | 
						|
        window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.scrollX, scrollToOptions.top != null ? scrollToOptions.top : window.scrollY);
 | 
						|
    }
 | 
						|
}
 | 
						|
function getScrollKey(path, delta) {
 | 
						|
    const position = history.state ? history.state.position - delta : -1;
 | 
						|
    return position + path;
 | 
						|
}
 | 
						|
const scrollPositions = new Map();
 | 
						|
function saveScrollPosition(key, scrollPosition) {
 | 
						|
    scrollPositions.set(key, scrollPosition);
 | 
						|
}
 | 
						|
function getSavedScrollPosition(key) {
 | 
						|
    const scroll = scrollPositions.get(key);
 | 
						|
    // consume it so it's not used again
 | 
						|
    scrollPositions.delete(key);
 | 
						|
    return scroll;
 | 
						|
}
 | 
						|
// TODO: RFC about how to save scroll position
 | 
						|
/**
 | 
						|
 * ScrollBehavior instance used by the router to compute and restore the scroll
 | 
						|
 * position when navigating.
 | 
						|
 */
 | 
						|
// export interface ScrollHandler<ScrollPositionEntry extends HistoryStateValue, ScrollPosition extends ScrollPositionEntry> {
 | 
						|
//   // returns a scroll position that can be saved in history
 | 
						|
//   compute(): ScrollPositionEntry
 | 
						|
//   // can take an extended ScrollPositionEntry
 | 
						|
//   scroll(position: ScrollPosition): void
 | 
						|
// }
 | 
						|
// export const scrollHandler: ScrollHandler<ScrollPosition> = {
 | 
						|
//   compute: computeScroll,
 | 
						|
//   scroll: scrollToPosition,
 | 
						|
// }
 | 
						|
 | 
						|
let createBaseLocation = () => location.protocol + '//' + location.host;
 | 
						|
/**
 | 
						|
 * Creates a normalized history location from a window.location object
 | 
						|
 * @param base - The base path
 | 
						|
 * @param location - The window.location object
 | 
						|
 */
 | 
						|
function createCurrentLocation(base, location) {
 | 
						|
    const { pathname, search, hash } = location;
 | 
						|
    // allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end
 | 
						|
    const hashPos = base.indexOf('#');
 | 
						|
    if (hashPos > -1) {
 | 
						|
        let slicePos = hash.includes(base.slice(hashPos))
 | 
						|
            ? base.slice(hashPos).length
 | 
						|
            : 1;
 | 
						|
        let pathFromHash = hash.slice(slicePos);
 | 
						|
        // prepend the starting slash to hash so the url starts with /#
 | 
						|
        if (pathFromHash[0] !== '/')
 | 
						|
            pathFromHash = '/' + pathFromHash;
 | 
						|
        return stripBase(pathFromHash, '');
 | 
						|
    }
 | 
						|
    const path = stripBase(pathname, base);
 | 
						|
    return path + search + hash;
 | 
						|
}
 | 
						|
function useHistoryListeners(base, historyState, currentLocation, replace) {
 | 
						|
    let listeners = [];
 | 
						|
    let teardowns = [];
 | 
						|
    // TODO: should it be a stack? a Dict. Check if the popstate listener
 | 
						|
    // can trigger twice
 | 
						|
    let pauseState = null;
 | 
						|
    const popStateHandler = ({ state, }) => {
 | 
						|
        const to = createCurrentLocation(base, location);
 | 
						|
        const from = currentLocation.value;
 | 
						|
        const fromState = historyState.value;
 | 
						|
        let delta = 0;
 | 
						|
        if (state) {
 | 
						|
            currentLocation.value = to;
 | 
						|
            historyState.value = state;
 | 
						|
            // ignore the popstate and reset the pauseState
 | 
						|
            if (pauseState && pauseState === from) {
 | 
						|
                pauseState = null;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            delta = fromState ? state.position - fromState.position : 0;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            replace(to);
 | 
						|
        }
 | 
						|
        // Here we could also revert the navigation by calling history.go(-delta)
 | 
						|
        // this listener will have to be adapted to not trigger again and to wait for the url
 | 
						|
        // to be updated before triggering the listeners. Some kind of validation function would also
 | 
						|
        // need to be passed to the listeners so the navigation can be accepted
 | 
						|
        // call all listeners
 | 
						|
        listeners.forEach(listener => {
 | 
						|
            listener(currentLocation.value, from, {
 | 
						|
                delta,
 | 
						|
                type: NavigationType.pop,
 | 
						|
                direction: delta
 | 
						|
                    ? delta > 0
 | 
						|
                        ? NavigationDirection.forward
 | 
						|
                        : NavigationDirection.back
 | 
						|
                    : NavigationDirection.unknown,
 | 
						|
            });
 | 
						|
        });
 | 
						|
    };
 | 
						|
    function pauseListeners() {
 | 
						|
        pauseState = currentLocation.value;
 | 
						|
    }
 | 
						|
    function listen(callback) {
 | 
						|
        // set up the listener and prepare teardown callbacks
 | 
						|
        listeners.push(callback);
 | 
						|
        const teardown = () => {
 | 
						|
            const index = listeners.indexOf(callback);
 | 
						|
            if (index > -1)
 | 
						|
                listeners.splice(index, 1);
 | 
						|
        };
 | 
						|
        teardowns.push(teardown);
 | 
						|
        return teardown;
 | 
						|
    }
 | 
						|
    function beforeUnloadListener() {
 | 
						|
        const { history } = window;
 | 
						|
        if (!history.state)
 | 
						|
            return;
 | 
						|
        history.replaceState(assign({}, history.state, { scroll: computeScrollPosition() }), '');
 | 
						|
    }
 | 
						|
    function destroy() {
 | 
						|
        for (const teardown of teardowns)
 | 
						|
            teardown();
 | 
						|
        teardowns = [];
 | 
						|
        window.removeEventListener('popstate', popStateHandler);
 | 
						|
        window.removeEventListener('beforeunload', beforeUnloadListener);
 | 
						|
    }
 | 
						|
    // set up the listeners and prepare teardown callbacks
 | 
						|
    window.addEventListener('popstate', popStateHandler);
 | 
						|
    // TODO: could we use 'pagehide' or 'visibilitychange' instead?
 | 
						|
    // https://developer.chrome.com/blog/page-lifecycle-api/
 | 
						|
    window.addEventListener('beforeunload', beforeUnloadListener, {
 | 
						|
        passive: true,
 | 
						|
    });
 | 
						|
    return {
 | 
						|
        pauseListeners,
 | 
						|
        listen,
 | 
						|
        destroy,
 | 
						|
    };
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Creates a state object
 | 
						|
 */
 | 
						|
function buildState(back, current, forward, replaced = false, computeScroll = false) {
 | 
						|
    return {
 | 
						|
        back,
 | 
						|
        current,
 | 
						|
        forward,
 | 
						|
        replaced,
 | 
						|
        position: window.history.length,
 | 
						|
        scroll: computeScroll ? computeScrollPosition() : null,
 | 
						|
    };
 | 
						|
}
 | 
						|
function useHistoryStateNavigation(base) {
 | 
						|
    const { history, location } = window;
 | 
						|
    // private variables
 | 
						|
    const currentLocation = {
 | 
						|
        value: createCurrentLocation(base, location),
 | 
						|
    };
 | 
						|
    const historyState = { value: history.state };
 | 
						|
    // build current history entry as this is a fresh navigation
 | 
						|
    if (!historyState.value) {
 | 
						|
        changeLocation(currentLocation.value, {
 | 
						|
            back: null,
 | 
						|
            current: currentLocation.value,
 | 
						|
            forward: null,
 | 
						|
            // the length is off by one, we need to decrease it
 | 
						|
            position: history.length - 1,
 | 
						|
            replaced: true,
 | 
						|
            // don't add a scroll as the user may have an anchor, and we want
 | 
						|
            // scrollBehavior to be triggered without a saved position
 | 
						|
            scroll: null,
 | 
						|
        }, true);
 | 
						|
    }
 | 
						|
    function changeLocation(to, state, replace) {
 | 
						|
        /**
 | 
						|
         * if a base tag is provided, and we are on a normal domain, we have to
 | 
						|
         * respect the provided `base` attribute because pushState() will use it and
 | 
						|
         * potentially erase anything before the `#` like at
 | 
						|
         * https://github.com/vuejs/router/issues/685 where a base of
 | 
						|
         * `/folder/#` but a base of `/` would erase the `/folder/` section. If
 | 
						|
         * there is no host, the `<base>` tag makes no sense and if there isn't a
 | 
						|
         * base tag we can just use everything after the `#`.
 | 
						|
         */
 | 
						|
        const hashIndex = base.indexOf('#');
 | 
						|
        const url = hashIndex > -1
 | 
						|
            ? (location.host && document.querySelector('base')
 | 
						|
                ? base
 | 
						|
                : base.slice(hashIndex)) + to
 | 
						|
            : createBaseLocation() + base + to;
 | 
						|
        try {
 | 
						|
            // BROWSER QUIRK
 | 
						|
            // NOTE: Safari throws a SecurityError when calling this function 100 times in 30 seconds
 | 
						|
            history[replace ? 'replaceState' : 'pushState'](state, '', url);
 | 
						|
            historyState.value = state;
 | 
						|
        }
 | 
						|
        catch (err) {
 | 
						|
            {
 | 
						|
                console.error(err);
 | 
						|
            }
 | 
						|
            // Force the navigation, this also resets the call count
 | 
						|
            location[replace ? 'replace' : 'assign'](url);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    function replace(to, data) {
 | 
						|
        const state = assign({}, history.state, buildState(historyState.value.back, 
 | 
						|
        // keep back and forward entries but override current position
 | 
						|
        to, historyState.value.forward, true), data, { position: historyState.value.position });
 | 
						|
        changeLocation(to, state, true);
 | 
						|
        currentLocation.value = to;
 | 
						|
    }
 | 
						|
    function push(to, data) {
 | 
						|
        // Add to current entry the information of where we are going
 | 
						|
        // as well as saving the current position
 | 
						|
        const currentState = assign({}, 
 | 
						|
        // use current history state to gracefully handle a wrong call to
 | 
						|
        // history.replaceState
 | 
						|
        // https://github.com/vuejs/router/issues/366
 | 
						|
        historyState.value, history.state, {
 | 
						|
            forward: to,
 | 
						|
            scroll: computeScrollPosition(),
 | 
						|
        });
 | 
						|
        changeLocation(currentState.current, currentState, true);
 | 
						|
        const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data);
 | 
						|
        changeLocation(to, state, false);
 | 
						|
        currentLocation.value = to;
 | 
						|
    }
 | 
						|
    return {
 | 
						|
        location: currentLocation,
 | 
						|
        state: historyState,
 | 
						|
        push,
 | 
						|
        replace,
 | 
						|
    };
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Creates an HTML5 history. Most common history for single page applications.
 | 
						|
 *
 | 
						|
 * @param base -
 | 
						|
 */
 | 
						|
function createWebHistory(base) {
 | 
						|
    base = normalizeBase(base);
 | 
						|
    const historyNavigation = useHistoryStateNavigation(base);
 | 
						|
    const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location, historyNavigation.replace);
 | 
						|
    function go(delta, triggerListeners = true) {
 | 
						|
        if (!triggerListeners)
 | 
						|
            historyListeners.pauseListeners();
 | 
						|
        history.go(delta);
 | 
						|
    }
 | 
						|
    const routerHistory = assign({
 | 
						|
        // it's overridden right after
 | 
						|
        location: '',
 | 
						|
        base,
 | 
						|
        go,
 | 
						|
        createHref: createHref.bind(null, base),
 | 
						|
    }, historyNavigation, historyListeners);
 | 
						|
    Object.defineProperty(routerHistory, 'location', {
 | 
						|
        enumerable: true,
 | 
						|
        get: () => historyNavigation.location.value,
 | 
						|
    });
 | 
						|
    Object.defineProperty(routerHistory, 'state', {
 | 
						|
        enumerable: true,
 | 
						|
        get: () => historyNavigation.state.value,
 | 
						|
    });
 | 
						|
    return routerHistory;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates an in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere.
 | 
						|
 * It's up to the user to replace that location with the starter location by either calling `router.push` or `router.replace`.
 | 
						|
 *
 | 
						|
 * @param base - Base applied to all urls, defaults to '/'
 | 
						|
 * @returns a history object that can be passed to the router constructor
 | 
						|
 */
 | 
						|
function createMemoryHistory(base = '') {
 | 
						|
    let listeners = [];
 | 
						|
    let queue = [[START, {}]];
 | 
						|
    let position = 0;
 | 
						|
    base = normalizeBase(base);
 | 
						|
    function setLocation(location, state = {}) {
 | 
						|
        position++;
 | 
						|
        if (position !== queue.length) {
 | 
						|
            // we are in the middle, we remove everything from here in the queue
 | 
						|
            queue.splice(position);
 | 
						|
        }
 | 
						|
        queue.push([location, state]);
 | 
						|
    }
 | 
						|
    function triggerListeners(to, from, { direction, delta }) {
 | 
						|
        const info = {
 | 
						|
            direction,
 | 
						|
            delta,
 | 
						|
            type: NavigationType.pop,
 | 
						|
        };
 | 
						|
        for (const callback of listeners) {
 | 
						|
            callback(to, from, info);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    const routerHistory = {
 | 
						|
        // rewritten by Object.defineProperty
 | 
						|
        location: START,
 | 
						|
        // rewritten by Object.defineProperty
 | 
						|
        state: {},
 | 
						|
        base,
 | 
						|
        createHref: createHref.bind(null, base),
 | 
						|
        replace(to, state) {
 | 
						|
            // remove current entry and decrement position
 | 
						|
            queue.splice(position--, 1);
 | 
						|
            setLocation(to, state);
 | 
						|
        },
 | 
						|
        push(to, state) {
 | 
						|
            setLocation(to, state);
 | 
						|
        },
 | 
						|
        listen(callback) {
 | 
						|
            listeners.push(callback);
 | 
						|
            return () => {
 | 
						|
                const index = listeners.indexOf(callback);
 | 
						|
                if (index > -1)
 | 
						|
                    listeners.splice(index, 1);
 | 
						|
            };
 | 
						|
        },
 | 
						|
        destroy() {
 | 
						|
            listeners = [];
 | 
						|
            queue = [[START, {}]];
 | 
						|
            position = 0;
 | 
						|
        },
 | 
						|
        go(delta, shouldTrigger = true) {
 | 
						|
            const from = this.location;
 | 
						|
            const direction = 
 | 
						|
            // we are considering delta === 0 going forward, but in abstract mode
 | 
						|
            // using 0 for the delta doesn't make sense like it does in html5 where
 | 
						|
            // it reloads the page
 | 
						|
            delta < 0 ? NavigationDirection.back : NavigationDirection.forward;
 | 
						|
            position = Math.max(0, Math.min(position + delta, queue.length - 1));
 | 
						|
            if (shouldTrigger) {
 | 
						|
                triggerListeners(this.location, from, {
 | 
						|
                    direction,
 | 
						|
                    delta,
 | 
						|
                });
 | 
						|
            }
 | 
						|
        },
 | 
						|
    };
 | 
						|
    Object.defineProperty(routerHistory, 'location', {
 | 
						|
        enumerable: true,
 | 
						|
        get: () => queue[position][0],
 | 
						|
    });
 | 
						|
    Object.defineProperty(routerHistory, 'state', {
 | 
						|
        enumerable: true,
 | 
						|
        get: () => queue[position][1],
 | 
						|
    });
 | 
						|
    return routerHistory;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates a hash history. Useful for web applications with no host (e.g. `file://`) or when configuring a server to
 | 
						|
 * handle any URL is not possible.
 | 
						|
 *
 | 
						|
 * @param base - optional base to provide. Defaults to `location.pathname + location.search` If there is a `<base>` tag
 | 
						|
 * in the `head`, its value will be ignored in favor of this parameter **but note it affects all the history.pushState()
 | 
						|
 * calls**, meaning that if you use a `<base>` tag, it's `href` value **has to match this parameter** (ignoring anything
 | 
						|
 * after the `#`).
 | 
						|
 *
 | 
						|
 * @example
 | 
						|
 * ```js
 | 
						|
 * // at https://example.com/folder
 | 
						|
 * createWebHashHistory() // gives a url of `https://example.com/folder#`
 | 
						|
 * createWebHashHistory('/folder/') // gives a url of `https://example.com/folder/#`
 | 
						|
 * // if the `#` is provided in the base, it won't be added by `createWebHashHistory`
 | 
						|
 * createWebHashHistory('/folder/#/app/') // gives a url of `https://example.com/folder/#/app/`
 | 
						|
 * // you should avoid doing this because it changes the original url and breaks copying urls
 | 
						|
 * createWebHashHistory('/other-folder/') // gives a url of `https://example.com/other-folder/#`
 | 
						|
 *
 | 
						|
 * // at file:///usr/etc/folder/index.html
 | 
						|
 * // for locations with no `host`, the base is ignored
 | 
						|
 * createWebHashHistory('/iAmIgnored') // gives a url of `file:///usr/etc/folder/index.html#`
 | 
						|
 * ```
 | 
						|
 */
 | 
						|
function createWebHashHistory(base) {
 | 
						|
    // Make sure this implementation is fine in terms of encoding, specially for IE11
 | 
						|
    // for `file://`, directly use the pathname and ignore the base
 | 
						|
    // location.pathname contains an initial `/` even at the root: `https://example.com`
 | 
						|
    base = location.host ? base || location.pathname + location.search : '';
 | 
						|
    // allow the user to provide a `#` in the middle: `/base/#/app`
 | 
						|
    if (!base.includes('#'))
 | 
						|
        base += '#';
 | 
						|
    return createWebHistory(base);
 | 
						|
}
 | 
						|
 | 
						|
function isRouteLocation(route) {
 | 
						|
    return typeof route === 'string' || (route && typeof route === 'object');
 | 
						|
}
 | 
						|
function isRouteName(name) {
 | 
						|
    return typeof name === 'string' || typeof name === 'symbol';
 | 
						|
}
 | 
						|
 | 
						|
const NavigationFailureSymbol = Symbol('');
 | 
						|
/**
 | 
						|
 * Enumeration with all possible types for navigation failures. Can be passed to
 | 
						|
 * {@link isNavigationFailure} to check for specific failures.
 | 
						|
 */
 | 
						|
exports.NavigationFailureType = void 0;
 | 
						|
(function (NavigationFailureType) {
 | 
						|
    /**
 | 
						|
     * An aborted navigation is a navigation that failed because a navigation
 | 
						|
     * guard returned `false` or called `next(false)`
 | 
						|
     */
 | 
						|
    NavigationFailureType[NavigationFailureType["aborted"] = 4] = "aborted";
 | 
						|
    /**
 | 
						|
     * A cancelled navigation is a navigation that failed because a more recent
 | 
						|
     * navigation finished started (not necessarily finished).
 | 
						|
     */
 | 
						|
    NavigationFailureType[NavigationFailureType["cancelled"] = 8] = "cancelled";
 | 
						|
    /**
 | 
						|
     * A duplicated navigation is a navigation that failed because it was
 | 
						|
     * initiated while already being at the exact same location.
 | 
						|
     */
 | 
						|
    NavigationFailureType[NavigationFailureType["duplicated"] = 16] = "duplicated";
 | 
						|
})(exports.NavigationFailureType || (exports.NavigationFailureType = {}));
 | 
						|
// DEV only debug messages
 | 
						|
const ErrorTypeMessages = {
 | 
						|
    [1 /* ErrorTypes.MATCHER_NOT_FOUND */]({ location, currentLocation }) {
 | 
						|
        return `No match for\n ${JSON.stringify(location)}${currentLocation
 | 
						|
            ? '\nwhile being at\n' + JSON.stringify(currentLocation)
 | 
						|
            : ''}`;
 | 
						|
    },
 | 
						|
    [2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */]({ from, to, }) {
 | 
						|
        return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`;
 | 
						|
    },
 | 
						|
    [4 /* ErrorTypes.NAVIGATION_ABORTED */]({ from, to }) {
 | 
						|
        return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`;
 | 
						|
    },
 | 
						|
    [8 /* ErrorTypes.NAVIGATION_CANCELLED */]({ from, to }) {
 | 
						|
        return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`;
 | 
						|
    },
 | 
						|
    [16 /* ErrorTypes.NAVIGATION_DUPLICATED */]({ from, to }) {
 | 
						|
        return `Avoided redundant navigation to current location: "${from.fullPath}".`;
 | 
						|
    },
 | 
						|
};
 | 
						|
/**
 | 
						|
 * Creates a typed NavigationFailure object.
 | 
						|
 * @internal
 | 
						|
 * @param type - NavigationFailureType
 | 
						|
 * @param params - { from, to }
 | 
						|
 */
 | 
						|
function createRouterError(type, params) {
 | 
						|
    // keep full error messages in cjs versions
 | 
						|
    {
 | 
						|
        return assign(new Error(ErrorTypeMessages[type](params)), {
 | 
						|
            type,
 | 
						|
            [NavigationFailureSymbol]: true,
 | 
						|
        }, params);
 | 
						|
    }
 | 
						|
}
 | 
						|
function isNavigationFailure(error, type) {
 | 
						|
    return (error instanceof Error &&
 | 
						|
        NavigationFailureSymbol in error &&
 | 
						|
        (type == null || !!(error.type & type)));
 | 
						|
}
 | 
						|
const propertiesToLog = ['params', 'query', 'hash'];
 | 
						|
function stringifyRoute(to) {
 | 
						|
    if (typeof to === 'string')
 | 
						|
        return to;
 | 
						|
    if (to.path != null)
 | 
						|
        return to.path;
 | 
						|
    const location = {};
 | 
						|
    for (const key of propertiesToLog) {
 | 
						|
        if (key in to)
 | 
						|
            location[key] = to[key];
 | 
						|
    }
 | 
						|
    return JSON.stringify(location, null, 2);
 | 
						|
}
 | 
						|
 | 
						|
// default pattern for a param: non-greedy everything but /
 | 
						|
const BASE_PARAM_PATTERN = '[^/]+?';
 | 
						|
const BASE_PATH_PARSER_OPTIONS = {
 | 
						|
    sensitive: false,
 | 
						|
    strict: false,
 | 
						|
    start: true,
 | 
						|
    end: true,
 | 
						|
};
 | 
						|
// Special Regex characters that must be escaped in static tokens
 | 
						|
const REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g;
 | 
						|
/**
 | 
						|
 * Creates a path parser from an array of Segments (a segment is an array of Tokens)
 | 
						|
 *
 | 
						|
 * @param segments - array of segments returned by tokenizePath
 | 
						|
 * @param extraOptions - optional options for the regexp
 | 
						|
 * @returns a PathParser
 | 
						|
 */
 | 
						|
function tokensToParser(segments, extraOptions) {
 | 
						|
    const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions);
 | 
						|
    // the amount of scores is the same as the length of segments except for the root segment "/"
 | 
						|
    const score = [];
 | 
						|
    // the regexp as a string
 | 
						|
    let pattern = options.start ? '^' : '';
 | 
						|
    // extracted keys
 | 
						|
    const keys = [];
 | 
						|
    for (const segment of segments) {
 | 
						|
        // the root segment needs special treatment
 | 
						|
        const segmentScores = segment.length ? [] : [90 /* PathScore.Root */];
 | 
						|
        // allow trailing slash
 | 
						|
        if (options.strict && !segment.length)
 | 
						|
            pattern += '/';
 | 
						|
        for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
 | 
						|
            const token = segment[tokenIndex];
 | 
						|
            // resets the score if we are inside a sub-segment /:a-other-:b
 | 
						|
            let subSegmentScore = 40 /* PathScore.Segment */ +
 | 
						|
                (options.sensitive ? 0.25 /* PathScore.BonusCaseSensitive */ : 0);
 | 
						|
            if (token.type === 0 /* TokenType.Static */) {
 | 
						|
                // prepend the slash if we are starting a new segment
 | 
						|
                if (!tokenIndex)
 | 
						|
                    pattern += '/';
 | 
						|
                pattern += token.value.replace(REGEX_CHARS_RE, '\\$&');
 | 
						|
                subSegmentScore += 40 /* PathScore.Static */;
 | 
						|
            }
 | 
						|
            else if (token.type === 1 /* TokenType.Param */) {
 | 
						|
                const { value, repeatable, optional, regexp } = token;
 | 
						|
                keys.push({
 | 
						|
                    name: value,
 | 
						|
                    repeatable,
 | 
						|
                    optional,
 | 
						|
                });
 | 
						|
                const re = regexp ? regexp : BASE_PARAM_PATTERN;
 | 
						|
                // the user provided a custom regexp /:id(\\d+)
 | 
						|
                if (re !== BASE_PARAM_PATTERN) {
 | 
						|
                    subSegmentScore += 10 /* PathScore.BonusCustomRegExp */;
 | 
						|
                    // make sure the regexp is valid before using it
 | 
						|
                    try {
 | 
						|
                        new RegExp(`(${re})`);
 | 
						|
                    }
 | 
						|
                    catch (err) {
 | 
						|
                        throw new Error(`Invalid custom RegExp for param "${value}" (${re}): ` +
 | 
						|
                            err.message);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                // when we repeat we must take care of the repeating leading slash
 | 
						|
                let subPattern = repeatable ? `((?:${re})(?:/(?:${re}))*)` : `(${re})`;
 | 
						|
                // prepend the slash if we are starting a new segment
 | 
						|
                if (!tokenIndex)
 | 
						|
                    subPattern =
 | 
						|
                        // avoid an optional / if there are more segments e.g. /:p?-static
 | 
						|
                        // or /:p?-:p2
 | 
						|
                        optional && segment.length < 2
 | 
						|
                            ? `(?:/${subPattern})`
 | 
						|
                            : '/' + subPattern;
 | 
						|
                if (optional)
 | 
						|
                    subPattern += '?';
 | 
						|
                pattern += subPattern;
 | 
						|
                subSegmentScore += 20 /* PathScore.Dynamic */;
 | 
						|
                if (optional)
 | 
						|
                    subSegmentScore += -8 /* PathScore.BonusOptional */;
 | 
						|
                if (repeatable)
 | 
						|
                    subSegmentScore += -20 /* PathScore.BonusRepeatable */;
 | 
						|
                if (re === '.*')
 | 
						|
                    subSegmentScore += -50 /* PathScore.BonusWildcard */;
 | 
						|
            }
 | 
						|
            segmentScores.push(subSegmentScore);
 | 
						|
        }
 | 
						|
        // an empty array like /home/ -> [[{home}], []]
 | 
						|
        // if (!segment.length) pattern += '/'
 | 
						|
        score.push(segmentScores);
 | 
						|
    }
 | 
						|
    // only apply the strict bonus to the last score
 | 
						|
    if (options.strict && options.end) {
 | 
						|
        const i = score.length - 1;
 | 
						|
        score[i][score[i].length - 1] += 0.7000000000000001 /* PathScore.BonusStrict */;
 | 
						|
    }
 | 
						|
    // TODO: dev only warn double trailing slash
 | 
						|
    if (!options.strict)
 | 
						|
        pattern += '/?';
 | 
						|
    if (options.end)
 | 
						|
        pattern += '$';
 | 
						|
    // allow paths like /dynamic to only match dynamic or dynamic/... but not dynamic_something_else
 | 
						|
    else if (options.strict && !pattern.endsWith('/'))
 | 
						|
        pattern += '(?:/|$)';
 | 
						|
    const re = new RegExp(pattern, options.sensitive ? '' : 'i');
 | 
						|
    function parse(path) {
 | 
						|
        const match = path.match(re);
 | 
						|
        const params = {};
 | 
						|
        if (!match)
 | 
						|
            return null;
 | 
						|
        for (let i = 1; i < match.length; i++) {
 | 
						|
            const value = match[i] || '';
 | 
						|
            const key = keys[i - 1];
 | 
						|
            params[key.name] = value && key.repeatable ? value.split('/') : value;
 | 
						|
        }
 | 
						|
        return params;
 | 
						|
    }
 | 
						|
    function stringify(params) {
 | 
						|
        let path = '';
 | 
						|
        // for optional parameters to allow to be empty
 | 
						|
        let avoidDuplicatedSlash = false;
 | 
						|
        for (const segment of segments) {
 | 
						|
            if (!avoidDuplicatedSlash || !path.endsWith('/'))
 | 
						|
                path += '/';
 | 
						|
            avoidDuplicatedSlash = false;
 | 
						|
            for (const token of segment) {
 | 
						|
                if (token.type === 0 /* TokenType.Static */) {
 | 
						|
                    path += token.value;
 | 
						|
                }
 | 
						|
                else if (token.type === 1 /* TokenType.Param */) {
 | 
						|
                    const { value, repeatable, optional } = token;
 | 
						|
                    const param = value in params ? params[value] : '';
 | 
						|
                    if (isArray(param) && !repeatable) {
 | 
						|
                        throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`);
 | 
						|
                    }
 | 
						|
                    const text = isArray(param)
 | 
						|
                        ? param.join('/')
 | 
						|
                        : param;
 | 
						|
                    if (!text) {
 | 
						|
                        if (optional) {
 | 
						|
                            // if we have more than one optional param like /:a?-static we don't need to care about the optional param
 | 
						|
                            if (segment.length < 2) {
 | 
						|
                                // remove the last slash as we could be at the end
 | 
						|
                                if (path.endsWith('/'))
 | 
						|
                                    path = path.slice(0, -1);
 | 
						|
                                // do not append a slash on the next iteration
 | 
						|
                                else
 | 
						|
                                    avoidDuplicatedSlash = true;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                            throw new Error(`Missing required param "${value}"`);
 | 
						|
                    }
 | 
						|
                    path += text;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // avoid empty path when we have multiple optional params
 | 
						|
        return path || '/';
 | 
						|
    }
 | 
						|
    return {
 | 
						|
        re,
 | 
						|
        score,
 | 
						|
        keys,
 | 
						|
        parse,
 | 
						|
        stringify,
 | 
						|
    };
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Compares an array of numbers as used in PathParser.score and returns a
 | 
						|
 * number. This function can be used to `sort` an array
 | 
						|
 *
 | 
						|
 * @param a - first array of numbers
 | 
						|
 * @param b - second array of numbers
 | 
						|
 * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b
 | 
						|
 * should be sorted first
 | 
						|
 */
 | 
						|
function compareScoreArray(a, b) {
 | 
						|
    let i = 0;
 | 
						|
    while (i < a.length && i < b.length) {
 | 
						|
        const diff = b[i] - a[i];
 | 
						|
        // only keep going if diff === 0
 | 
						|
        if (diff)
 | 
						|
            return diff;
 | 
						|
        i++;
 | 
						|
    }
 | 
						|
    // if the last subsegment was Static, the shorter segments should be sorted first
 | 
						|
    // otherwise sort the longest segment first
 | 
						|
    if (a.length < b.length) {
 | 
						|
        return a.length === 1 && a[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */
 | 
						|
            ? -1
 | 
						|
            : 1;
 | 
						|
    }
 | 
						|
    else if (a.length > b.length) {
 | 
						|
        return b.length === 1 && b[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */
 | 
						|
            ? 1
 | 
						|
            : -1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Compare function that can be used with `sort` to sort an array of PathParser
 | 
						|
 *
 | 
						|
 * @param a - first PathParser
 | 
						|
 * @param b - second PathParser
 | 
						|
 * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b
 | 
						|
 */
 | 
						|
function comparePathParserScore(a, b) {
 | 
						|
    let i = 0;
 | 
						|
    const aScore = a.score;
 | 
						|
    const bScore = b.score;
 | 
						|
    while (i < aScore.length && i < bScore.length) {
 | 
						|
        const comp = compareScoreArray(aScore[i], bScore[i]);
 | 
						|
        // do not return if both are equal
 | 
						|
        if (comp)
 | 
						|
            return comp;
 | 
						|
        i++;
 | 
						|
    }
 | 
						|
    if (Math.abs(bScore.length - aScore.length) === 1) {
 | 
						|
        if (isLastScoreNegative(aScore))
 | 
						|
            return 1;
 | 
						|
        if (isLastScoreNegative(bScore))
 | 
						|
            return -1;
 | 
						|
    }
 | 
						|
    // if a and b share the same score entries but b has more, sort b first
 | 
						|
    return bScore.length - aScore.length;
 | 
						|
    // this is the ternary version
 | 
						|
    // return aScore.length < bScore.length
 | 
						|
    //   ? 1
 | 
						|
    //   : aScore.length > bScore.length
 | 
						|
    //   ? -1
 | 
						|
    //   : 0
 | 
						|
}
 | 
						|
/**
 | 
						|
 * This allows detecting splats at the end of a path: /home/:id(.*)*
 | 
						|
 *
 | 
						|
 * @param score - score to check
 | 
						|
 * @returns true if the last entry is negative
 | 
						|
 */
 | 
						|
function isLastScoreNegative(score) {
 | 
						|
    const last = score[score.length - 1];
 | 
						|
    return score.length > 0 && last[last.length - 1] < 0;
 | 
						|
}
 | 
						|
 | 
						|
const ROOT_TOKEN = {
 | 
						|
    type: 0 /* TokenType.Static */,
 | 
						|
    value: '',
 | 
						|
};
 | 
						|
const VALID_PARAM_RE = /[a-zA-Z0-9_]/;
 | 
						|
// After some profiling, the cache seems to be unnecessary because tokenizePath
 | 
						|
// (the slowest part of adding a route) is very fast
 | 
						|
// const tokenCache = new Map<string, Token[][]>()
 | 
						|
function tokenizePath(path) {
 | 
						|
    if (!path)
 | 
						|
        return [[]];
 | 
						|
    if (path === '/')
 | 
						|
        return [[ROOT_TOKEN]];
 | 
						|
    if (!path.startsWith('/')) {
 | 
						|
        throw new Error(`Invalid path "${path}"`);
 | 
						|
    }
 | 
						|
    // if (tokenCache.has(path)) return tokenCache.get(path)!
 | 
						|
    function crash(message) {
 | 
						|
        throw new Error(`ERR (${state})/"${buffer}": ${message}`);
 | 
						|
    }
 | 
						|
    let state = 0 /* TokenizerState.Static */;
 | 
						|
    let previousState = state;
 | 
						|
    const tokens = [];
 | 
						|
    // the segment will always be valid because we get into the initial state
 | 
						|
    // with the leading /
 | 
						|
    let segment;
 | 
						|
    function finalizeSegment() {
 | 
						|
        if (segment)
 | 
						|
            tokens.push(segment);
 | 
						|
        segment = [];
 | 
						|
    }
 | 
						|
    // index on the path
 | 
						|
    let i = 0;
 | 
						|
    // char at index
 | 
						|
    let char;
 | 
						|
    // buffer of the value read
 | 
						|
    let buffer = '';
 | 
						|
    // custom regexp for a param
 | 
						|
    let customRe = '';
 | 
						|
    function consumeBuffer() {
 | 
						|
        if (!buffer)
 | 
						|
            return;
 | 
						|
        if (state === 0 /* TokenizerState.Static */) {
 | 
						|
            segment.push({
 | 
						|
                type: 0 /* TokenType.Static */,
 | 
						|
                value: buffer,
 | 
						|
            });
 | 
						|
        }
 | 
						|
        else if (state === 1 /* TokenizerState.Param */ ||
 | 
						|
            state === 2 /* TokenizerState.ParamRegExp */ ||
 | 
						|
            state === 3 /* TokenizerState.ParamRegExpEnd */) {
 | 
						|
            if (segment.length > 1 && (char === '*' || char === '+'))
 | 
						|
                crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`);
 | 
						|
            segment.push({
 | 
						|
                type: 1 /* TokenType.Param */,
 | 
						|
                value: buffer,
 | 
						|
                regexp: customRe,
 | 
						|
                repeatable: char === '*' || char === '+',
 | 
						|
                optional: char === '*' || char === '?',
 | 
						|
            });
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            crash('Invalid state to consume buffer');
 | 
						|
        }
 | 
						|
        buffer = '';
 | 
						|
    }
 | 
						|
    function addCharToBuffer() {
 | 
						|
        buffer += char;
 | 
						|
    }
 | 
						|
    while (i < path.length) {
 | 
						|
        char = path[i++];
 | 
						|
        if (char === '\\' && state !== 2 /* TokenizerState.ParamRegExp */) {
 | 
						|
            previousState = state;
 | 
						|
            state = 4 /* TokenizerState.EscapeNext */;
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        switch (state) {
 | 
						|
            case 0 /* TokenizerState.Static */:
 | 
						|
                if (char === '/') {
 | 
						|
                    if (buffer) {
 | 
						|
                        consumeBuffer();
 | 
						|
                    }
 | 
						|
                    finalizeSegment();
 | 
						|
                }
 | 
						|
                else if (char === ':') {
 | 
						|
                    consumeBuffer();
 | 
						|
                    state = 1 /* TokenizerState.Param */;
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    addCharToBuffer();
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            case 4 /* TokenizerState.EscapeNext */:
 | 
						|
                addCharToBuffer();
 | 
						|
                state = previousState;
 | 
						|
                break;
 | 
						|
            case 1 /* TokenizerState.Param */:
 | 
						|
                if (char === '(') {
 | 
						|
                    state = 2 /* TokenizerState.ParamRegExp */;
 | 
						|
                }
 | 
						|
                else if (VALID_PARAM_RE.test(char)) {
 | 
						|
                    addCharToBuffer();
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    consumeBuffer();
 | 
						|
                    state = 0 /* TokenizerState.Static */;
 | 
						|
                    // go back one character if we were not modifying
 | 
						|
                    if (char !== '*' && char !== '?' && char !== '+')
 | 
						|
                        i--;
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            case 2 /* TokenizerState.ParamRegExp */:
 | 
						|
                // TODO: is it worth handling nested regexp? like :p(?:prefix_([^/]+)_suffix)
 | 
						|
                // it already works by escaping the closing )
 | 
						|
                // https://paths.esm.dev/?p=AAMeJbiAwQEcDKbAoAAkP60PG2R6QAvgNaA6AFACM2ABuQBB#
 | 
						|
                // is this really something people need since you can also write
 | 
						|
                // /prefix_:p()_suffix
 | 
						|
                if (char === ')') {
 | 
						|
                    // handle the escaped )
 | 
						|
                    if (customRe[customRe.length - 1] == '\\')
 | 
						|
                        customRe = customRe.slice(0, -1) + char;
 | 
						|
                    else
 | 
						|
                        state = 3 /* TokenizerState.ParamRegExpEnd */;
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    customRe += char;
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            case 3 /* TokenizerState.ParamRegExpEnd */:
 | 
						|
                // same as finalizing a param
 | 
						|
                consumeBuffer();
 | 
						|
                state = 0 /* TokenizerState.Static */;
 | 
						|
                // go back one character if we were not modifying
 | 
						|
                if (char !== '*' && char !== '?' && char !== '+')
 | 
						|
                    i--;
 | 
						|
                customRe = '';
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                crash('Unknown state');
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (state === 2 /* TokenizerState.ParamRegExp */)
 | 
						|
        crash(`Unfinished custom RegExp for param "${buffer}"`);
 | 
						|
    consumeBuffer();
 | 
						|
    finalizeSegment();
 | 
						|
    // tokenCache.set(path, tokens)
 | 
						|
    return tokens;
 | 
						|
}
 | 
						|
 | 
						|
function createRouteRecordMatcher(record, parent, options) {
 | 
						|
    const parser = tokensToParser(tokenizePath(record.path), options);
 | 
						|
    const matcher = assign(parser, {
 | 
						|
        record,
 | 
						|
        parent,
 | 
						|
        // these needs to be populated by the parent
 | 
						|
        children: [],
 | 
						|
        alias: [],
 | 
						|
    });
 | 
						|
    if (parent) {
 | 
						|
        // both are aliases or both are not aliases
 | 
						|
        // we don't want to mix them because the order is used when
 | 
						|
        // passing originalRecord in Matcher.addRoute
 | 
						|
        if (!matcher.record.aliasOf === !parent.record.aliasOf)
 | 
						|
            parent.children.push(matcher);
 | 
						|
    }
 | 
						|
    return matcher;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates a Router Matcher.
 | 
						|
 *
 | 
						|
 * @internal
 | 
						|
 * @param routes - array of initial routes
 | 
						|
 * @param globalOptions - global route options
 | 
						|
 */
 | 
						|
function createRouterMatcher(routes, globalOptions) {
 | 
						|
    // normalized ordered array of matchers
 | 
						|
    const matchers = [];
 | 
						|
    const matcherMap = new Map();
 | 
						|
    globalOptions = mergeOptions({ strict: false, end: true, sensitive: false }, globalOptions);
 | 
						|
    function getRecordMatcher(name) {
 | 
						|
        return matcherMap.get(name);
 | 
						|
    }
 | 
						|
    function addRoute(record, parent, originalRecord) {
 | 
						|
        // used later on to remove by name
 | 
						|
        const isRootAdd = !originalRecord;
 | 
						|
        const mainNormalizedRecord = normalizeRouteRecord(record);
 | 
						|
        // we might be the child of an alias
 | 
						|
        mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record;
 | 
						|
        const options = mergeOptions(globalOptions, record);
 | 
						|
        // generate an array of records to correctly handle aliases
 | 
						|
        const normalizedRecords = [mainNormalizedRecord];
 | 
						|
        if ('alias' in record) {
 | 
						|
            const aliases = typeof record.alias === 'string' ? [record.alias] : record.alias;
 | 
						|
            for (const alias of aliases) {
 | 
						|
                normalizedRecords.push(
 | 
						|
                // we need to normalize again to ensure the `mods` property
 | 
						|
                // being non enumerable
 | 
						|
                normalizeRouteRecord(assign({}, mainNormalizedRecord, {
 | 
						|
                    // this allows us to hold a copy of the `components` option
 | 
						|
                    // so that async components cache is hold on the original record
 | 
						|
                    components: originalRecord
 | 
						|
                        ? originalRecord.record.components
 | 
						|
                        : mainNormalizedRecord.components,
 | 
						|
                    path: alias,
 | 
						|
                    // we might be the child of an alias
 | 
						|
                    aliasOf: originalRecord
 | 
						|
                        ? originalRecord.record
 | 
						|
                        : mainNormalizedRecord,
 | 
						|
                    // the aliases are always of the same kind as the original since they
 | 
						|
                    // are defined on the same record
 | 
						|
                })));
 | 
						|
            }
 | 
						|
        }
 | 
						|
        let matcher;
 | 
						|
        let originalMatcher;
 | 
						|
        for (const normalizedRecord of normalizedRecords) {
 | 
						|
            const { path } = normalizedRecord;
 | 
						|
            // Build up the path for nested routes if the child isn't an absolute
 | 
						|
            // route. Only add the / delimiter if the child path isn't empty and if the
 | 
						|
            // parent path doesn't have a trailing slash
 | 
						|
            if (parent && path[0] !== '/') {
 | 
						|
                const parentPath = parent.record.path;
 | 
						|
                const connectingSlash = parentPath[parentPath.length - 1] === '/' ? '' : '/';
 | 
						|
                normalizedRecord.path =
 | 
						|
                    parent.record.path + (path && connectingSlash + path);
 | 
						|
            }
 | 
						|
            // create the object beforehand, so it can be passed to children
 | 
						|
            matcher = createRouteRecordMatcher(normalizedRecord, parent, options);
 | 
						|
            // if we are an alias we must tell the original record that we exist,
 | 
						|
            // so we can be removed
 | 
						|
            if (originalRecord) {
 | 
						|
                originalRecord.alias.push(matcher);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                // otherwise, the first record is the original and others are aliases
 | 
						|
                originalMatcher = originalMatcher || matcher;
 | 
						|
                if (originalMatcher !== matcher)
 | 
						|
                    originalMatcher.alias.push(matcher);
 | 
						|
                // remove the route if named and only for the top record (avoid in nested calls)
 | 
						|
                // this works because the original record is the first one
 | 
						|
                if (isRootAdd && record.name && !isAliasRecord(matcher)) {
 | 
						|
                    removeRoute(record.name);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Avoid adding a record that doesn't display anything. This allows passing through records without a component to
 | 
						|
            // not be reached and pass through the catch all route
 | 
						|
            if (isMatchable(matcher)) {
 | 
						|
                insertMatcher(matcher);
 | 
						|
            }
 | 
						|
            if (mainNormalizedRecord.children) {
 | 
						|
                const children = mainNormalizedRecord.children;
 | 
						|
                for (let i = 0; i < children.length; i++) {
 | 
						|
                    addRoute(children[i], matcher, originalRecord && originalRecord.children[i]);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // if there was no original record, then the first one was not an alias and all
 | 
						|
            // other aliases (if any) need to reference this record when adding children
 | 
						|
            originalRecord = originalRecord || matcher;
 | 
						|
            // TODO: add normalized records for more flexibility
 | 
						|
            // if (parent && isAliasRecord(originalRecord)) {
 | 
						|
            //   parent.children.push(originalRecord)
 | 
						|
            // }
 | 
						|
        }
 | 
						|
        return originalMatcher
 | 
						|
            ? () => {
 | 
						|
                // since other matchers are aliases, they should be removed by the original matcher
 | 
						|
                removeRoute(originalMatcher);
 | 
						|
            }
 | 
						|
            : noop;
 | 
						|
    }
 | 
						|
    function removeRoute(matcherRef) {
 | 
						|
        if (isRouteName(matcherRef)) {
 | 
						|
            const matcher = matcherMap.get(matcherRef);
 | 
						|
            if (matcher) {
 | 
						|
                matcherMap.delete(matcherRef);
 | 
						|
                matchers.splice(matchers.indexOf(matcher), 1);
 | 
						|
                matcher.children.forEach(removeRoute);
 | 
						|
                matcher.alias.forEach(removeRoute);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            const index = matchers.indexOf(matcherRef);
 | 
						|
            if (index > -1) {
 | 
						|
                matchers.splice(index, 1);
 | 
						|
                if (matcherRef.record.name)
 | 
						|
                    matcherMap.delete(matcherRef.record.name);
 | 
						|
                matcherRef.children.forEach(removeRoute);
 | 
						|
                matcherRef.alias.forEach(removeRoute);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    function getRoutes() {
 | 
						|
        return matchers;
 | 
						|
    }
 | 
						|
    function insertMatcher(matcher) {
 | 
						|
        const index = findInsertionIndex(matcher, matchers);
 | 
						|
        matchers.splice(index, 0, matcher);
 | 
						|
        // only add the original record to the name map
 | 
						|
        if (matcher.record.name && !isAliasRecord(matcher))
 | 
						|
            matcherMap.set(matcher.record.name, matcher);
 | 
						|
    }
 | 
						|
    function resolve(location, currentLocation) {
 | 
						|
        let matcher;
 | 
						|
        let params = {};
 | 
						|
        let path;
 | 
						|
        let name;
 | 
						|
        if ('name' in location && location.name) {
 | 
						|
            matcher = matcherMap.get(location.name);
 | 
						|
            if (!matcher)
 | 
						|
                throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, {
 | 
						|
                    location,
 | 
						|
                });
 | 
						|
            name = matcher.record.name;
 | 
						|
            params = assign(
 | 
						|
            // paramsFromLocation is a new object
 | 
						|
            paramsFromLocation(currentLocation.params, 
 | 
						|
            // only keep params that exist in the resolved location
 | 
						|
            // only keep optional params coming from a parent record
 | 
						|
            matcher.keys
 | 
						|
                .filter(k => !k.optional)
 | 
						|
                .concat(matcher.parent ? matcher.parent.keys.filter(k => k.optional) : [])
 | 
						|
                .map(k => k.name)), 
 | 
						|
            // discard any existing params in the current location that do not exist here
 | 
						|
            // #1497 this ensures better active/exact matching
 | 
						|
            location.params &&
 | 
						|
                paramsFromLocation(location.params, matcher.keys.map(k => k.name)));
 | 
						|
            // throws if cannot be stringified
 | 
						|
            path = matcher.stringify(params);
 | 
						|
        }
 | 
						|
        else if (location.path != null) {
 | 
						|
            // no need to resolve the path with the matcher as it was provided
 | 
						|
            // this also allows the user to control the encoding
 | 
						|
            path = location.path;
 | 
						|
            matcher = matchers.find(m => m.re.test(path));
 | 
						|
            // matcher should have a value after the loop
 | 
						|
            if (matcher) {
 | 
						|
                // we know the matcher works because we tested the regexp
 | 
						|
                params = matcher.parse(path);
 | 
						|
                name = matcher.record.name;
 | 
						|
            }
 | 
						|
            // location is a relative path
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            // match by name or path of current route
 | 
						|
            matcher = currentLocation.name
 | 
						|
                ? matcherMap.get(currentLocation.name)
 | 
						|
                : matchers.find(m => m.re.test(currentLocation.path));
 | 
						|
            if (!matcher)
 | 
						|
                throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, {
 | 
						|
                    location,
 | 
						|
                    currentLocation,
 | 
						|
                });
 | 
						|
            name = matcher.record.name;
 | 
						|
            // since we are navigating to the same location, we don't need to pick the
 | 
						|
            // params like when `name` is provided
 | 
						|
            params = assign({}, currentLocation.params, location.params);
 | 
						|
            path = matcher.stringify(params);
 | 
						|
        }
 | 
						|
        const matched = [];
 | 
						|
        let parentMatcher = matcher;
 | 
						|
        while (parentMatcher) {
 | 
						|
            // reversed order so parents are at the beginning
 | 
						|
            matched.unshift(parentMatcher.record);
 | 
						|
            parentMatcher = parentMatcher.parent;
 | 
						|
        }
 | 
						|
        return {
 | 
						|
            name,
 | 
						|
            path,
 | 
						|
            params,
 | 
						|
            matched,
 | 
						|
            meta: mergeMetaFields(matched),
 | 
						|
        };
 | 
						|
    }
 | 
						|
    // add initial routes
 | 
						|
    routes.forEach(route => addRoute(route));
 | 
						|
    function clearRoutes() {
 | 
						|
        matchers.length = 0;
 | 
						|
        matcherMap.clear();
 | 
						|
    }
 | 
						|
    return {
 | 
						|
        addRoute,
 | 
						|
        resolve,
 | 
						|
        removeRoute,
 | 
						|
        clearRoutes,
 | 
						|
        getRoutes,
 | 
						|
        getRecordMatcher,
 | 
						|
    };
 | 
						|
}
 | 
						|
function paramsFromLocation(params, keys) {
 | 
						|
    const newParams = {};
 | 
						|
    for (const key of keys) {
 | 
						|
        if (key in params)
 | 
						|
            newParams[key] = params[key];
 | 
						|
    }
 | 
						|
    return newParams;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Normalizes a RouteRecordRaw. Creates a copy
 | 
						|
 *
 | 
						|
 * @param record
 | 
						|
 * @returns the normalized version
 | 
						|
 */
 | 
						|
function normalizeRouteRecord(record) {
 | 
						|
    const normalized = {
 | 
						|
        path: record.path,
 | 
						|
        redirect: record.redirect,
 | 
						|
        name: record.name,
 | 
						|
        meta: record.meta || {},
 | 
						|
        aliasOf: record.aliasOf,
 | 
						|
        beforeEnter: record.beforeEnter,
 | 
						|
        props: normalizeRecordProps(record),
 | 
						|
        children: record.children || [],
 | 
						|
        instances: {},
 | 
						|
        leaveGuards: new Set(),
 | 
						|
        updateGuards: new Set(),
 | 
						|
        enterCallbacks: {},
 | 
						|
        // must be declared afterwards
 | 
						|
        // mods: {},
 | 
						|
        components: 'components' in record
 | 
						|
            ? record.components || null
 | 
						|
            : record.component && { default: record.component },
 | 
						|
    };
 | 
						|
    // mods contain modules and shouldn't be copied,
 | 
						|
    // logged or anything. It's just used for internal
 | 
						|
    // advanced use cases like data loaders
 | 
						|
    Object.defineProperty(normalized, 'mods', {
 | 
						|
        value: {},
 | 
						|
    });
 | 
						|
    return normalized;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Normalize the optional `props` in a record to always be an object similar to
 | 
						|
 * components. Also accept a boolean for components.
 | 
						|
 * @param record
 | 
						|
 */
 | 
						|
function normalizeRecordProps(record) {
 | 
						|
    const propsObject = {};
 | 
						|
    // props does not exist on redirect records, but we can set false directly
 | 
						|
    const props = record.props || false;
 | 
						|
    if ('component' in record) {
 | 
						|
        propsObject.default = props;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        // NOTE: we could also allow a function to be applied to every component.
 | 
						|
        // Would need user feedback for use cases
 | 
						|
        for (const name in record.components)
 | 
						|
            propsObject[name] = typeof props === 'object' ? props[name] : props;
 | 
						|
    }
 | 
						|
    return propsObject;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Checks if a record or any of its parent is an alias
 | 
						|
 * @param record
 | 
						|
 */
 | 
						|
function isAliasRecord(record) {
 | 
						|
    while (record) {
 | 
						|
        if (record.record.aliasOf)
 | 
						|
            return true;
 | 
						|
        record = record.parent;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Merge meta fields of an array of records
 | 
						|
 *
 | 
						|
 * @param matched - array of matched records
 | 
						|
 */
 | 
						|
function mergeMetaFields(matched) {
 | 
						|
    return matched.reduce((meta, record) => assign(meta, record.meta), {});
 | 
						|
}
 | 
						|
function mergeOptions(defaults, partialOptions) {
 | 
						|
    const options = {};
 | 
						|
    for (const key in defaults) {
 | 
						|
        options[key] = key in partialOptions ? partialOptions[key] : defaults[key];
 | 
						|
    }
 | 
						|
    return options;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Performs a binary search to find the correct insertion index for a new matcher.
 | 
						|
 *
 | 
						|
 * Matchers are primarily sorted by their score. If scores are tied then we also consider parent/child relationships,
 | 
						|
 * with descendants coming before ancestors. If there's still a tie, new routes are inserted after existing routes.
 | 
						|
 *
 | 
						|
 * @param matcher - new matcher to be inserted
 | 
						|
 * @param matchers - existing matchers
 | 
						|
 */
 | 
						|
function findInsertionIndex(matcher, matchers) {
 | 
						|
    // First phase: binary search based on score
 | 
						|
    let lower = 0;
 | 
						|
    let upper = matchers.length;
 | 
						|
    while (lower !== upper) {
 | 
						|
        const mid = (lower + upper) >> 1;
 | 
						|
        const sortOrder = comparePathParserScore(matcher, matchers[mid]);
 | 
						|
        if (sortOrder < 0) {
 | 
						|
            upper = mid;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            lower = mid + 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // Second phase: check for an ancestor with the same score
 | 
						|
    const insertionAncestor = getInsertionAncestor(matcher);
 | 
						|
    if (insertionAncestor) {
 | 
						|
        upper = matchers.lastIndexOf(insertionAncestor, upper - 1);
 | 
						|
    }
 | 
						|
    return upper;
 | 
						|
}
 | 
						|
function getInsertionAncestor(matcher) {
 | 
						|
    let ancestor = matcher;
 | 
						|
    while ((ancestor = ancestor.parent)) {
 | 
						|
        if (isMatchable(ancestor) &&
 | 
						|
            comparePathParserScore(matcher, ancestor) === 0) {
 | 
						|
            return ancestor;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Checks if a matcher can be reachable. This means if it's possible to reach it as a route. For example, routes without
 | 
						|
 * a component, or name, or redirect, are just used to group other routes.
 | 
						|
 * @param matcher
 | 
						|
 * @param matcher.record record of the matcher
 | 
						|
 * @returns
 | 
						|
 */
 | 
						|
function isMatchable({ record }) {
 | 
						|
    return !!(record.name ||
 | 
						|
        (record.components && Object.keys(record.components).length) ||
 | 
						|
        record.redirect);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Transforms a queryString into a {@link LocationQuery} object. Accept both, a
 | 
						|
 * version with the leading `?` and without Should work as URLSearchParams
 | 
						|
 | 
						|
 * @internal
 | 
						|
 *
 | 
						|
 * @param search - search string to parse
 | 
						|
 * @returns a query object
 | 
						|
 */
 | 
						|
function parseQuery(search) {
 | 
						|
    const query = {};
 | 
						|
    // avoid creating an object with an empty key and empty value
 | 
						|
    // because of split('&')
 | 
						|
    if (search === '' || search === '?')
 | 
						|
        return query;
 | 
						|
    const hasLeadingIM = search[0] === '?';
 | 
						|
    const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&');
 | 
						|
    for (let i = 0; i < searchParams.length; ++i) {
 | 
						|
        // pre decode the + into space
 | 
						|
        const searchParam = searchParams[i].replace(PLUS_RE, ' ');
 | 
						|
        // allow the = character
 | 
						|
        const eqPos = searchParam.indexOf('=');
 | 
						|
        const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
 | 
						|
        const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
 | 
						|
        if (key in query) {
 | 
						|
            // an extra variable for ts types
 | 
						|
            let currentValue = query[key];
 | 
						|
            if (!isArray(currentValue)) {
 | 
						|
                currentValue = query[key] = [currentValue];
 | 
						|
            }
 | 
						|
            currentValue.push(value);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            query[key] = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return query;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Stringifies a {@link LocationQueryRaw} object. Like `URLSearchParams`, it
 | 
						|
 * doesn't prepend a `?`
 | 
						|
 *
 | 
						|
 * @internal
 | 
						|
 *
 | 
						|
 * @param query - query object to stringify
 | 
						|
 * @returns string version of the query without the leading `?`
 | 
						|
 */
 | 
						|
function stringifyQuery(query) {
 | 
						|
    let search = '';
 | 
						|
    for (let key in query) {
 | 
						|
        const value = query[key];
 | 
						|
        key = encodeQueryKey(key);
 | 
						|
        if (value == null) {
 | 
						|
            // only null adds the value
 | 
						|
            if (value !== undefined) {
 | 
						|
                search += (search.length ? '&' : '') + key;
 | 
						|
            }
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        // keep null values
 | 
						|
        const values = isArray(value)
 | 
						|
            ? value.map(v => v && encodeQueryValue(v))
 | 
						|
            : [value && encodeQueryValue(value)];
 | 
						|
        values.forEach(value => {
 | 
						|
            // skip undefined values in arrays as if they were not present
 | 
						|
            // smaller code than using filter
 | 
						|
            if (value !== undefined) {
 | 
						|
                // only append & with non-empty search
 | 
						|
                search += (search.length ? '&' : '') + key;
 | 
						|
                if (value != null)
 | 
						|
                    search += '=' + value;
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
    return search;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Transforms a {@link LocationQueryRaw} into a {@link LocationQuery} by casting
 | 
						|
 * numbers into strings, removing keys with an undefined value and replacing
 | 
						|
 * undefined with null in arrays
 | 
						|
 *
 | 
						|
 * @param query - query object to normalize
 | 
						|
 * @returns a normalized query object
 | 
						|
 */
 | 
						|
function normalizeQuery(query) {
 | 
						|
    const normalizedQuery = {};
 | 
						|
    for (const key in query) {
 | 
						|
        const value = query[key];
 | 
						|
        if (value !== undefined) {
 | 
						|
            normalizedQuery[key] = isArray(value)
 | 
						|
                ? value.map(v => (v == null ? null : '' + v))
 | 
						|
                : value == null
 | 
						|
                    ? value
 | 
						|
                    : '' + value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return normalizedQuery;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * RouteRecord being rendered by the closest ancestor Router View. Used for
 | 
						|
 * `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View
 | 
						|
 * Location Matched
 | 
						|
 *
 | 
						|
 * @internal
 | 
						|
 */
 | 
						|
const matchedRouteKey = Symbol('');
 | 
						|
/**
 | 
						|
 * Allows overriding the router view depth to control which component in
 | 
						|
 * `matched` is rendered. rvd stands for Router View Depth
 | 
						|
 *
 | 
						|
 * @internal
 | 
						|
 */
 | 
						|
const viewDepthKey = Symbol('');
 | 
						|
/**
 | 
						|
 * Allows overriding the router instance returned by `useRouter` in tests. r
 | 
						|
 * stands for router
 | 
						|
 *
 | 
						|
 * @internal
 | 
						|
 */
 | 
						|
const routerKey = Symbol('');
 | 
						|
/**
 | 
						|
 * Allows overriding the current route returned by `useRoute` in tests. rl
 | 
						|
 * stands for route location
 | 
						|
 *
 | 
						|
 * @internal
 | 
						|
 */
 | 
						|
const routeLocationKey = Symbol('');
 | 
						|
/**
 | 
						|
 * Allows overriding the current route used by router-view. Internally this is
 | 
						|
 * used when the `route` prop is passed.
 | 
						|
 *
 | 
						|
 * @internal
 | 
						|
 */
 | 
						|
const routerViewLocationKey = Symbol('');
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a list of callbacks that can be reset. Used to create before and after navigation guards list
 | 
						|
 */
 | 
						|
function useCallbacks() {
 | 
						|
    let handlers = [];
 | 
						|
    function add(handler) {
 | 
						|
        handlers.push(handler);
 | 
						|
        return () => {
 | 
						|
            const i = handlers.indexOf(handler);
 | 
						|
            if (i > -1)
 | 
						|
                handlers.splice(i, 1);
 | 
						|
        };
 | 
						|
    }
 | 
						|
    function reset() {
 | 
						|
        handlers = [];
 | 
						|
    }
 | 
						|
    return {
 | 
						|
        add,
 | 
						|
        list: () => handlers.slice(),
 | 
						|
        reset,
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
function registerGuard(record, name, guard) {
 | 
						|
    const removeFromList = () => {
 | 
						|
        record[name].delete(guard);
 | 
						|
    };
 | 
						|
    vue.onUnmounted(removeFromList);
 | 
						|
    vue.onDeactivated(removeFromList);
 | 
						|
    vue.onActivated(() => {
 | 
						|
        record[name].add(guard);
 | 
						|
    });
 | 
						|
    record[name].add(guard);
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Add a navigation guard that triggers whenever the component for the current
 | 
						|
 * location is about to be left. Similar to {@link beforeRouteLeave} but can be
 | 
						|
 * used in any component. The guard is removed when the component is unmounted.
 | 
						|
 *
 | 
						|
 * @param leaveGuard - {@link NavigationGuard}
 | 
						|
 */
 | 
						|
function onBeforeRouteLeave(leaveGuard) {
 | 
						|
    const activeRecord = vue.inject(matchedRouteKey, 
 | 
						|
    // to avoid warning
 | 
						|
    {}).value;
 | 
						|
    if (!activeRecord) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    registerGuard(activeRecord, 'leaveGuards', leaveGuard);
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Add a navigation guard that triggers whenever the current location is about
 | 
						|
 * to be updated. Similar to {@link beforeRouteUpdate} but can be used in any
 | 
						|
 * component. The guard is removed when the component is unmounted.
 | 
						|
 *
 | 
						|
 * @param updateGuard - {@link NavigationGuard}
 | 
						|
 */
 | 
						|
function onBeforeRouteUpdate(updateGuard) {
 | 
						|
    const activeRecord = vue.inject(matchedRouteKey, 
 | 
						|
    // to avoid warning
 | 
						|
    {}).value;
 | 
						|
    if (!activeRecord) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    registerGuard(activeRecord, 'updateGuards', updateGuard);
 | 
						|
}
 | 
						|
function guardToPromiseFn(guard, to, from, record, name, runWithContext = fn => fn()) {
 | 
						|
    // keep a reference to the enterCallbackArray to prevent pushing callbacks if a new navigation took place
 | 
						|
    const enterCallbackArray = record &&
 | 
						|
        // name is defined if record is because of the function overload
 | 
						|
        (record.enterCallbacks[name] = record.enterCallbacks[name] || []);
 | 
						|
    return () => new Promise((resolve, reject) => {
 | 
						|
        const next = (valid) => {
 | 
						|
            if (valid === false) {
 | 
						|
                reject(createRouterError(4 /* ErrorTypes.NAVIGATION_ABORTED */, {
 | 
						|
                    from,
 | 
						|
                    to,
 | 
						|
                }));
 | 
						|
            }
 | 
						|
            else if (valid instanceof Error) {
 | 
						|
                reject(valid);
 | 
						|
            }
 | 
						|
            else if (isRouteLocation(valid)) {
 | 
						|
                reject(createRouterError(2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */, {
 | 
						|
                    from: to,
 | 
						|
                    to: valid,
 | 
						|
                }));
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                if (enterCallbackArray &&
 | 
						|
                    // since enterCallbackArray is truthy, both record and name also are
 | 
						|
                    record.enterCallbacks[name] === enterCallbackArray &&
 | 
						|
                    typeof valid === 'function') {
 | 
						|
                    enterCallbackArray.push(valid);
 | 
						|
                }
 | 
						|
                resolve();
 | 
						|
            }
 | 
						|
        };
 | 
						|
        // wrapping with Promise.resolve allows it to work with both async and sync guards
 | 
						|
        const guardReturn = runWithContext(() => guard.call(record && record.instances[name], to, from, next));
 | 
						|
        let guardCall = Promise.resolve(guardReturn);
 | 
						|
        if (guard.length < 3)
 | 
						|
            guardCall = guardCall.then(next);
 | 
						|
        guardCall.catch(err => reject(err));
 | 
						|
    });
 | 
						|
}
 | 
						|
function extractComponentsGuards(matched, guardType, to, from, runWithContext = fn => fn()) {
 | 
						|
    const guards = [];
 | 
						|
    for (const record of matched) {
 | 
						|
        for (const name in record.components) {
 | 
						|
            let rawComponent = record.components[name];
 | 
						|
            // skip update and leave guards if the route component is not mounted
 | 
						|
            if (guardType !== 'beforeRouteEnter' && !record.instances[name])
 | 
						|
                continue;
 | 
						|
            if (isRouteComponent(rawComponent)) {
 | 
						|
                // __vccOpts is added by vue-class-component and contain the regular options
 | 
						|
                const options = rawComponent.__vccOpts || rawComponent;
 | 
						|
                const guard = options[guardType];
 | 
						|
                guard &&
 | 
						|
                    guards.push(guardToPromiseFn(guard, to, from, record, name, runWithContext));
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                // start requesting the chunk already
 | 
						|
                let componentPromise = rawComponent();
 | 
						|
                guards.push(() => componentPromise.then(resolved => {
 | 
						|
                    if (!resolved)
 | 
						|
                        throw new Error(`Couldn't resolve component "${name}" at "${record.path}"`);
 | 
						|
                    const resolvedComponent = isESModule(resolved)
 | 
						|
                        ? resolved.default
 | 
						|
                        : resolved;
 | 
						|
                    // keep the resolved module for plugins like data loaders
 | 
						|
                    record.mods[name] = resolved;
 | 
						|
                    // replace the function with the resolved component
 | 
						|
                    // cannot be null or undefined because we went into the for loop
 | 
						|
                    record.components[name] = resolvedComponent;
 | 
						|
                    // __vccOpts is added by vue-class-component and contain the regular options
 | 
						|
                    const options = resolvedComponent.__vccOpts || resolvedComponent;
 | 
						|
                    const guard = options[guardType];
 | 
						|
                    return (guard &&
 | 
						|
                        guardToPromiseFn(guard, to, from, record, name, runWithContext)());
 | 
						|
                }));
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return guards;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Ensures a route is loaded, so it can be passed as o prop to `<RouterView>`.
 | 
						|
 *
 | 
						|
 * @param route - resolved route to load
 | 
						|
 */
 | 
						|
function loadRouteLocation(route) {
 | 
						|
    return route.matched.every(record => record.redirect)
 | 
						|
        ? Promise.reject(new Error('Cannot load a route that redirects.'))
 | 
						|
        : Promise.all(route.matched.map(record => record.components &&
 | 
						|
            Promise.all(Object.keys(record.components).reduce((promises, name) => {
 | 
						|
                const rawComponent = record.components[name];
 | 
						|
                if (typeof rawComponent === 'function' &&
 | 
						|
                    !('displayName' in rawComponent)) {
 | 
						|
                    promises.push(rawComponent().then(resolved => {
 | 
						|
                        if (!resolved)
 | 
						|
                            return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}". Ensure you passed a function that returns a promise.`));
 | 
						|
                        const resolvedComponent = isESModule(resolved)
 | 
						|
                            ? resolved.default
 | 
						|
                            : resolved;
 | 
						|
                        // keep the resolved module for plugins like data loaders
 | 
						|
                        record.mods[name] = resolved;
 | 
						|
                        // replace the function with the resolved component
 | 
						|
                        // cannot be null or undefined because we went into the for loop
 | 
						|
                        record.components[name] = resolvedComponent;
 | 
						|
                        return;
 | 
						|
                    }));
 | 
						|
                }
 | 
						|
                return promises;
 | 
						|
            }, [])))).then(() => route);
 | 
						|
}
 | 
						|
 | 
						|
// TODO: we could allow currentRoute as a prop to expose `isActive` and
 | 
						|
// `isExactActive` behavior should go through an RFC
 | 
						|
/**
 | 
						|
 * Returns the internal behavior of a {@link RouterLink} without the rendering part.
 | 
						|
 *
 | 
						|
 * @param props - a `to` location and an optional `replace` flag
 | 
						|
 */
 | 
						|
function useLink(props) {
 | 
						|
    const router = vue.inject(routerKey);
 | 
						|
    const currentRoute = vue.inject(routeLocationKey);
 | 
						|
    const route = vue.computed(() => {
 | 
						|
        const to = vue.unref(props.to);
 | 
						|
        return router.resolve(to);
 | 
						|
    });
 | 
						|
    const activeRecordIndex = vue.computed(() => {
 | 
						|
        const { matched } = route.value;
 | 
						|
        const { length } = matched;
 | 
						|
        const routeMatched = matched[length - 1];
 | 
						|
        const currentMatched = currentRoute.matched;
 | 
						|
        if (!routeMatched || !currentMatched.length)
 | 
						|
            return -1;
 | 
						|
        const index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched));
 | 
						|
        if (index > -1)
 | 
						|
            return index;
 | 
						|
        // possible parent record
 | 
						|
        const parentRecordPath = getOriginalPath(matched[length - 2]);
 | 
						|
        return (
 | 
						|
        // we are dealing with nested routes
 | 
						|
        length > 1 &&
 | 
						|
            // if the parent and matched route have the same path, this link is
 | 
						|
            // referring to the empty child. Or we currently are on a different
 | 
						|
            // child of the same parent
 | 
						|
            getOriginalPath(routeMatched) === parentRecordPath &&
 | 
						|
            // avoid comparing the child with its parent
 | 
						|
            currentMatched[currentMatched.length - 1].path !== parentRecordPath
 | 
						|
            ? currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2]))
 | 
						|
            : index);
 | 
						|
    });
 | 
						|
    const isActive = vue.computed(() => activeRecordIndex.value > -1 &&
 | 
						|
        includesParams(currentRoute.params, route.value.params));
 | 
						|
    const isExactActive = vue.computed(() => activeRecordIndex.value > -1 &&
 | 
						|
        activeRecordIndex.value === currentRoute.matched.length - 1 &&
 | 
						|
        isSameRouteLocationParams(currentRoute.params, route.value.params));
 | 
						|
    function navigate(e = {}) {
 | 
						|
        if (guardEvent(e)) {
 | 
						|
            const p = router[vue.unref(props.replace) ? 'replace' : 'push'](vue.unref(props.to)
 | 
						|
            // avoid uncaught errors are they are logged anyway
 | 
						|
            ).catch(noop);
 | 
						|
            if (props.viewTransition &&
 | 
						|
                typeof document !== 'undefined' &&
 | 
						|
                'startViewTransition' in document) {
 | 
						|
                document.startViewTransition(() => p);
 | 
						|
            }
 | 
						|
            return p;
 | 
						|
        }
 | 
						|
        return Promise.resolve();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * NOTE: update {@link _RouterLinkI}'s `$slots` type when updating this
 | 
						|
     */
 | 
						|
    return {
 | 
						|
        route,
 | 
						|
        href: vue.computed(() => route.value.href),
 | 
						|
        isActive,
 | 
						|
        isExactActive,
 | 
						|
        navigate,
 | 
						|
    };
 | 
						|
}
 | 
						|
function preferSingleVNode(vnodes) {
 | 
						|
    return vnodes.length === 1 ? vnodes[0] : vnodes;
 | 
						|
}
 | 
						|
const RouterLinkImpl = /*#__PURE__*/ vue.defineComponent({
 | 
						|
    name: 'RouterLink',
 | 
						|
    compatConfig: { MODE: 3 },
 | 
						|
    props: {
 | 
						|
        to: {
 | 
						|
            type: [String, Object],
 | 
						|
            required: true,
 | 
						|
        },
 | 
						|
        replace: Boolean,
 | 
						|
        activeClass: String,
 | 
						|
        // inactiveClass: String,
 | 
						|
        exactActiveClass: String,
 | 
						|
        custom: Boolean,
 | 
						|
        ariaCurrentValue: {
 | 
						|
            type: String,
 | 
						|
            default: 'page',
 | 
						|
        },
 | 
						|
        viewTransition: Boolean,
 | 
						|
    },
 | 
						|
    useLink,
 | 
						|
    setup(props, { slots }) {
 | 
						|
        const link = vue.reactive(useLink(props));
 | 
						|
        const { options } = vue.inject(routerKey);
 | 
						|
        const elClass = vue.computed(() => ({
 | 
						|
            [getLinkClass(props.activeClass, options.linkActiveClass, 'router-link-active')]: link.isActive,
 | 
						|
            // [getLinkClass(
 | 
						|
            //   props.inactiveClass,
 | 
						|
            //   options.linkInactiveClass,
 | 
						|
            //   'router-link-inactive'
 | 
						|
            // )]: !link.isExactActive,
 | 
						|
            [getLinkClass(props.exactActiveClass, options.linkExactActiveClass, 'router-link-exact-active')]: link.isExactActive,
 | 
						|
        }));
 | 
						|
        return () => {
 | 
						|
            const children = slots.default && preferSingleVNode(slots.default(link));
 | 
						|
            return props.custom
 | 
						|
                ? children
 | 
						|
                : vue.h('a', {
 | 
						|
                    'aria-current': link.isExactActive
 | 
						|
                        ? props.ariaCurrentValue
 | 
						|
                        : null,
 | 
						|
                    href: link.href,
 | 
						|
                    // this would override user added attrs but Vue will still add
 | 
						|
                    // the listener, so we end up triggering both
 | 
						|
                    onClick: link.navigate,
 | 
						|
                    class: elClass.value,
 | 
						|
                }, children);
 | 
						|
        };
 | 
						|
    },
 | 
						|
});
 | 
						|
// export the public type for h/tsx inference
 | 
						|
// also to avoid inline import() in generated d.ts files
 | 
						|
/**
 | 
						|
 * Component to render a link that triggers a navigation on click.
 | 
						|
 */
 | 
						|
const RouterLink = RouterLinkImpl;
 | 
						|
function guardEvent(e) {
 | 
						|
    // don't redirect with control keys
 | 
						|
    if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
 | 
						|
        return;
 | 
						|
    // don't redirect when preventDefault called
 | 
						|
    if (e.defaultPrevented)
 | 
						|
        return;
 | 
						|
    // don't redirect on right click
 | 
						|
    if (e.button !== undefined && e.button !== 0)
 | 
						|
        return;
 | 
						|
    // don't redirect if `target="_blank"`
 | 
						|
    // @ts-expect-error getAttribute does exist
 | 
						|
    if (e.currentTarget && e.currentTarget.getAttribute) {
 | 
						|
        // @ts-expect-error getAttribute exists
 | 
						|
        const target = e.currentTarget.getAttribute('target');
 | 
						|
        if (/\b_blank\b/i.test(target))
 | 
						|
            return;
 | 
						|
    }
 | 
						|
    // this may be a Weex event which doesn't have this method
 | 
						|
    if (e.preventDefault)
 | 
						|
        e.preventDefault();
 | 
						|
    return true;
 | 
						|
}
 | 
						|
function includesParams(outer, inner) {
 | 
						|
    for (const key in inner) {
 | 
						|
        const innerValue = inner[key];
 | 
						|
        const outerValue = outer[key];
 | 
						|
        if (typeof innerValue === 'string') {
 | 
						|
            if (innerValue !== outerValue)
 | 
						|
                return false;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            if (!isArray(outerValue) ||
 | 
						|
                outerValue.length !== innerValue.length ||
 | 
						|
                innerValue.some((value, i) => value !== outerValue[i]))
 | 
						|
                return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Get the original path value of a record by following its aliasOf
 | 
						|
 * @param record
 | 
						|
 */
 | 
						|
function getOriginalPath(record) {
 | 
						|
    return record ? (record.aliasOf ? record.aliasOf.path : record.path) : '';
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Utility class to get the active class based on defaults.
 | 
						|
 * @param propClass
 | 
						|
 * @param globalClass
 | 
						|
 * @param defaultClass
 | 
						|
 */
 | 
						|
const getLinkClass = (propClass, globalClass, defaultClass) => propClass != null
 | 
						|
    ? propClass
 | 
						|
    : globalClass != null
 | 
						|
        ? globalClass
 | 
						|
        : defaultClass;
 | 
						|
 | 
						|
const RouterViewImpl = /*#__PURE__*/ vue.defineComponent({
 | 
						|
    name: 'RouterView',
 | 
						|
    // #674 we manually inherit them
 | 
						|
    inheritAttrs: false,
 | 
						|
    props: {
 | 
						|
        name: {
 | 
						|
            type: String,
 | 
						|
            default: 'default',
 | 
						|
        },
 | 
						|
        route: Object,
 | 
						|
    },
 | 
						|
    // Better compat for @vue/compat users
 | 
						|
    // https://github.com/vuejs/router/issues/1315
 | 
						|
    compatConfig: { MODE: 3 },
 | 
						|
    setup(props, { attrs, slots }) {
 | 
						|
        const injectedRoute = vue.inject(routerViewLocationKey);
 | 
						|
        const routeToDisplay = vue.computed(() => props.route || injectedRoute.value);
 | 
						|
        const injectedDepth = vue.inject(viewDepthKey, 0);
 | 
						|
        // The depth changes based on empty components option, which allows passthrough routes e.g. routes with children
 | 
						|
        // that are used to reuse the `path` property
 | 
						|
        const depth = vue.computed(() => {
 | 
						|
            let initialDepth = vue.unref(injectedDepth);
 | 
						|
            const { matched } = routeToDisplay.value;
 | 
						|
            let matchedRoute;
 | 
						|
            while ((matchedRoute = matched[initialDepth]) &&
 | 
						|
                !matchedRoute.components) {
 | 
						|
                initialDepth++;
 | 
						|
            }
 | 
						|
            return initialDepth;
 | 
						|
        });
 | 
						|
        const matchedRouteRef = vue.computed(() => routeToDisplay.value.matched[depth.value]);
 | 
						|
        vue.provide(viewDepthKey, vue.computed(() => depth.value + 1));
 | 
						|
        vue.provide(matchedRouteKey, matchedRouteRef);
 | 
						|
        vue.provide(routerViewLocationKey, routeToDisplay);
 | 
						|
        const viewRef = vue.ref();
 | 
						|
        // watch at the same time the component instance, the route record we are
 | 
						|
        // rendering, and the name
 | 
						|
        vue.watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [oldInstance, from, oldName]) => {
 | 
						|
            // copy reused instances
 | 
						|
            if (to) {
 | 
						|
                // this will update the instance for new instances as well as reused
 | 
						|
                // instances when navigating to a new route
 | 
						|
                to.instances[name] = instance;
 | 
						|
                // the component instance is reused for a different route or name, so
 | 
						|
                // we copy any saved update or leave guards. With async setup, the
 | 
						|
                // mounting component will mount before the matchedRoute changes,
 | 
						|
                // making instance === oldInstance, so we check if guards have been
 | 
						|
                // added before. This works because we remove guards when
 | 
						|
                // unmounting/deactivating components
 | 
						|
                if (from && from !== to && instance && instance === oldInstance) {
 | 
						|
                    if (!to.leaveGuards.size) {
 | 
						|
                        to.leaveGuards = from.leaveGuards;
 | 
						|
                    }
 | 
						|
                    if (!to.updateGuards.size) {
 | 
						|
                        to.updateGuards = from.updateGuards;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // trigger beforeRouteEnter next callbacks
 | 
						|
            if (instance &&
 | 
						|
                to &&
 | 
						|
                // if there is no instance but to and from are the same this might be
 | 
						|
                // the first visit
 | 
						|
                (!from || !isSameRouteRecord(to, from) || !oldInstance)) {
 | 
						|
                (to.enterCallbacks[name] || []).forEach(callback => callback(instance));
 | 
						|
            }
 | 
						|
        }, { flush: 'post' });
 | 
						|
        return () => {
 | 
						|
            const route = routeToDisplay.value;
 | 
						|
            // we need the value at the time we render because when we unmount, we
 | 
						|
            // navigated to a different location so the value is different
 | 
						|
            const currentName = props.name;
 | 
						|
            const matchedRoute = matchedRouteRef.value;
 | 
						|
            const ViewComponent = matchedRoute && matchedRoute.components[currentName];
 | 
						|
            if (!ViewComponent) {
 | 
						|
                return normalizeSlot(slots.default, { Component: ViewComponent, route });
 | 
						|
            }
 | 
						|
            // props from route configuration
 | 
						|
            const routePropsOption = matchedRoute.props[currentName];
 | 
						|
            const routeProps = routePropsOption
 | 
						|
                ? routePropsOption === true
 | 
						|
                    ? route.params
 | 
						|
                    : typeof routePropsOption === 'function'
 | 
						|
                        ? routePropsOption(route)
 | 
						|
                        : routePropsOption
 | 
						|
                : null;
 | 
						|
            const onVnodeUnmounted = vnode => {
 | 
						|
                // remove the instance reference to prevent leak
 | 
						|
                if (vnode.component.isUnmounted) {
 | 
						|
                    matchedRoute.instances[currentName] = null;
 | 
						|
                }
 | 
						|
            };
 | 
						|
            const component = vue.h(ViewComponent, assign({}, routeProps, attrs, {
 | 
						|
                onVnodeUnmounted,
 | 
						|
                ref: viewRef,
 | 
						|
            }));
 | 
						|
            return (
 | 
						|
            // pass the vnode to the slot as a prop.
 | 
						|
            // h and <component :is="..."> both accept vnodes
 | 
						|
            normalizeSlot(slots.default, { Component: component, route }) ||
 | 
						|
                component);
 | 
						|
        };
 | 
						|
    },
 | 
						|
});
 | 
						|
function normalizeSlot(slot, data) {
 | 
						|
    if (!slot)
 | 
						|
        return null;
 | 
						|
    const slotContent = slot(data);
 | 
						|
    return slotContent.length === 1 ? slotContent[0] : slotContent;
 | 
						|
}
 | 
						|
// export the public type for h/tsx inference
 | 
						|
// also to avoid inline import() in generated d.ts files
 | 
						|
/**
 | 
						|
 * Component to display the current route the user is at.
 | 
						|
 */
 | 
						|
const RouterView = RouterViewImpl;
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates a Router instance that can be used by a Vue app.
 | 
						|
 *
 | 
						|
 * @param options - {@link RouterOptions}
 | 
						|
 */
 | 
						|
function createRouter(options) {
 | 
						|
    const matcher = createRouterMatcher(options.routes, options);
 | 
						|
    const parseQuery$1 = options.parseQuery || parseQuery;
 | 
						|
    const stringifyQuery$1 = options.stringifyQuery || stringifyQuery;
 | 
						|
    const routerHistory = options.history;
 | 
						|
    const beforeGuards = useCallbacks();
 | 
						|
    const beforeResolveGuards = useCallbacks();
 | 
						|
    const afterGuards = useCallbacks();
 | 
						|
    const currentRoute = vue.shallowRef(START_LOCATION_NORMALIZED);
 | 
						|
    let pendingLocation = START_LOCATION_NORMALIZED;
 | 
						|
    // leave the scrollRestoration if no scrollBehavior is provided
 | 
						|
    if (isBrowser && options.scrollBehavior && 'scrollRestoration' in history) {
 | 
						|
        history.scrollRestoration = 'manual';
 | 
						|
    }
 | 
						|
    const normalizeParams = applyToParams.bind(null, paramValue => '' + paramValue);
 | 
						|
    const encodeParams = applyToParams.bind(null, encodeParam);
 | 
						|
    const decodeParams = 
 | 
						|
    // @ts-expect-error: intentionally avoid the type check
 | 
						|
    applyToParams.bind(null, decode);
 | 
						|
    function addRoute(parentOrRoute, route) {
 | 
						|
        let parent;
 | 
						|
        let record;
 | 
						|
        if (isRouteName(parentOrRoute)) {
 | 
						|
            parent = matcher.getRecordMatcher(parentOrRoute);
 | 
						|
            record = route;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            record = parentOrRoute;
 | 
						|
        }
 | 
						|
        return matcher.addRoute(record, parent);
 | 
						|
    }
 | 
						|
    function removeRoute(name) {
 | 
						|
        const recordMatcher = matcher.getRecordMatcher(name);
 | 
						|
        if (recordMatcher) {
 | 
						|
            matcher.removeRoute(recordMatcher);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    function getRoutes() {
 | 
						|
        return matcher.getRoutes().map(routeMatcher => routeMatcher.record);
 | 
						|
    }
 | 
						|
    function hasRoute(name) {
 | 
						|
        return !!matcher.getRecordMatcher(name);
 | 
						|
    }
 | 
						|
    function resolve(rawLocation, currentLocation) {
 | 
						|
        // const resolve: Router['resolve'] = (rawLocation: RouteLocationRaw, currentLocation) => {
 | 
						|
        // const objectLocation = routerLocationAsObject(rawLocation)
 | 
						|
        // we create a copy to modify it later
 | 
						|
        currentLocation = assign({}, currentLocation || currentRoute.value);
 | 
						|
        if (typeof rawLocation === 'string') {
 | 
						|
            const locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path);
 | 
						|
            const matchedRoute = matcher.resolve({ path: locationNormalized.path }, currentLocation);
 | 
						|
            const href = routerHistory.createHref(locationNormalized.fullPath);
 | 
						|
            // locationNormalized is always a new object
 | 
						|
            return assign(locationNormalized, matchedRoute, {
 | 
						|
                params: decodeParams(matchedRoute.params),
 | 
						|
                hash: decode(locationNormalized.hash),
 | 
						|
                redirectedFrom: undefined,
 | 
						|
                href,
 | 
						|
            });
 | 
						|
        }
 | 
						|
        let matcherLocation;
 | 
						|
        // path could be relative in object as well
 | 
						|
        if (rawLocation.path != null) {
 | 
						|
            matcherLocation = assign({}, rawLocation, {
 | 
						|
                path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path,
 | 
						|
            });
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            // remove any nullish param
 | 
						|
            const targetParams = assign({}, rawLocation.params);
 | 
						|
            for (const key in targetParams) {
 | 
						|
                if (targetParams[key] == null) {
 | 
						|
                    delete targetParams[key];
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // pass encoded values to the matcher, so it can produce encoded path and fullPath
 | 
						|
            matcherLocation = assign({}, rawLocation, {
 | 
						|
                params: encodeParams(targetParams),
 | 
						|
            });
 | 
						|
            // current location params are decoded, we need to encode them in case the
 | 
						|
            // matcher merges the params
 | 
						|
            currentLocation.params = encodeParams(currentLocation.params);
 | 
						|
        }
 | 
						|
        const matchedRoute = matcher.resolve(matcherLocation, currentLocation);
 | 
						|
        const hash = rawLocation.hash || '';
 | 
						|
        // the matcher might have merged current location params, so
 | 
						|
        // we need to run the decoding again
 | 
						|
        matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params));
 | 
						|
        const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, {
 | 
						|
            hash: encodeHash(hash),
 | 
						|
            path: matchedRoute.path,
 | 
						|
        }));
 | 
						|
        const href = routerHistory.createHref(fullPath);
 | 
						|
        return assign({
 | 
						|
            fullPath,
 | 
						|
            // keep the hash encoded so fullPath is effectively path + encodedQuery +
 | 
						|
            // hash
 | 
						|
            hash,
 | 
						|
            query: 
 | 
						|
            // if the user is using a custom query lib like qs, we might have
 | 
						|
            // nested objects, so we keep the query as is, meaning it can contain
 | 
						|
            // numbers at `$route.query`, but at the point, the user will have to
 | 
						|
            // use their own type anyway.
 | 
						|
            // https://github.com/vuejs/router/issues/328#issuecomment-649481567
 | 
						|
            stringifyQuery$1 === stringifyQuery
 | 
						|
                ? normalizeQuery(rawLocation.query)
 | 
						|
                : (rawLocation.query || {}),
 | 
						|
        }, matchedRoute, {
 | 
						|
            redirectedFrom: undefined,
 | 
						|
            href,
 | 
						|
        });
 | 
						|
    }
 | 
						|
    function locationAsObject(to) {
 | 
						|
        return typeof to === 'string'
 | 
						|
            ? parseURL(parseQuery$1, to, currentRoute.value.path)
 | 
						|
            : assign({}, to);
 | 
						|
    }
 | 
						|
    function checkCanceledNavigation(to, from) {
 | 
						|
        if (pendingLocation !== to) {
 | 
						|
            return createRouterError(8 /* ErrorTypes.NAVIGATION_CANCELLED */, {
 | 
						|
                from,
 | 
						|
                to,
 | 
						|
            });
 | 
						|
        }
 | 
						|
    }
 | 
						|
    function push(to) {
 | 
						|
        return pushWithRedirect(to);
 | 
						|
    }
 | 
						|
    function replace(to) {
 | 
						|
        return push(assign(locationAsObject(to), { replace: true }));
 | 
						|
    }
 | 
						|
    function handleRedirectRecord(to) {
 | 
						|
        const lastMatched = to.matched[to.matched.length - 1];
 | 
						|
        if (lastMatched && lastMatched.redirect) {
 | 
						|
            const { redirect } = lastMatched;
 | 
						|
            let newTargetLocation = typeof redirect === 'function' ? redirect(to) : redirect;
 | 
						|
            if (typeof newTargetLocation === 'string') {
 | 
						|
                newTargetLocation =
 | 
						|
                    newTargetLocation.includes('?') || newTargetLocation.includes('#')
 | 
						|
                        ? (newTargetLocation = locationAsObject(newTargetLocation))
 | 
						|
                        : // force empty params
 | 
						|
                            { path: newTargetLocation };
 | 
						|
                // @ts-expect-error: force empty params when a string is passed to let
 | 
						|
                // the router parse them again
 | 
						|
                newTargetLocation.params = {};
 | 
						|
            }
 | 
						|
            return assign({
 | 
						|
                query: to.query,
 | 
						|
                hash: to.hash,
 | 
						|
                // avoid transferring params if the redirect has a path
 | 
						|
                params: newTargetLocation.path != null ? {} : to.params,
 | 
						|
            }, newTargetLocation);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    function pushWithRedirect(to, redirectedFrom) {
 | 
						|
        const targetLocation = (pendingLocation = resolve(to));
 | 
						|
        const from = currentRoute.value;
 | 
						|
        const data = to.state;
 | 
						|
        const force = to.force;
 | 
						|
        // to could be a string where `replace` is a function
 | 
						|
        const replace = to.replace === true;
 | 
						|
        const shouldRedirect = handleRedirectRecord(targetLocation);
 | 
						|
        if (shouldRedirect)
 | 
						|
            return pushWithRedirect(assign(locationAsObject(shouldRedirect), {
 | 
						|
                state: typeof shouldRedirect === 'object'
 | 
						|
                    ? assign({}, data, shouldRedirect.state)
 | 
						|
                    : data,
 | 
						|
                force,
 | 
						|
                replace,
 | 
						|
            }), 
 | 
						|
            // keep original redirectedFrom if it exists
 | 
						|
            redirectedFrom || targetLocation);
 | 
						|
        // if it was a redirect we already called `pushWithRedirect` above
 | 
						|
        const toLocation = targetLocation;
 | 
						|
        toLocation.redirectedFrom = redirectedFrom;
 | 
						|
        let failure;
 | 
						|
        if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
 | 
						|
            failure = createRouterError(16 /* ErrorTypes.NAVIGATION_DUPLICATED */, { to: toLocation, from });
 | 
						|
            // trigger scroll to allow scrolling to the same anchor
 | 
						|
            handleScroll(from, from, 
 | 
						|
            // this is a push, the only way for it to be triggered from a
 | 
						|
            // history.listen is with a redirect, which makes it become a push
 | 
						|
            true, 
 | 
						|
            // This cannot be the first navigation because the initial location
 | 
						|
            // cannot be manually navigated to
 | 
						|
            false);
 | 
						|
        }
 | 
						|
        return (failure ? Promise.resolve(failure) : navigate(toLocation, from))
 | 
						|
            .catch((error) => isNavigationFailure(error)
 | 
						|
            ? // navigation redirects still mark the router as ready
 | 
						|
                isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)
 | 
						|
                    ? error
 | 
						|
                    : markAsReady(error) // also returns the error
 | 
						|
            : // reject any unknown error
 | 
						|
                triggerError(error, toLocation, from))
 | 
						|
            .then((failure) => {
 | 
						|
            if (failure) {
 | 
						|
                if (isNavigationFailure(failure, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) {
 | 
						|
                    return pushWithRedirect(
 | 
						|
                    // keep options
 | 
						|
                    assign({
 | 
						|
                        // preserve an existing replacement but allow the redirect to override it
 | 
						|
                        replace,
 | 
						|
                    }, locationAsObject(failure.to), {
 | 
						|
                        state: typeof failure.to === 'object'
 | 
						|
                            ? assign({}, data, failure.to.state)
 | 
						|
                            : data,
 | 
						|
                        force,
 | 
						|
                    }), 
 | 
						|
                    // preserve the original redirectedFrom if any
 | 
						|
                    redirectedFrom || toLocation);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                // if we fail we don't finalize the navigation
 | 
						|
                failure = finalizeNavigation(toLocation, from, true, replace, data);
 | 
						|
            }
 | 
						|
            triggerAfterEach(toLocation, from, failure);
 | 
						|
            return failure;
 | 
						|
        });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Helper to reject and skip all navigation guards if a new navigation happened
 | 
						|
     * @param to
 | 
						|
     * @param from
 | 
						|
     */
 | 
						|
    function checkCanceledNavigationAndReject(to, from) {
 | 
						|
        const error = checkCanceledNavigation(to, from);
 | 
						|
        return error ? Promise.reject(error) : Promise.resolve();
 | 
						|
    }
 | 
						|
    function runWithContext(fn) {
 | 
						|
        const app = installedApps.values().next().value;
 | 
						|
        // support Vue < 3.3
 | 
						|
        return app && typeof app.runWithContext === 'function'
 | 
						|
            ? app.runWithContext(fn)
 | 
						|
            : fn();
 | 
						|
    }
 | 
						|
    // TODO: refactor the whole before guards by internally using router.beforeEach
 | 
						|
    function navigate(to, from) {
 | 
						|
        let guards;
 | 
						|
        const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from);
 | 
						|
        // all components here have been resolved once because we are leaving
 | 
						|
        guards = extractComponentsGuards(leavingRecords.reverse(), 'beforeRouteLeave', to, from);
 | 
						|
        // leavingRecords is already reversed
 | 
						|
        for (const record of leavingRecords) {
 | 
						|
            record.leaveGuards.forEach(guard => {
 | 
						|
                guards.push(guardToPromiseFn(guard, to, from));
 | 
						|
            });
 | 
						|
        }
 | 
						|
        const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from);
 | 
						|
        guards.push(canceledNavigationCheck);
 | 
						|
        // run the queue of per route beforeRouteLeave guards
 | 
						|
        return (runGuardQueue(guards)
 | 
						|
            .then(() => {
 | 
						|
            // check global guards beforeEach
 | 
						|
            guards = [];
 | 
						|
            for (const guard of beforeGuards.list()) {
 | 
						|
                guards.push(guardToPromiseFn(guard, to, from));
 | 
						|
            }
 | 
						|
            guards.push(canceledNavigationCheck);
 | 
						|
            return runGuardQueue(guards);
 | 
						|
        })
 | 
						|
            .then(() => {
 | 
						|
            // check in components beforeRouteUpdate
 | 
						|
            guards = extractComponentsGuards(updatingRecords, 'beforeRouteUpdate', to, from);
 | 
						|
            for (const record of updatingRecords) {
 | 
						|
                record.updateGuards.forEach(guard => {
 | 
						|
                    guards.push(guardToPromiseFn(guard, to, from));
 | 
						|
                });
 | 
						|
            }
 | 
						|
            guards.push(canceledNavigationCheck);
 | 
						|
            // run the queue of per route beforeEnter guards
 | 
						|
            return runGuardQueue(guards);
 | 
						|
        })
 | 
						|
            .then(() => {
 | 
						|
            // check the route beforeEnter
 | 
						|
            guards = [];
 | 
						|
            for (const record of enteringRecords) {
 | 
						|
                // do not trigger beforeEnter on reused views
 | 
						|
                if (record.beforeEnter) {
 | 
						|
                    if (isArray(record.beforeEnter)) {
 | 
						|
                        for (const beforeEnter of record.beforeEnter)
 | 
						|
                            guards.push(guardToPromiseFn(beforeEnter, to, from));
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        guards.push(guardToPromiseFn(record.beforeEnter, to, from));
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            guards.push(canceledNavigationCheck);
 | 
						|
            // run the queue of per route beforeEnter guards
 | 
						|
            return runGuardQueue(guards);
 | 
						|
        })
 | 
						|
            .then(() => {
 | 
						|
            // NOTE: at this point to.matched is normalized and does not contain any () => Promise<Component>
 | 
						|
            // clear existing enterCallbacks, these are added by extractComponentsGuards
 | 
						|
            to.matched.forEach(record => (record.enterCallbacks = {}));
 | 
						|
            // check in-component beforeRouteEnter
 | 
						|
            guards = extractComponentsGuards(enteringRecords, 'beforeRouteEnter', to, from, runWithContext);
 | 
						|
            guards.push(canceledNavigationCheck);
 | 
						|
            // run the queue of per route beforeEnter guards
 | 
						|
            return runGuardQueue(guards);
 | 
						|
        })
 | 
						|
            .then(() => {
 | 
						|
            // check global guards beforeResolve
 | 
						|
            guards = [];
 | 
						|
            for (const guard of beforeResolveGuards.list()) {
 | 
						|
                guards.push(guardToPromiseFn(guard, to, from));
 | 
						|
            }
 | 
						|
            guards.push(canceledNavigationCheck);
 | 
						|
            return runGuardQueue(guards);
 | 
						|
        })
 | 
						|
            // catch any navigation canceled
 | 
						|
            .catch(err => isNavigationFailure(err, 8 /* ErrorTypes.NAVIGATION_CANCELLED */)
 | 
						|
            ? err
 | 
						|
            : Promise.reject(err)));
 | 
						|
    }
 | 
						|
    function triggerAfterEach(to, from, failure) {
 | 
						|
        // navigation is confirmed, call afterGuards
 | 
						|
        // TODO: wrap with error handlers
 | 
						|
        afterGuards
 | 
						|
            .list()
 | 
						|
            .forEach(guard => runWithContext(() => guard(to, from, failure)));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * - Cleans up any navigation guards
 | 
						|
     * - Changes the url if necessary
 | 
						|
     * - Calls the scrollBehavior
 | 
						|
     */
 | 
						|
    function finalizeNavigation(toLocation, from, isPush, replace, data) {
 | 
						|
        // a more recent navigation took place
 | 
						|
        const error = checkCanceledNavigation(toLocation, from);
 | 
						|
        if (error)
 | 
						|
            return error;
 | 
						|
        // only consider as push if it's not the first navigation
 | 
						|
        const isFirstNavigation = from === START_LOCATION_NORMALIZED;
 | 
						|
        const state = !isBrowser ? {} : history.state;
 | 
						|
        // change URL only if the user did a push/replace and if it's not the initial navigation because
 | 
						|
        // it's just reflecting the url
 | 
						|
        if (isPush) {
 | 
						|
            // on the initial navigation, we want to reuse the scroll position from
 | 
						|
            // history state if it exists
 | 
						|
            if (replace || isFirstNavigation)
 | 
						|
                routerHistory.replace(toLocation.fullPath, assign({
 | 
						|
                    scroll: isFirstNavigation && state && state.scroll,
 | 
						|
                }, data));
 | 
						|
            else
 | 
						|
                routerHistory.push(toLocation.fullPath, data);
 | 
						|
        }
 | 
						|
        // accept current navigation
 | 
						|
        currentRoute.value = toLocation;
 | 
						|
        handleScroll(toLocation, from, isPush, isFirstNavigation);
 | 
						|
        markAsReady();
 | 
						|
    }
 | 
						|
    let removeHistoryListener;
 | 
						|
    // attach listener to history to trigger navigations
 | 
						|
    function setupListeners() {
 | 
						|
        // avoid setting up listeners twice due to an invalid first navigation
 | 
						|
        if (removeHistoryListener)
 | 
						|
            return;
 | 
						|
        removeHistoryListener = routerHistory.listen((to, _from, info) => {
 | 
						|
            if (!router.listening)
 | 
						|
                return;
 | 
						|
            // cannot be a redirect route because it was in history
 | 
						|
            const toLocation = resolve(to);
 | 
						|
            // due to dynamic routing, and to hash history with manual navigation
 | 
						|
            // (manually changing the url or calling history.hash = '#/somewhere'),
 | 
						|
            // there could be a redirect record in history
 | 
						|
            const shouldRedirect = handleRedirectRecord(toLocation);
 | 
						|
            if (shouldRedirect) {
 | 
						|
                pushWithRedirect(assign(shouldRedirect, { replace: true, force: true }), toLocation).catch(noop);
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            pendingLocation = toLocation;
 | 
						|
            const from = currentRoute.value;
 | 
						|
            // TODO: should be moved to web history?
 | 
						|
            if (isBrowser) {
 | 
						|
                saveScrollPosition(getScrollKey(from.fullPath, info.delta), computeScrollPosition());
 | 
						|
            }
 | 
						|
            navigate(toLocation, from)
 | 
						|
                .catch((error) => {
 | 
						|
                if (isNavigationFailure(error, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) {
 | 
						|
                    return error;
 | 
						|
                }
 | 
						|
                if (isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) {
 | 
						|
                    // Here we could call if (info.delta) routerHistory.go(-info.delta,
 | 
						|
                    // false) but this is bug prone as we have no way to wait the
 | 
						|
                    // navigation to be finished before calling pushWithRedirect. Using
 | 
						|
                    // a setTimeout of 16ms seems to work but there is no guarantee for
 | 
						|
                    // it to work on every browser. So instead we do not restore the
 | 
						|
                    // history entry and trigger a new navigation as requested by the
 | 
						|
                    // navigation guard.
 | 
						|
                    // the error is already handled by router.push we just want to avoid
 | 
						|
                    // logging the error
 | 
						|
                    pushWithRedirect(assign(locationAsObject(error.to), {
 | 
						|
                        force: true,
 | 
						|
                    }), toLocation
 | 
						|
                    // avoid an uncaught rejection, let push call triggerError
 | 
						|
                    )
 | 
						|
                        .then(failure => {
 | 
						|
                        // manual change in hash history #916 ending up in the URL not
 | 
						|
                        // changing, but it was changed by the manual url change, so we
 | 
						|
                        // need to manually change it ourselves
 | 
						|
                        if (isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ |
 | 
						|
                            16 /* ErrorTypes.NAVIGATION_DUPLICATED */) &&
 | 
						|
                            !info.delta &&
 | 
						|
                            info.type === NavigationType.pop) {
 | 
						|
                            routerHistory.go(-1, false);
 | 
						|
                        }
 | 
						|
                    })
 | 
						|
                        .catch(noop);
 | 
						|
                    // avoid the then branch
 | 
						|
                    return Promise.reject();
 | 
						|
                }
 | 
						|
                // do not restore history on unknown direction
 | 
						|
                if (info.delta) {
 | 
						|
                    routerHistory.go(-info.delta, false);
 | 
						|
                }
 | 
						|
                // unrecognized error, transfer to the global handler
 | 
						|
                return triggerError(error, toLocation, from);
 | 
						|
            })
 | 
						|
                .then((failure) => {
 | 
						|
                failure =
 | 
						|
                    failure ||
 | 
						|
                        finalizeNavigation(
 | 
						|
                        // after navigation, all matched components are resolved
 | 
						|
                        toLocation, from, false);
 | 
						|
                // revert the navigation
 | 
						|
                if (failure) {
 | 
						|
                    if (info.delta &&
 | 
						|
                        // a new navigation has been triggered, so we do not want to revert, that will change the current history
 | 
						|
                        // entry while a different route is displayed
 | 
						|
                        !isNavigationFailure(failure, 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) {
 | 
						|
                        routerHistory.go(-info.delta, false);
 | 
						|
                    }
 | 
						|
                    else if (info.type === NavigationType.pop &&
 | 
						|
                        isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 16 /* ErrorTypes.NAVIGATION_DUPLICATED */)) {
 | 
						|
                        // manual change in hash history #916
 | 
						|
                        // it's like a push but lacks the information of the direction
 | 
						|
                        routerHistory.go(-1, false);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                triggerAfterEach(toLocation, from, failure);
 | 
						|
            })
 | 
						|
                // avoid warnings in the console about uncaught rejections, they are logged by triggerErrors
 | 
						|
                .catch(noop);
 | 
						|
        });
 | 
						|
    }
 | 
						|
    // Initialization and Errors
 | 
						|
    let readyHandlers = useCallbacks();
 | 
						|
    let errorListeners = useCallbacks();
 | 
						|
    let ready;
 | 
						|
    /**
 | 
						|
     * Trigger errorListeners added via onError and throws the error as well
 | 
						|
     *
 | 
						|
     * @param error - error to throw
 | 
						|
     * @param to - location we were navigating to when the error happened
 | 
						|
     * @param from - location we were navigating from when the error happened
 | 
						|
     * @returns the error as a rejected promise
 | 
						|
     */
 | 
						|
    function triggerError(error, to, from) {
 | 
						|
        markAsReady(error);
 | 
						|
        const list = errorListeners.list();
 | 
						|
        if (list.length) {
 | 
						|
            list.forEach(handler => handler(error, to, from));
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            console.error(error);
 | 
						|
        }
 | 
						|
        // reject the error no matter there were error listeners or not
 | 
						|
        return Promise.reject(error);
 | 
						|
    }
 | 
						|
    function isReady() {
 | 
						|
        if (ready && currentRoute.value !== START_LOCATION_NORMALIZED)
 | 
						|
            return Promise.resolve();
 | 
						|
        return new Promise((resolve, reject) => {
 | 
						|
            readyHandlers.add([resolve, reject]);
 | 
						|
        });
 | 
						|
    }
 | 
						|
    function markAsReady(err) {
 | 
						|
        if (!ready) {
 | 
						|
            // still not ready if an error happened
 | 
						|
            ready = !err;
 | 
						|
            setupListeners();
 | 
						|
            readyHandlers
 | 
						|
                .list()
 | 
						|
                .forEach(([resolve, reject]) => (err ? reject(err) : resolve()));
 | 
						|
            readyHandlers.reset();
 | 
						|
        }
 | 
						|
        return err;
 | 
						|
    }
 | 
						|
    // Scroll behavior
 | 
						|
    function handleScroll(to, from, isPush, isFirstNavigation) {
 | 
						|
        const { scrollBehavior } = options;
 | 
						|
        if (!isBrowser || !scrollBehavior)
 | 
						|
            return Promise.resolve();
 | 
						|
        const scrollPosition = (!isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0))) ||
 | 
						|
            ((isFirstNavigation || !isPush) &&
 | 
						|
                history.state &&
 | 
						|
                history.state.scroll) ||
 | 
						|
            null;
 | 
						|
        return vue.nextTick()
 | 
						|
            .then(() => scrollBehavior(to, from, scrollPosition))
 | 
						|
            .then(position => position && scrollToPosition(position))
 | 
						|
            .catch(err => triggerError(err, to, from));
 | 
						|
    }
 | 
						|
    const go = (delta) => routerHistory.go(delta);
 | 
						|
    let started;
 | 
						|
    const installedApps = new Set();
 | 
						|
    const router = {
 | 
						|
        currentRoute,
 | 
						|
        listening: true,
 | 
						|
        addRoute,
 | 
						|
        removeRoute,
 | 
						|
        clearRoutes: matcher.clearRoutes,
 | 
						|
        hasRoute,
 | 
						|
        getRoutes,
 | 
						|
        resolve,
 | 
						|
        options,
 | 
						|
        push,
 | 
						|
        replace,
 | 
						|
        go,
 | 
						|
        back: () => go(-1),
 | 
						|
        forward: () => go(1),
 | 
						|
        beforeEach: beforeGuards.add,
 | 
						|
        beforeResolve: beforeResolveGuards.add,
 | 
						|
        afterEach: afterGuards.add,
 | 
						|
        onError: errorListeners.add,
 | 
						|
        isReady,
 | 
						|
        install(app) {
 | 
						|
            const router = this;
 | 
						|
            app.component('RouterLink', RouterLink);
 | 
						|
            app.component('RouterView', RouterView);
 | 
						|
            app.config.globalProperties.$router = router;
 | 
						|
            Object.defineProperty(app.config.globalProperties, '$route', {
 | 
						|
                enumerable: true,
 | 
						|
                get: () => vue.unref(currentRoute),
 | 
						|
            });
 | 
						|
            // this initial navigation is only necessary on client, on server it doesn't
 | 
						|
            // make sense because it will create an extra unnecessary navigation and could
 | 
						|
            // lead to problems
 | 
						|
            if (isBrowser &&
 | 
						|
                // used for the initial navigation client side to avoid pushing
 | 
						|
                // multiple times when the router is used in multiple apps
 | 
						|
                !started &&
 | 
						|
                currentRoute.value === START_LOCATION_NORMALIZED) {
 | 
						|
                // see above
 | 
						|
                started = true;
 | 
						|
                push(routerHistory.location).catch(err => {
 | 
						|
                });
 | 
						|
            }
 | 
						|
            const reactiveRoute = {};
 | 
						|
            for (const key in START_LOCATION_NORMALIZED) {
 | 
						|
                Object.defineProperty(reactiveRoute, key, {
 | 
						|
                    get: () => currentRoute.value[key],
 | 
						|
                    enumerable: true,
 | 
						|
                });
 | 
						|
            }
 | 
						|
            app.provide(routerKey, router);
 | 
						|
            app.provide(routeLocationKey, vue.shallowReactive(reactiveRoute));
 | 
						|
            app.provide(routerViewLocationKey, currentRoute);
 | 
						|
            const unmountApp = app.unmount;
 | 
						|
            installedApps.add(app);
 | 
						|
            app.unmount = function () {
 | 
						|
                installedApps.delete(app);
 | 
						|
                // the router is not attached to an app anymore
 | 
						|
                if (installedApps.size < 1) {
 | 
						|
                    // invalidate the current navigation
 | 
						|
                    pendingLocation = START_LOCATION_NORMALIZED;
 | 
						|
                    removeHistoryListener && removeHistoryListener();
 | 
						|
                    removeHistoryListener = null;
 | 
						|
                    currentRoute.value = START_LOCATION_NORMALIZED;
 | 
						|
                    started = false;
 | 
						|
                    ready = false;
 | 
						|
                }
 | 
						|
                unmountApp();
 | 
						|
            };
 | 
						|
        },
 | 
						|
    };
 | 
						|
    // TODO: type this as NavigationGuardReturn or similar instead of any
 | 
						|
    function runGuardQueue(guards) {
 | 
						|
        return guards.reduce((promise, guard) => promise.then(() => runWithContext(guard)), Promise.resolve());
 | 
						|
    }
 | 
						|
    return router;
 | 
						|
}
 | 
						|
function extractChangingRecords(to, from) {
 | 
						|
    const leavingRecords = [];
 | 
						|
    const updatingRecords = [];
 | 
						|
    const enteringRecords = [];
 | 
						|
    const len = Math.max(from.matched.length, to.matched.length);
 | 
						|
    for (let i = 0; i < len; i++) {
 | 
						|
        const recordFrom = from.matched[i];
 | 
						|
        if (recordFrom) {
 | 
						|
            if (to.matched.find(record => isSameRouteRecord(record, recordFrom)))
 | 
						|
                updatingRecords.push(recordFrom);
 | 
						|
            else
 | 
						|
                leavingRecords.push(recordFrom);
 | 
						|
        }
 | 
						|
        const recordTo = to.matched[i];
 | 
						|
        if (recordTo) {
 | 
						|
            // the type doesn't matter because we are comparing per reference
 | 
						|
            if (!from.matched.find(record => isSameRouteRecord(record, recordTo))) {
 | 
						|
                enteringRecords.push(recordTo);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return [leavingRecords, updatingRecords, enteringRecords];
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Returns the router instance. Equivalent to using `$router` inside
 | 
						|
 * templates.
 | 
						|
 */
 | 
						|
function useRouter() {
 | 
						|
    return vue.inject(routerKey);
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Returns the current route location. Equivalent to using `$route` inside
 | 
						|
 * templates.
 | 
						|
 */
 | 
						|
function useRoute(_name) {
 | 
						|
    return vue.inject(routeLocationKey);
 | 
						|
}
 | 
						|
 | 
						|
exports.RouterLink = RouterLink;
 | 
						|
exports.RouterView = RouterView;
 | 
						|
exports.START_LOCATION = START_LOCATION_NORMALIZED;
 | 
						|
exports.createMemoryHistory = createMemoryHistory;
 | 
						|
exports.createRouter = createRouter;
 | 
						|
exports.createRouterMatcher = createRouterMatcher;
 | 
						|
exports.createWebHashHistory = createWebHashHistory;
 | 
						|
exports.createWebHistory = createWebHistory;
 | 
						|
exports.isNavigationFailure = isNavigationFailure;
 | 
						|
exports.loadRouteLocation = loadRouteLocation;
 | 
						|
exports.matchedRouteKey = matchedRouteKey;
 | 
						|
exports.onBeforeRouteLeave = onBeforeRouteLeave;
 | 
						|
exports.onBeforeRouteUpdate = onBeforeRouteUpdate;
 | 
						|
exports.parseQuery = parseQuery;
 | 
						|
exports.routeLocationKey = routeLocationKey;
 | 
						|
exports.routerKey = routerKey;
 | 
						|
exports.routerViewLocationKey = routerViewLocationKey;
 | 
						|
exports.stringifyQuery = stringifyQuery;
 | 
						|
exports.useLink = useLink;
 | 
						|
exports.useRoute = useRoute;
 | 
						|
exports.useRouter = useRouter;
 | 
						|
exports.viewDepthKey = viewDepthKey;
 |