package.fesm2022.router.mjs.map Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of router Show documentation
Show all versions of router Show documentation
Angular - the routing library
{"version":3,"file":"router.mjs","sources":["../../../../../../packages/router/src/shared.ts","../../../../../../packages/router/src/utils/collection.ts","../../../../../../packages/router/src/url_tree.ts","../../../../../../packages/router/src/create_url_tree.ts","../../../../../../packages/router/src/events.ts","../../../../../../packages/router/src/utils/config.ts","../../../../../../packages/router/src/router_outlet_context.ts","../../../../../../packages/router/src/utils/tree.ts","../../../../../../packages/router/src/router_state.ts","../../../../../../packages/router/src/directives/router_outlet.ts","../../../../../../packages/router/src/create_router_state.ts","../../../../../../packages/router/src/models.ts","../../../../../../packages/router/src/navigation_canceling_error.ts","../../../../../../packages/router/src/operators/activate_routes.ts","../../../../../../packages/router/src/utils/preactivation.ts","../../../../../../packages/router/src/utils/type_guards.ts","../../../../../../packages/router/src/operators/prioritized_guard_value.ts","../../../../../../packages/router/src/operators/check_guards.ts","../../../../../../packages/router/src/apply_redirects.ts","../../../../../../packages/router/src/utils/config_matching.ts","../../../../../../packages/router/src/recognize.ts","../../../../../../packages/router/src/operators/recognize.ts","../../../../../../packages/router/src/operators/resolve_data.ts","../../../../../../packages/router/src/operators/switch_tap.ts","../../../../../../packages/router/src/page_title_strategy.ts","../../../../../../packages/router/src/router_config.ts","../../../../../../packages/router/src/components/empty_outlet.ts","../../../../../../packages/router/src/router_config_loader.ts","../../../../../../packages/router/src/url_handling_strategy.ts","../../../../../../packages/router/src/utils/view_transition.ts","../../../../../../packages/router/src/navigation_transition.ts","../../../../../../packages/router/src/route_reuse_strategy.ts","../../../../../../packages/router/src/statemanager/state_manager.ts","../../../../../../packages/router/src/utils/navigations.ts","../../../../../../packages/router/src/router.ts","../../../../../../packages/router/src/directives/router_link.ts","../../../../../../packages/router/src/directives/router_link_active.ts","../../../../../../packages/router/src/router_preloader.ts","../../../../../../packages/router/src/router_scroller.ts","../../../../../../packages/router/src/provide_router.ts","../../../../../../packages/router/src/router_module.ts","../../../../../../packages/router/src/utils/functional_guards.ts","../../../../../../packages/router/src/version.ts","../../../../../../packages/router/public_api.ts","../../../../../../packages/router/index.ts","../../../../../../packages/router/router.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Route, UrlMatchResult} from './models';\nimport {UrlSegment, UrlSegmentGroup} from './url_tree';\n\n/**\n * The primary routing outlet.\n *\n * @publicApi\n */\nexport const PRIMARY_OUTLET = 'primary';\n\n/**\n * A private symbol used to store the value of `Route.title` inside the `Route.data` if it is a\n * static string or `Route.resolve` if anything else. This allows us to reuse the existing route\n * data/resolvers to support the title feature without new instrumentation in the `Router` pipeline.\n */\nexport const RouteTitleKey = /* @__PURE__ */ Symbol('RouteTitle');\n\n/**\n * A collection of matrix and query URL parameters.\n * @see {@link convertToParamMap}\n * @see {@link ParamMap}\n *\n * @publicApi\n */\nexport type Params = {\n [key: string]: any;\n};\n\n/**\n * A map that provides access to the required and optional parameters\n * specific to a route.\n * The map supports retrieving a single value with `get()`\n * or multiple values with `getAll()`.\n *\n * @see [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)\n *\n * @publicApi\n */\nexport interface ParamMap {\n /**\n * Reports whether the map contains a given parameter.\n * @param name The parameter name.\n * @returns True if the map contains the given parameter, false otherwise.\n */\n has(name: string): boolean;\n /**\n * Retrieves a single value for a parameter.\n * @param name The parameter name.\n * @return The parameter's single value,\n * or the first value if the parameter has multiple values,\n * or `null` when there is no such parameter.\n */\n get(name: string): string | null;\n /**\n * Retrieves multiple values for a parameter.\n * @param name The parameter name.\n * @return An array containing one or more values,\n * or an empty array if there is no such parameter.\n *\n */\n getAll(name: string): string[];\n\n /** Names of the parameters in the map. */\n readonly keys: string[];\n}\n\nclass ParamsAsMap implements ParamMap {\n private params: Params;\n\n constructor(params: Params) {\n this.params = params || {};\n }\n\n has(name: string): boolean {\n return Object.prototype.hasOwnProperty.call(this.params, name);\n }\n\n get(name: string): string | null {\n if (this.has(name)) {\n const v = this.params[name];\n return Array.isArray(v) ? v[0] : v;\n }\n\n return null;\n }\n\n getAll(name: string): string[] {\n if (this.has(name)) {\n const v = this.params[name];\n return Array.isArray(v) ? v : [v];\n }\n\n return [];\n }\n\n get keys(): string[] {\n return Object.keys(this.params);\n }\n}\n\n/**\n * Converts a `Params` instance to a `ParamMap`.\n * @param params The instance to convert.\n * @returns The new map instance.\n *\n * @publicApi\n */\nexport function convertToParamMap(params: Params): ParamMap {\n return new ParamsAsMap(params);\n}\n\n/**\n * Matches the route configuration (`route`) against the actual URL (`segments`).\n *\n * When no matcher is defined on a `Route`, this is the matcher used by the Router by default.\n *\n * @param segments The remaining unmatched segments in the current navigation\n * @param segmentGroup The current segment group being matched\n * @param route The `Route` to match against.\n *\n * @see {@link UrlMatchResult}\n * @see {@link Route}\n *\n * @returns The resulting match information or `null` if the `route` should not match.\n * @publicApi\n */\nexport function defaultUrlMatcher(\n segments: UrlSegment[],\n segmentGroup: UrlSegmentGroup,\n route: Route,\n): UrlMatchResult | null {\n const parts = route.path!.split('/');\n\n if (parts.length > segments.length) {\n // The actual URL is shorter than the config, no match\n return null;\n }\n\n if (\n route.pathMatch === 'full' &&\n (segmentGroup.hasChildren() || parts.length < segments.length)\n ) {\n // The config is longer than the actual URL but we are looking for a full match, return null\n return null;\n }\n\n const posParams: {[key: string]: UrlSegment} = {};\n\n // Check each config part against the actual URL\n for (let index = 0; index < parts.length; index++) {\n const part = parts[index];\n const segment = segments[index];\n const isParameter = part[0] === ':';\n if (isParameter) {\n posParams[part.substring(1)] = segment;\n } else if (part !== segment.path) {\n // The actual URL part does not match the config, no match\n return null;\n }\n }\n\n return {consumed: segments.slice(0, parts.length), posParams};\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {ɵisPromise as isPromise} from '@angular/core';\nimport {from, isObservable, Observable, of} from 'rxjs';\n\nexport function shallowEqualArrays(a: any[], b: any[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; ++i) {\n if (!shallowEqual(a[i], b[i])) return false;\n }\n return true;\n}\n\nexport function shallowEqual(\n a: {[key: string | symbol]: any},\n b: {[key: string | symbol]: any},\n): boolean {\n // While `undefined` should never be possible, it would sometimes be the case in IE 11\n // and pre-chromium Edge. The check below accounts for this edge case.\n const k1 = a ? getDataKeys(a) : undefined;\n const k2 = b ? getDataKeys(b) : undefined;\n if (!k1 || !k2 || k1.length != k2.length) {\n return false;\n }\n let key: string | symbol;\n for (let i = 0; i < k1.length; i++) {\n key = k1[i];\n if (!equalArraysOrString(a[key], b[key])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Gets the keys of an object, including `symbol` keys.\n */\nexport function getDataKeys(obj: Object): Array {\n return [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)];\n}\n\n/**\n * Test equality for arrays of strings or a string.\n */\nexport function equalArraysOrString(a: string | string[], b: string | string[]) {\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n const aSorted = [...a].sort();\n const bSorted = [...b].sort();\n return aSorted.every((val, index) => bSorted[index] === val);\n } else {\n return a === b;\n }\n}\n\n/**\n * Return the last element of an array.\n */\nexport function last(a: T[]): T | null {\n return a.length > 0 ? a[a.length - 1] : null;\n}\n\nexport function wrapIntoObservable(value: T | Promise | Observable): Observable {\n if (isObservable(value)) {\n return value;\n }\n\n if (isPromise(value)) {\n // Use `Promise.resolve()` to wrap promise-like instances.\n // Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the\n // change detection.\n return from(Promise.resolve(value));\n }\n\n return of(value);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Injectable, ɵRuntimeError as RuntimeError} from '@angular/core';\n\nimport {RuntimeErrorCode} from './errors';\nimport {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET} from './shared';\nimport {equalArraysOrString, shallowEqual} from './utils/collection';\n\n/**\n * A set of options which specify how to determine if a `UrlTree` is active, given the `UrlTree`\n * for the current router state.\n *\n * @publicApi\n * @see {@link Router#isActive}\n */\nexport interface IsActiveMatchOptions {\n /**\n * Defines the strategy for comparing the matrix parameters of two `UrlTree`s.\n *\n * The matrix parameter matching is dependent on the strategy for matching the\n * segments. That is, if the `paths` option is set to `'subset'`, only\n * the matrix parameters of the matching segments will be compared.\n *\n * - `'exact'`: Requires that matching segments also have exact matrix parameter\n * matches.\n * - `'subset'`: The matching segments in the router's active `UrlTree` may contain\n * extra matrix parameters, but those that exist in the `UrlTree` in question must match.\n * - `'ignored'`: When comparing `UrlTree`s, matrix params will be ignored.\n */\n matrixParams: 'exact' | 'subset' | 'ignored';\n /**\n * Defines the strategy for comparing the query parameters of two `UrlTree`s.\n *\n * - `'exact'`: the query parameters must match exactly.\n * - `'subset'`: the active `UrlTree` may contain extra parameters,\n * but must match the key and value of any that exist in the `UrlTree` in question.\n * - `'ignored'`: When comparing `UrlTree`s, query params will be ignored.\n */\n queryParams: 'exact' | 'subset' | 'ignored';\n /**\n * Defines the strategy for comparing the `UrlSegment`s of the `UrlTree`s.\n *\n * - `'exact'`: all segments in each `UrlTree` must match.\n * - `'subset'`: a `UrlTree` will be determined to be active if it\n * is a subtree of the active route. That is, the active route may contain extra\n * segments, but must at least have all the segments of the `UrlTree` in question.\n */\n paths: 'exact' | 'subset';\n /**\n * - `'exact'`: indicates that the `UrlTree` fragments must be equal.\n * - `'ignored'`: the fragments will not be compared when determining if a\n * `UrlTree` is active.\n */\n fragment: 'exact' | 'ignored';\n}\n\ntype ParamMatchOptions = 'exact' | 'subset' | 'ignored';\n\ntype PathCompareFn = (\n container: UrlSegmentGroup,\n containee: UrlSegmentGroup,\n matrixParams: ParamMatchOptions,\n) => boolean;\ntype ParamCompareFn = (container: Params, containee: Params) => boolean;\n\nconst pathCompareMap: Record = {\n 'exact': equalSegmentGroups,\n 'subset': containsSegmentGroup,\n};\nconst paramCompareMap: Record = {\n 'exact': equalParams,\n 'subset': containsParams,\n 'ignored': () => true,\n};\n\nexport function containsTree(\n container: UrlTree,\n containee: UrlTree,\n options: IsActiveMatchOptions,\n): boolean {\n return (\n pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) &&\n paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) &&\n !(options.fragment === 'exact' && container.fragment !== containee.fragment)\n );\n}\n\nfunction equalParams(container: Params, containee: Params): boolean {\n // TODO: This does not handle array params correctly.\n return shallowEqual(container, containee);\n}\n\nfunction equalSegmentGroups(\n container: UrlSegmentGroup,\n containee: UrlSegmentGroup,\n matrixParams: ParamMatchOptions,\n): boolean {\n if (!equalPath(container.segments, containee.segments)) return false;\n if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) {\n return false;\n }\n if (container.numberOfChildren !== containee.numberOfChildren) return false;\n for (const c in containee.children) {\n if (!container.children[c]) return false;\n if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams))\n return false;\n }\n return true;\n}\n\nfunction containsParams(container: Params, containee: Params): boolean {\n return (\n Object.keys(containee).length <= Object.keys(container).length &&\n Object.keys(containee).every((key) => equalArraysOrString(container[key], containee[key]))\n );\n}\n\nfunction containsSegmentGroup(\n container: UrlSegmentGroup,\n containee: UrlSegmentGroup,\n matrixParams: ParamMatchOptions,\n): boolean {\n return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams);\n}\n\nfunction containsSegmentGroupHelper(\n container: UrlSegmentGroup,\n containee: UrlSegmentGroup,\n containeePaths: UrlSegment[],\n matrixParams: ParamMatchOptions,\n): boolean {\n if (container.segments.length > containeePaths.length) {\n const current = container.segments.slice(0, containeePaths.length);\n if (!equalPath(current, containeePaths)) return false;\n if (containee.hasChildren()) return false;\n if (!matrixParamsMatch(current, containeePaths, matrixParams)) return false;\n return true;\n } else if (container.segments.length === containeePaths.length) {\n if (!equalPath(container.segments, containeePaths)) return false;\n if (!matrixParamsMatch(container.segments, containeePaths, matrixParams)) return false;\n for (const c in containee.children) {\n if (!container.children[c]) return false;\n if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) {\n return false;\n }\n }\n return true;\n } else {\n const current = containeePaths.slice(0, container.segments.length);\n const next = containeePaths.slice(container.segments.length);\n if (!equalPath(container.segments, current)) return false;\n if (!matrixParamsMatch(container.segments, current, matrixParams)) return false;\n if (!container.children[PRIMARY_OUTLET]) return false;\n return containsSegmentGroupHelper(\n container.children[PRIMARY_OUTLET],\n containee,\n next,\n matrixParams,\n );\n }\n}\n\nfunction matrixParamsMatch(\n containerPaths: UrlSegment[],\n containeePaths: UrlSegment[],\n options: ParamMatchOptions,\n) {\n return containeePaths.every((containeeSegment, i) => {\n return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters);\n });\n}\n\n/**\n * @description\n *\n * Represents the parsed URL.\n *\n * Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a\n * serialized tree.\n * UrlTree is a data structure that provides a lot of affordances in dealing with URLs\n *\n * @usageNotes\n * ### Example\n *\n * ```\n * @Component({templateUrl:'template.html'})\n * class MyComponent {\n * constructor(router: Router) {\n * const tree: UrlTree =\n * router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');\n * const f = tree.fragment; // return 'fragment'\n * const q = tree.queryParams; // returns {debug: 'true'}\n * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];\n * const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'\n * g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'\n * g.children['support'].segments; // return 1 segment 'help'\n * }\n * }\n * ```\n *\n * @publicApi\n */\nexport class UrlTree {\n /** @internal */\n _queryParamMap?: ParamMap;\n\n constructor(\n /** The root segment group of the URL tree */\n public root: UrlSegmentGroup = new UrlSegmentGroup([], {}),\n /** The query params of the URL */\n public queryParams: Params = {},\n /** The fragment of the URL */\n public fragment: string | null = null,\n ) {\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n if (root.segments.length > 0) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROOT_URL_SEGMENT,\n 'The root `UrlSegmentGroup` should not contain `segments`. ' +\n 'Instead, these segments belong in the `children` so they can be associated with a named outlet.',\n );\n }\n }\n }\n\n get queryParamMap(): ParamMap {\n this._queryParamMap ??= convertToParamMap(this.queryParams);\n return this._queryParamMap;\n }\n\n /** @docsNotRequired */\n toString(): string {\n return DEFAULT_SERIALIZER.serialize(this);\n }\n}\n\n/**\n * @description\n *\n * Represents the parsed URL segment group.\n *\n * See `UrlTree` for more information.\n *\n * @publicApi\n */\nexport class UrlSegmentGroup {\n /** The parent node in the url tree */\n parent: UrlSegmentGroup | null = null;\n\n constructor(\n /** The URL segments of this group. See `UrlSegment` for more information */\n public segments: UrlSegment[],\n /** The list of children of this group */\n public children: {[key: string]: UrlSegmentGroup},\n ) {\n Object.values(children).forEach((v) => (v.parent = this));\n }\n\n /** Whether the segment has child segments */\n hasChildren(): boolean {\n return this.numberOfChildren > 0;\n }\n\n /** Number of child segments */\n get numberOfChildren(): number {\n return Object.keys(this.children).length;\n }\n\n /** @docsNotRequired */\n toString(): string {\n return serializePaths(this);\n }\n}\n\n/**\n * @description\n *\n * Represents a single URL segment.\n *\n * A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix\n * parameters associated with the segment.\n *\n * @usageNotes\n * ### Example\n *\n * ```\n * @Component({templateUrl:'template.html'})\n * class MyComponent {\n * constructor(router: Router) {\n * const tree: UrlTree = router.parseUrl('/team;id=33');\n * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];\n * const s: UrlSegment[] = g.segments;\n * s[0].path; // returns 'team'\n * s[0].parameters; // returns {id: 33}\n * }\n * }\n * ```\n *\n * @publicApi\n */\nexport class UrlSegment {\n /** @internal */\n _parameterMap?: ParamMap;\n\n constructor(\n /** The path part of a URL segment */\n public path: string,\n\n /** The matrix parameters associated with a segment */\n public parameters: {[name: string]: string},\n ) {}\n\n get parameterMap(): ParamMap {\n this._parameterMap ??= convertToParamMap(this.parameters);\n return this._parameterMap;\n }\n\n /** @docsNotRequired */\n toString(): string {\n return serializePath(this);\n }\n}\n\nexport function equalSegments(as: UrlSegment[], bs: UrlSegment[]): boolean {\n return equalPath(as, bs) && as.every((a, i) => shallowEqual(a.parameters, bs[i].parameters));\n}\n\nexport function equalPath(as: UrlSegment[], bs: UrlSegment[]): boolean {\n if (as.length !== bs.length) return false;\n return as.every((a, i) => a.path === bs[i].path);\n}\n\nexport function mapChildrenIntoArray(\n segment: UrlSegmentGroup,\n fn: (v: UrlSegmentGroup, k: string) => T[],\n): T[] {\n let res: T[] = [];\n Object.entries(segment.children).forEach(([childOutlet, child]) => {\n if (childOutlet === PRIMARY_OUTLET) {\n res = res.concat(fn(child, childOutlet));\n }\n });\n Object.entries(segment.children).forEach(([childOutlet, child]) => {\n if (childOutlet !== PRIMARY_OUTLET) {\n res = res.concat(fn(child, childOutlet));\n }\n });\n return res;\n}\n\n/**\n * @description\n *\n * Serializes and deserializes a URL string into a URL tree.\n *\n * The url serialization strategy is customizable. You can\n * make all URLs case insensitive by providing a custom UrlSerializer.\n *\n * See `DefaultUrlSerializer` for an example of a URL serializer.\n *\n * @publicApi\n */\n@Injectable({providedIn: 'root', useFactory: () => new DefaultUrlSerializer()})\nexport abstract class UrlSerializer {\n /** Parse a url into a `UrlTree` */\n abstract parse(url: string): UrlTree;\n\n /** Converts a `UrlTree` into a url */\n abstract serialize(tree: UrlTree): string;\n}\n\n/**\n * @description\n *\n * A default implementation of the `UrlSerializer`.\n *\n * Example URLs:\n *\n * ```\n * /inbox/33(popup:compose)\n * /inbox/33;open=true/messages/44\n * ```\n *\n * DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the\n * colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to\n * specify route specific parameters.\n *\n * @publicApi\n */\nexport class DefaultUrlSerializer implements UrlSerializer {\n /** Parses a url into a `UrlTree` */\n parse(url: string): UrlTree {\n const p = new UrlParser(url);\n return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());\n }\n\n /** Converts a `UrlTree` into a url */\n serialize(tree: UrlTree): string {\n const segment = `/${serializeSegment(tree.root, true)}`;\n const query = serializeQueryParams(tree.queryParams);\n const fragment =\n typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';\n\n return `${segment}${query}${fragment}`;\n }\n}\n\nconst DEFAULT_SERIALIZER = new DefaultUrlSerializer();\n\nexport function serializePaths(segment: UrlSegmentGroup): string {\n return segment.segments.map((p) => serializePath(p)).join('/');\n}\n\nfunction serializeSegment(segment: UrlSegmentGroup, root: boolean): string {\n if (!segment.hasChildren()) {\n return serializePaths(segment);\n }\n\n if (root) {\n const primary = segment.children[PRIMARY_OUTLET]\n ? serializeSegment(segment.children[PRIMARY_OUTLET], false)\n : '';\n const children: string[] = [];\n\n Object.entries(segment.children).forEach(([k, v]) => {\n if (k !== PRIMARY_OUTLET) {\n children.push(`${k}:${serializeSegment(v, false)}`);\n }\n });\n\n return children.length > 0 ? `${primary}(${children.join('//')})` : primary;\n } else {\n const children = mapChildrenIntoArray(segment, (v: UrlSegmentGroup, k: string) => {\n if (k === PRIMARY_OUTLET) {\n return [serializeSegment(segment.children[PRIMARY_OUTLET], false)];\n }\n\n return [`${k}:${serializeSegment(v, false)}`];\n });\n\n // use no parenthesis if the only child is a primary outlet route\n if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) {\n return `${serializePaths(segment)}/${children[0]}`;\n }\n\n return `${serializePaths(segment)}/(${children.join('//')})`;\n }\n}\n\n/**\n * Encodes a URI string with the default encoding. This function will only ever be called from\n * `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need\n * a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't\n * have to be encoded per https://url.spec.whatwg.org.\n */\nfunction encodeUriString(s: string): string {\n return encodeURIComponent(s)\n .replace(/%40/g, '@')\n .replace(/%3A/gi, ':')\n .replace(/%24/g, '$')\n .replace(/%2C/gi, ',');\n}\n\n/**\n * This function should be used to encode both keys and values in a query string key/value. In\n * the following URL, you need to call encodeUriQuery on \"k\" and \"v\":\n *\n * http://www.site.org/html;mk=mv?k=v#f\n */\nexport function encodeUriQuery(s: string): string {\n return encodeUriString(s).replace(/%3B/gi, ';');\n}\n\n/**\n * This function should be used to encode a URL fragment. In the following URL, you need to call\n * encodeUriFragment on \"f\":\n *\n * http://www.site.org/html;mk=mv?k=v#f\n */\nexport function encodeUriFragment(s: string): string {\n return encodeURI(s);\n}\n\n/**\n * This function should be run on any URI segment as well as the key and value in a key/value\n * pair for matrix params. In the following URL, you need to call encodeUriSegment on \"html\",\n * \"mk\", and \"mv\":\n *\n * http://www.site.org/html;mk=mv?k=v#f\n */\nexport function encodeUriSegment(s: string): string {\n return encodeUriString(s).replace(/\\(/g, '%28').replace(/\\)/g, '%29').replace(/%26/gi, '&');\n}\n\nexport function decode(s: string): string {\n return decodeURIComponent(s);\n}\n\n// Query keys/values should have the \"+\" replaced first, as \"+\" in a query string is \" \".\n// decodeURIComponent function will not decode \"+\" as a space.\nexport function decodeQuery(s: string): string {\n return decode(s.replace(/\\+/g, '%20'));\n}\n\nexport function serializePath(path: UrlSegment): string {\n return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`;\n}\n\nfunction serializeMatrixParams(params: {[key: string]: string}): string {\n return Object.entries(params)\n .map(([key, value]) => `;${encodeUriSegment(key)}=${encodeUriSegment(value)}`)\n .join('');\n}\n\nfunction serializeQueryParams(params: {[key: string]: any}): string {\n const strParams: string[] = Object.entries(params)\n .map(([name, value]) => {\n return Array.isArray(value)\n ? value.map((v) => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&')\n : `${encodeUriQuery(name)}=${encodeUriQuery(value)}`;\n })\n .filter((s) => s);\n\n return strParams.length ? `?${strParams.join('&')}` : '';\n}\n\nconst SEGMENT_RE = /^[^\\/()?;#]+/;\nfunction matchSegments(str: string): string {\n const match = str.match(SEGMENT_RE);\n return match ? match[0] : '';\n}\n\nconst MATRIX_PARAM_SEGMENT_RE = /^[^\\/()?;=#]+/;\nfunction matchMatrixKeySegments(str: string): string {\n const match = str.match(MATRIX_PARAM_SEGMENT_RE);\n return match ? match[0] : '';\n}\n\nconst QUERY_PARAM_RE = /^[^=?]+/;\n// Return the name of the query param at the start of the string or an empty string\nfunction matchQueryParams(str: string): string {\n const match = str.match(QUERY_PARAM_RE);\n return match ? match[0] : '';\n}\n\nconst QUERY_PARAM_VALUE_RE = /^[^]+/;\n// Return the value of the query param at the start of the string or an empty string\nfunction matchUrlQueryParamValue(str: string): string {\n const match = str.match(QUERY_PARAM_VALUE_RE);\n return match ? match[0] : '';\n}\n\nclass UrlParser {\n private remaining: string;\n\n constructor(private url: string) {\n this.remaining = url;\n }\n\n parseRootSegment(): UrlSegmentGroup {\n this.consumeOptional('/');\n\n if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {\n return new UrlSegmentGroup([], {});\n }\n\n // The root segment group never has segments\n return new UrlSegmentGroup([], this.parseChildren());\n }\n\n parseQueryParams(): Params {\n const params: Params = {};\n if (this.consumeOptional('?')) {\n do {\n this.parseQueryParam(params);\n } while (this.consumeOptional('&'));\n }\n return params;\n }\n\n parseFragment(): string | null {\n return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null;\n }\n\n private parseChildren(): {[outlet: string]: UrlSegmentGroup} {\n if (this.remaining === '') {\n return {};\n }\n\n this.consumeOptional('/');\n\n const segments: UrlSegment[] = [];\n if (!this.peekStartsWith('(')) {\n segments.push(this.parseSegment());\n }\n\n while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) {\n this.capture('/');\n segments.push(this.parseSegment());\n }\n\n let children: {[outlet: string]: UrlSegmentGroup} = {};\n if (this.peekStartsWith('/(')) {\n this.capture('/');\n children = this.parseParens(true);\n }\n\n let res: {[outlet: string]: UrlSegmentGroup} = {};\n if (this.peekStartsWith('(')) {\n res = this.parseParens(false);\n }\n\n if (segments.length > 0 || Object.keys(children).length > 0) {\n res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);\n }\n\n return res;\n }\n\n // parse a segment with its matrix parameters\n // ie `name;k1=v1;k2`\n private parseSegment(): UrlSegment {\n const path = matchSegments(this.remaining);\n if (path === '' && this.peekStartsWith(';')) {\n throw new RuntimeError(\n RuntimeErrorCode.EMPTY_PATH_WITH_PARAMS,\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n `Empty path url segment cannot have parameters: '${this.remaining}'.`,\n );\n }\n\n this.capture(path);\n return new UrlSegment(decode(path), this.parseMatrixParams());\n }\n\n private parseMatrixParams(): {[key: string]: string} {\n const params: {[key: string]: string} = {};\n while (this.consumeOptional(';')) {\n this.parseParam(params);\n }\n return params;\n }\n\n private parseParam(params: {[key: string]: string}): void {\n const key = matchMatrixKeySegments(this.remaining);\n if (!key) {\n return;\n }\n this.capture(key);\n let value: any = '';\n if (this.consumeOptional('=')) {\n const valueMatch = matchSegments(this.remaining);\n if (valueMatch) {\n value = valueMatch;\n this.capture(value);\n }\n }\n\n params[decode(key)] = decode(value);\n }\n\n // Parse a single query parameter `name[=value]`\n private parseQueryParam(params: Params): void {\n const key = matchQueryParams(this.remaining);\n if (!key) {\n return;\n }\n this.capture(key);\n let value: any = '';\n if (this.consumeOptional('=')) {\n const valueMatch = matchUrlQueryParamValue(this.remaining);\n if (valueMatch) {\n value = valueMatch;\n this.capture(value);\n }\n }\n\n const decodedKey = decodeQuery(key);\n const decodedVal = decodeQuery(value);\n\n if (params.hasOwnProperty(decodedKey)) {\n // Append to existing values\n let currentVal = params[decodedKey];\n if (!Array.isArray(currentVal)) {\n currentVal = [currentVal];\n params[decodedKey] = currentVal;\n }\n currentVal.push(decodedVal);\n } else {\n // Create a new value\n params[decodedKey] = decodedVal;\n }\n }\n\n // parse `(a/b//outlet_name:c/d)`\n private parseParens(allowPrimary: boolean): {[outlet: string]: UrlSegmentGroup} {\n const segments: {[key: string]: UrlSegmentGroup} = {};\n this.capture('(');\n\n while (!this.consumeOptional(')') && this.remaining.length > 0) {\n const path = matchSegments(this.remaining);\n\n const next = this.remaining[path.length];\n\n // if is is not one of these characters, then the segment was unescaped\n // or the group was not closed\n if (next !== '/' && next !== ')' && next !== ';') {\n throw new RuntimeError(\n RuntimeErrorCode.UNPARSABLE_URL,\n (typeof ngDevMode === 'undefined' || ngDevMode) && `Cannot parse url '${this.url}'`,\n );\n }\n\n let outletName: string = undefined!;\n if (path.indexOf(':') > -1) {\n outletName = path.slice(0, path.indexOf(':'));\n this.capture(outletName);\n this.capture(':');\n } else if (allowPrimary) {\n outletName = PRIMARY_OUTLET;\n }\n\n const children = this.parseChildren();\n segments[outletName] =\n Object.keys(children).length === 1\n ? children[PRIMARY_OUTLET]\n : new UrlSegmentGroup([], children);\n this.consumeOptional('//');\n }\n\n return segments;\n }\n\n private peekStartsWith(str: string): boolean {\n return this.remaining.startsWith(str);\n }\n\n // Consumes the prefix when it is present and returns whether it has been consumed\n private consumeOptional(str: string): boolean {\n if (this.peekStartsWith(str)) {\n this.remaining = this.remaining.substring(str.length);\n return true;\n }\n return false;\n }\n\n private capture(str: string): void {\n if (!this.consumeOptional(str)) {\n throw new RuntimeError(\n RuntimeErrorCode.UNEXPECTED_VALUE_IN_URL,\n (typeof ngDevMode === 'undefined' || ngDevMode) && `Expected \"${str}\".`,\n );\n }\n }\n}\n\nexport function createRoot(rootCandidate: UrlSegmentGroup) {\n return rootCandidate.segments.length > 0\n ? new UrlSegmentGroup([], {[PRIMARY_OUTLET]: rootCandidate})\n : rootCandidate;\n}\n\n/**\n * Recursively\n * - merges primary segment children into their parents\n * - drops empty children (those which have no segments and no children themselves). This latter\n * prevents serializing a group into something like `/a(aux:)`, where `aux` is an empty child\n * segment.\n * - merges named outlets without a primary segment sibling into the children. This prevents\n * serializing a URL like `//(a:a)(b:b) instead of `/(a:a//b:b)` when the aux b route lives on the\n * root but the `a` route lives under an empty path primary route.\n */\nexport function squashSegmentGroup(segmentGroup: UrlSegmentGroup): UrlSegmentGroup {\n const newChildren: Record = {};\n for (const [childOutlet, child] of Object.entries(segmentGroup.children)) {\n const childCandidate = squashSegmentGroup(child);\n // moves named children in an empty path primary child into this group\n if (\n childOutlet === PRIMARY_OUTLET &&\n childCandidate.segments.length === 0 &&\n childCandidate.hasChildren()\n ) {\n for (const [grandChildOutlet, grandChild] of Object.entries(childCandidate.children)) {\n newChildren[grandChildOutlet] = grandChild;\n }\n } // don't add empty children\n else if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {\n newChildren[childOutlet] = childCandidate;\n }\n }\n const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);\n return mergeTrivialChildren(s);\n}\n\n/**\n * When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.\n *\n * When a segment group has only one child which is a primary outlet, merges that child into the\n * parent. That is, the child segment group's segments are merged into the `s` and the child's\n * children become the children of `s`. Think of this like a 'squash', merging the child segment\n * group into the parent.\n */\nfunction mergeTrivialChildren(s: UrlSegmentGroup): UrlSegmentGroup {\n if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {\n const c = s.children[PRIMARY_OUTLET];\n return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);\n }\n\n return s;\n}\n\nexport function isUrlTree(v: any): v is UrlTree {\n return v instanceof UrlTree;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {ɵRuntimeError as RuntimeError} from '@angular/core';\n\nimport {RuntimeErrorCode} from './errors';\nimport {ActivatedRouteSnapshot} from './router_state';\nimport {Params, PRIMARY_OUTLET} from './shared';\nimport {createRoot, squashSegmentGroup, UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';\nimport {last, shallowEqual} from './utils/collection';\n\n/**\n * Creates a `UrlTree` relative to an `ActivatedRouteSnapshot`.\n *\n * @publicApi\n *\n *\n * @param relativeTo The `ActivatedRouteSnapshot` to apply the commands to\n * @param commands An array of URL fragments with which to construct the new URL tree.\n * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path\n * segments, followed by the parameters for each segment.\n * The fragments are applied to the one provided in the `relativeTo` parameter.\n * @param queryParams The query parameters for the `UrlTree`. `null` if the `UrlTree` does not have\n * any query parameters.\n * @param fragment The fragment for the `UrlTree`. `null` if the `UrlTree` does not have a fragment.\n *\n * @usageNotes\n *\n * ```\n * // create /team/33/user/11\n * createUrlTreeFromSnapshot(snapshot, ['/team', 33, 'user', 11]);\n *\n * // create /team/33;expand=true/user/11\n * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {expand: true}, 'user', 11]);\n *\n * // you can collapse static segments like this (this works only with the first passed-in value):\n * createUrlTreeFromSnapshot(snapshot, ['/team/33/user', userId]);\n *\n * // If the first segment can contain slashes, and you do not want the router to split it,\n * // you can do the following:\n * createUrlTreeFromSnapshot(snapshot, [{segmentPath: '/one/two'}]);\n *\n * // create /team/33/(user/11//right:chat)\n * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right:\n * 'chat'}}], null, null);\n *\n * // remove the right secondary node\n * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right: null}}]);\n *\n * // For the examples below, assume the current URL is for the `/team/33/user/11` and the\n * `ActivatedRouteSnapshot` points to `user/11`:\n *\n * // navigate to /team/33/user/11/details\n * createUrlTreeFromSnapshot(snapshot, ['details']);\n *\n * // navigate to /team/33/user/22\n * createUrlTreeFromSnapshot(snapshot, ['../22']);\n *\n * // navigate to /team/44/user/22\n * createUrlTreeFromSnapshot(snapshot, ['../../team/44/user/22']);\n * ```\n */\nexport function createUrlTreeFromSnapshot(\n relativeTo: ActivatedRouteSnapshot,\n commands: any[],\n queryParams: Params | null = null,\n fragment: string | null = null,\n): UrlTree {\n const relativeToUrlSegmentGroup = createSegmentGroupFromRoute(relativeTo);\n return createUrlTreeFromSegmentGroup(relativeToUrlSegmentGroup, commands, queryParams, fragment);\n}\n\nexport function createSegmentGroupFromRoute(route: ActivatedRouteSnapshot): UrlSegmentGroup {\n let targetGroup: UrlSegmentGroup | undefined;\n\n function createSegmentGroupFromRouteRecursive(\n currentRoute: ActivatedRouteSnapshot,\n ): UrlSegmentGroup {\n const childOutlets: {[outlet: string]: UrlSegmentGroup} = {};\n for (const childSnapshot of currentRoute.children) {\n const root = createSegmentGroupFromRouteRecursive(childSnapshot);\n childOutlets[childSnapshot.outlet] = root;\n }\n const segmentGroup = new UrlSegmentGroup(currentRoute.url, childOutlets);\n if (currentRoute === route) {\n targetGroup = segmentGroup;\n }\n return segmentGroup;\n }\n const rootCandidate = createSegmentGroupFromRouteRecursive(route.root);\n const rootSegmentGroup = createRoot(rootCandidate);\n\n return targetGroup ?? rootSegmentGroup;\n}\n\nexport function createUrlTreeFromSegmentGroup(\n relativeTo: UrlSegmentGroup,\n commands: any[],\n queryParams: Params | null,\n fragment: string | null,\n): UrlTree {\n let root = relativeTo;\n while (root.parent) {\n root = root.parent;\n }\n // There are no commands so the `UrlTree` goes to the same path as the one created from the\n // `UrlSegmentGroup`. All we need to do is update the `queryParams` and `fragment` without\n // applying any other logic.\n if (commands.length === 0) {\n return tree(root, root, root, queryParams, fragment);\n }\n\n const nav = computeNavigation(commands);\n\n if (nav.toRoot()) {\n return tree(root, root, new UrlSegmentGroup([], {}), queryParams, fragment);\n }\n\n const position = findStartingPositionForTargetGroup(nav, root, relativeTo);\n const newSegmentGroup = position.processChildren\n ? updateSegmentGroupChildren(position.segmentGroup, position.index, nav.commands)\n : updateSegmentGroup(position.segmentGroup, position.index, nav.commands);\n return tree(root, position.segmentGroup, newSegmentGroup, queryParams, fragment);\n}\n\nfunction isMatrixParams(command: any): boolean {\n return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;\n}\n\n/**\n * Determines if a given command has an `outlets` map. When we encounter a command\n * with an outlets k/v map, we need to apply each outlet individually to the existing segment.\n */\nfunction isCommandWithOutlets(command: any): command is {outlets: {[key: string]: any}} {\n return typeof command === 'object' && command != null && command.outlets;\n}\n\nfunction tree(\n oldRoot: UrlSegmentGroup,\n oldSegmentGroup: UrlSegmentGroup,\n newSegmentGroup: UrlSegmentGroup,\n queryParams: Params | null,\n fragment: string | null,\n): UrlTree {\n let qp: any = {};\n if (queryParams) {\n Object.entries(queryParams).forEach(([name, value]) => {\n qp[name] = Array.isArray(value) ? value.map((v: any) => `${v}`) : `${value}`;\n });\n }\n\n let rootCandidate: UrlSegmentGroup;\n if (oldRoot === oldSegmentGroup) {\n rootCandidate = newSegmentGroup;\n } else {\n rootCandidate = replaceSegment(oldRoot, oldSegmentGroup, newSegmentGroup);\n }\n\n const newRoot = createRoot(squashSegmentGroup(rootCandidate));\n return new UrlTree(newRoot, qp, fragment);\n}\n\n/**\n * Replaces the `oldSegment` which is located in some child of the `current` with the `newSegment`.\n * This also has the effect of creating new `UrlSegmentGroup` copies to update references. This\n * shouldn't be necessary but the fallback logic for an invalid ActivatedRoute in the creation uses\n * the Router's current url tree. If we don't create new segment groups, we end up modifying that\n * value.\n */\nfunction replaceSegment(\n current: UrlSegmentGroup,\n oldSegment: UrlSegmentGroup,\n newSegment: UrlSegmentGroup,\n): UrlSegmentGroup {\n const children: {[key: string]: UrlSegmentGroup} = {};\n Object.entries(current.children).forEach(([outletName, c]) => {\n if (c === oldSegment) {\n children[outletName] = newSegment;\n } else {\n children[outletName] = replaceSegment(c, oldSegment, newSegment);\n }\n });\n return new UrlSegmentGroup(current.segments, children);\n}\n\nclass Navigation {\n constructor(\n public isAbsolute: boolean,\n public numberOfDoubleDots: number,\n public commands: any[],\n ) {\n if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {\n throw new RuntimeError(\n RuntimeErrorCode.ROOT_SEGMENT_MATRIX_PARAMS,\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n 'Root segment cannot have matrix parameters',\n );\n }\n\n const cmdWithOutlet = commands.find(isCommandWithOutlets);\n if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {\n throw new RuntimeError(\n RuntimeErrorCode.MISPLACED_OUTLETS_COMMAND,\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n '{outlets:{}} has to be the last command',\n );\n }\n }\n\n public toRoot(): boolean {\n return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';\n }\n}\n\n/** Transforms commands to a normalized `Navigation` */\nfunction computeNavigation(commands: any[]): Navigation {\n if (typeof commands[0] === 'string' && commands.length === 1 && commands[0] === '/') {\n return new Navigation(true, 0, commands);\n }\n\n let numberOfDoubleDots = 0;\n let isAbsolute = false;\n\n const res: any[] = commands.reduce((res, cmd, cmdIdx) => {\n if (typeof cmd === 'object' && cmd != null) {\n if (cmd.outlets) {\n const outlets: {[k: string]: any} = {};\n Object.entries(cmd.outlets).forEach(([name, commands]) => {\n outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;\n });\n return [...res, {outlets}];\n }\n\n if (cmd.segmentPath) {\n return [...res, cmd.segmentPath];\n }\n }\n\n if (!(typeof cmd === 'string')) {\n return [...res, cmd];\n }\n\n if (cmdIdx === 0) {\n cmd.split('/').forEach((urlPart, partIndex) => {\n if (partIndex == 0 && urlPart === '.') {\n // skip './a'\n } else if (partIndex == 0 && urlPart === '') {\n // '/a'\n isAbsolute = true;\n } else if (urlPart === '..') {\n // '../a'\n numberOfDoubleDots++;\n } else if (urlPart != '') {\n res.push(urlPart);\n }\n });\n\n return res;\n }\n\n return [...res, cmd];\n }, []);\n\n return new Navigation(isAbsolute, numberOfDoubleDots, res);\n}\n\nclass Position {\n constructor(\n public segmentGroup: UrlSegmentGroup,\n public processChildren: boolean,\n public index: number,\n ) {}\n}\n\nfunction findStartingPositionForTargetGroup(\n nav: Navigation,\n root: UrlSegmentGroup,\n target: UrlSegmentGroup,\n): Position {\n if (nav.isAbsolute) {\n return new Position(root, true, 0);\n }\n\n if (!target) {\n // `NaN` is used only to maintain backwards compatibility with incorrectly mocked\n // `ActivatedRouteSnapshot` in tests. In prior versions of this code, the position here was\n // determined based on an internal property that was rarely mocked, resulting in `NaN`. In\n // reality, this code path should _never_ be touched since `target` is not allowed to be falsey.\n return new Position(root, false, NaN);\n }\n if (target.parent === null) {\n return new Position(target, true, 0);\n }\n\n const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;\n const index = target.segments.length - 1 + modifier;\n return createPositionApplyingDoubleDots(target, index, nav.numberOfDoubleDots);\n}\n\nfunction createPositionApplyingDoubleDots(\n group: UrlSegmentGroup,\n index: number,\n numberOfDoubleDots: number,\n): Position {\n let g = group;\n let ci = index;\n let dd = numberOfDoubleDots;\n while (dd > ci) {\n dd -= ci;\n g = g.parent!;\n if (!g) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_DOUBLE_DOTS,\n (typeof ngDevMode === 'undefined' || ngDevMode) && \"Invalid number of '../'\",\n );\n }\n ci = g.segments.length;\n }\n return new Position(g, false, ci - dd);\n}\n\nfunction getOutlets(commands: unknown[]): {[k: string]: unknown[] | string} {\n if (isCommandWithOutlets(commands[0])) {\n return commands[0].outlets;\n }\n\n return {[PRIMARY_OUTLET]: commands};\n}\n\nfunction updateSegmentGroup(\n segmentGroup: UrlSegmentGroup | undefined,\n startIndex: number,\n commands: any[],\n): UrlSegmentGroup {\n segmentGroup ??= new UrlSegmentGroup([], {});\n if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {\n return updateSegmentGroupChildren(segmentGroup, startIndex, commands);\n }\n\n const m = prefixedWith(segmentGroup, startIndex, commands);\n const slicedCommands = commands.slice(m.commandIndex);\n if (m.match && m.pathIndex < segmentGroup.segments.length) {\n const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});\n g.children[PRIMARY_OUTLET] = new UrlSegmentGroup(\n segmentGroup.segments.slice(m.pathIndex),\n segmentGroup.children,\n );\n return updateSegmentGroupChildren(g, 0, slicedCommands);\n } else if (m.match && slicedCommands.length === 0) {\n return new UrlSegmentGroup(segmentGroup.segments, {});\n } else if (m.match && !segmentGroup.hasChildren()) {\n return createNewSegmentGroup(segmentGroup, startIndex, commands);\n } else if (m.match) {\n return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands);\n } else {\n return createNewSegmentGroup(segmentGroup, startIndex, commands);\n }\n}\n\nfunction updateSegmentGroupChildren(\n segmentGroup: UrlSegmentGroup,\n startIndex: number,\n commands: any[],\n): UrlSegmentGroup {\n if (commands.length === 0) {\n return new UrlSegmentGroup(segmentGroup.segments, {});\n } else {\n const outlets = getOutlets(commands);\n const children: {[key: string]: UrlSegmentGroup} = {};\n // If the set of commands applies to anything other than the primary outlet and the child\n // segment is an empty path primary segment on its own, we want to apply the commands to the\n // empty child path rather than here. The outcome is that the empty primary child is effectively\n // removed from the final output UrlTree. Imagine the following config:\n //\n // {path: '', children: [{path: '**', outlet: 'popup'}]}.\n //\n // Navigation to /(popup:a) will activate the child outlet correctly Given a follow-up\n // navigation with commands\n // ['/', {outlets: {'popup': 'b'}}], we _would not_ want to apply the outlet commands to the\n // root segment because that would result in\n // //(popup:a)(popup:b) since the outlet command got applied one level above where it appears in\n // the `ActivatedRoute` rather than updating the existing one.\n //\n // Because empty paths do not appear in the URL segments and the fact that the segments used in\n // the output `UrlTree` are squashed to eliminate these empty paths where possible\n // https://github.com/angular/angular/blob/13f10de40e25c6900ca55bd83b36bd533dacfa9e/packages/router/src/url_tree.ts#L755\n // it can be hard to determine what is the right thing to do when applying commands to a\n // `UrlSegmentGroup` that is created from an \"unsquashed\"/expanded `ActivatedRoute` tree.\n // This code effectively \"squashes\" empty path primary routes when they have no siblings on\n // the same level of the tree.\n if (\n Object.keys(outlets).some((o) => o !== PRIMARY_OUTLET) &&\n segmentGroup.children[PRIMARY_OUTLET] &&\n segmentGroup.numberOfChildren === 1 &&\n segmentGroup.children[PRIMARY_OUTLET].segments.length === 0\n ) {\n const childrenOfEmptyChild = updateSegmentGroupChildren(\n segmentGroup.children[PRIMARY_OUTLET],\n startIndex,\n commands,\n );\n return new UrlSegmentGroup(segmentGroup.segments, childrenOfEmptyChild.children);\n }\n\n Object.entries(outlets).forEach(([outlet, commands]) => {\n if (typeof commands === 'string') {\n commands = [commands];\n }\n if (commands !== null) {\n children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);\n }\n });\n\n Object.entries(segmentGroup.children).forEach(([childOutlet, child]) => {\n if (outlets[childOutlet] === undefined) {\n children[childOutlet] = child;\n }\n });\n return new UrlSegmentGroup(segmentGroup.segments, children);\n }\n}\n\nfunction prefixedWith(segmentGroup: UrlSegmentGroup, startIndex: number, commands: any[]) {\n let currentCommandIndex = 0;\n let currentPathIndex = startIndex;\n\n const noMatch = {match: false, pathIndex: 0, commandIndex: 0};\n while (currentPathIndex < segmentGroup.segments.length) {\n if (currentCommandIndex >= commands.length) return noMatch;\n const path = segmentGroup.segments[currentPathIndex];\n const command = commands[currentCommandIndex];\n // Do not try to consume command as part of the prefixing if it has outlets because it can\n // contain outlets other than the one being processed. Consuming the outlets command would\n // result in other outlets being ignored.\n if (isCommandWithOutlets(command)) {\n break;\n }\n const curr = `${command}`;\n const next =\n currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;\n\n if (currentPathIndex > 0 && curr === undefined) break;\n\n if (curr && next && typeof next === 'object' && next.outlets === undefined) {\n if (!compare(curr, next, path)) return noMatch;\n currentCommandIndex += 2;\n } else {\n if (!compare(curr, {}, path)) return noMatch;\n currentCommandIndex++;\n }\n currentPathIndex++;\n }\n\n return {match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex};\n}\n\nfunction createNewSegmentGroup(\n segmentGroup: UrlSegmentGroup,\n startIndex: number,\n commands: any[],\n): UrlSegmentGroup {\n const paths = segmentGroup.segments.slice(0, startIndex);\n\n let i = 0;\n while (i < commands.length) {\n const command = commands[i];\n if (isCommandWithOutlets(command)) {\n const children = createNewSegmentChildren(command.outlets);\n return new UrlSegmentGroup(paths, children);\n }\n\n // if we start with an object literal, we need to reuse the path part from the segment\n if (i === 0 && isMatrixParams(commands[0])) {\n const p = segmentGroup.segments[startIndex];\n paths.push(new UrlSegment(p.path, stringify(commands[0])));\n i++;\n continue;\n }\n\n const curr = isCommandWithOutlets(command) ? command.outlets[PRIMARY_OUTLET] : `${command}`;\n const next = i < commands.length - 1 ? commands[i + 1] : null;\n if (curr && next && isMatrixParams(next)) {\n paths.push(new UrlSegment(curr, stringify(next)));\n i += 2;\n } else {\n paths.push(new UrlSegment(curr, {}));\n i++;\n }\n }\n return new UrlSegmentGroup(paths, {});\n}\n\nfunction createNewSegmentChildren(outlets: {[name: string]: unknown[] | string}): {\n [outlet: string]: UrlSegmentGroup;\n} {\n const children: {[outlet: string]: UrlSegmentGroup} = {};\n Object.entries(outlets).forEach(([outlet, commands]) => {\n if (typeof commands === 'string') {\n commands = [commands];\n }\n if (commands !== null) {\n children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands);\n }\n });\n return children;\n}\n\nfunction stringify(params: {[key: string]: any}): {[key: string]: string} {\n const res: {[key: string]: string} = {};\n Object.entries(params).forEach(([k, v]) => (res[k] = `${v}`));\n return res;\n}\n\nfunction compare(path: string, params: {[key: string]: any}, segment: UrlSegment): boolean {\n return path == segment.path && shallowEqual(params, segment.parameters);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {NavigationBehaviorOptions, Route} from './models';\nimport {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';\nimport {UrlTree} from './url_tree';\n\n/**\n * Identifies the call or event that triggered a navigation.\n *\n * * 'imperative': Triggered by `router.navigateByUrl()` or `router.navigate()`.\n * * 'popstate' : Triggered by a `popstate` event.\n * * 'hashchange'-: Triggered by a `hashchange` event.\n *\n * @publicApi\n */\nexport type NavigationTrigger = 'imperative' | 'popstate' | 'hashchange';\nexport const IMPERATIVE_NAVIGATION = 'imperative';\n\n/**\n * Identifies the type of a router event.\n *\n * @publicApi\n */\nexport enum EventType {\n NavigationStart,\n NavigationEnd,\n NavigationCancel,\n NavigationError,\n RoutesRecognized,\n ResolveStart,\n ResolveEnd,\n GuardsCheckStart,\n GuardsCheckEnd,\n RouteConfigLoadStart,\n RouteConfigLoadEnd,\n ChildActivationStart,\n ChildActivationEnd,\n ActivationStart,\n ActivationEnd,\n Scroll,\n NavigationSkipped,\n}\n\n/**\n * Base for events the router goes through, as opposed to events tied to a specific\n * route. Fired one time for any given navigation.\n *\n * The following code shows how a class subscribes to router events.\n *\n * ```ts\n * import {Event, RouterEvent, Router} from '@angular/router';\n *\n * class MyService {\n * constructor(public router: Router) {\n * router.events.pipe(\n * filter((e: Event | RouterEvent): e is RouterEvent => e instanceof RouterEvent)\n * ).subscribe((e: RouterEvent) => {\n * // Do something\n * });\n * }\n * }\n * ```\n *\n * @see {@link Event}\n * @see [Router events summary](guide/routing/router-reference#router-events)\n * @publicApi\n */\nexport class RouterEvent {\n constructor(\n /** A unique ID that the router assigns to every router navigation. */\n public id: number,\n /** The URL that is the destination for this navigation. */\n public url: string,\n ) {}\n}\n\n/**\n * An event triggered when a navigation starts.\n *\n * @publicApi\n */\nexport class NavigationStart extends RouterEvent {\n readonly type = EventType.NavigationStart;\n\n /**\n * Identifies the call or event that triggered the navigation.\n * An `imperative` trigger is a call to `router.navigateByUrl()` or `router.navigate()`.\n *\n * @see {@link NavigationEnd}\n * @see {@link NavigationCancel}\n * @see {@link NavigationError}\n */\n navigationTrigger?: NavigationTrigger;\n\n /**\n * The navigation state that was previously supplied to the `pushState` call,\n * when the navigation is triggered by a `popstate` event. Otherwise null.\n *\n * The state object is defined by `NavigationExtras`, and contains any\n * developer-defined state value, as well as a unique ID that\n * the router assigns to every router transition/navigation.\n *\n * From the perspective of the router, the router never \"goes back\".\n * When the user clicks on the back button in the browser,\n * a new navigation ID is created.\n *\n * Use the ID in this previous-state object to differentiate between a newly created\n * state and one returned to by a `popstate` event, so that you can restore some\n * remembered state, such as scroll position.\n *\n */\n restoredState?: {[k: string]: any; navigationId: number} | null;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /** @docsNotRequired */\n navigationTrigger: NavigationTrigger = 'imperative',\n /** @docsNotRequired */\n restoredState: {[k: string]: any; navigationId: number} | null = null,\n ) {\n super(id, url);\n this.navigationTrigger = navigationTrigger;\n this.restoredState = restoredState;\n }\n\n /** @docsNotRequired */\n override toString(): string {\n return `NavigationStart(id: ${this.id}, url: '${this.url}')`;\n }\n}\n\n/**\n * An event triggered when a navigation ends successfully.\n *\n * @see {@link NavigationStart}\n * @see {@link NavigationCancel}\n * @see {@link NavigationError}\n *\n * @publicApi\n */\nexport class NavigationEnd extends RouterEvent {\n readonly type = EventType.NavigationEnd;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /** @docsNotRequired */\n public urlAfterRedirects: string,\n ) {\n super(id, url);\n }\n\n /** @docsNotRequired */\n override toString(): string {\n return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`;\n }\n}\n\n/**\n * A code for the `NavigationCancel` event of the `Router` to indicate the\n * reason a navigation failed.\n *\n * @publicApi\n */\nexport enum NavigationCancellationCode {\n /**\n * A navigation failed because a guard returned a `UrlTree` to redirect.\n */\n Redirect,\n /**\n * A navigation failed because a more recent navigation started.\n */\n SupersededByNewNavigation,\n /**\n * A navigation failed because one of the resolvers completed without emitting a value.\n */\n NoDataFromResolver,\n /**\n * A navigation failed because a guard returned `false`.\n */\n GuardRejected,\n}\n\n/**\n * A code for the `NavigationSkipped` event of the `Router` to indicate the\n * reason a navigation was skipped.\n *\n * @publicApi\n */\nexport enum NavigationSkippedCode {\n /**\n * A navigation was skipped because the navigation URL was the same as the current Router URL.\n */\n IgnoredSameUrlNavigation,\n /**\n * A navigation was skipped because the configured `UrlHandlingStrategy` return `false` for both\n * the current Router URL and the target of the navigation.\n *\n * @see {@link UrlHandlingStrategy}\n */\n IgnoredByUrlHandlingStrategy,\n}\n\n/**\n * An event triggered when a navigation is canceled, directly or indirectly.\n * This can happen for several reasons including when a route guard\n * returns `false` or initiates a redirect by returning a `UrlTree`.\n *\n * @see {@link NavigationStart}\n * @see {@link NavigationEnd}\n * @see {@link NavigationError}\n *\n * @publicApi\n */\nexport class NavigationCancel extends RouterEvent {\n readonly type = EventType.NavigationCancel;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /**\n * A description of why the navigation was cancelled. For debug purposes only. Use `code`\n * instead for a stable cancellation reason that can be used in production.\n */\n public reason: string,\n /**\n * A code to indicate why the navigation was canceled. This cancellation code is stable for\n * the reason and can be relied on whereas the `reason` string could change and should not be\n * used in production.\n */\n readonly code?: NavigationCancellationCode,\n ) {\n super(id, url);\n }\n\n /** @docsNotRequired */\n override toString(): string {\n return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;\n }\n}\n\n/**\n * An event triggered when a navigation is skipped.\n * This can happen for a couple reasons including onSameUrlHandling\n * is set to `ignore` and the navigation URL is not different than the\n * current state.\n *\n * @publicApi\n */\nexport class NavigationSkipped extends RouterEvent {\n readonly type = EventType.NavigationSkipped;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /**\n * A description of why the navigation was skipped. For debug purposes only. Use `code`\n * instead for a stable skipped reason that can be used in production.\n */\n public reason: string,\n /**\n * A code to indicate why the navigation was skipped. This code is stable for\n * the reason and can be relied on whereas the `reason` string could change and should not be\n * used in production.\n */\n readonly code?: NavigationSkippedCode,\n ) {\n super(id, url);\n }\n}\n\n/**\n * An event triggered when a navigation fails due to an unexpected error.\n *\n * @see {@link NavigationStart}\n * @see {@link NavigationEnd}\n * @see {@link NavigationCancel}\n *\n * @publicApi\n */\nexport class NavigationError extends RouterEvent {\n readonly type = EventType.NavigationError;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /** @docsNotRequired */\n public error: any,\n /**\n * The target of the navigation when the error occurred.\n *\n * Note that this can be `undefined` because an error could have occurred before the\n * `RouterStateSnapshot` was created for the navigation.\n */\n readonly target?: RouterStateSnapshot,\n ) {\n super(id, url);\n }\n\n /** @docsNotRequired */\n override toString(): string {\n return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`;\n }\n}\n\n/**\n * An event triggered when routes are recognized.\n *\n * @publicApi\n */\nexport class RoutesRecognized extends RouterEvent {\n readonly type = EventType.RoutesRecognized;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /** @docsNotRequired */\n public urlAfterRedirects: string,\n /** @docsNotRequired */\n public state: RouterStateSnapshot,\n ) {\n super(id, url);\n }\n\n /** @docsNotRequired */\n override toString(): string {\n return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;\n }\n}\n\n/**\n * An event triggered at the start of the Guard phase of routing.\n *\n * @see {@link GuardsCheckEnd}\n *\n * @publicApi\n */\nexport class GuardsCheckStart extends RouterEvent {\n readonly type = EventType.GuardsCheckStart;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /** @docsNotRequired */\n public urlAfterRedirects: string,\n /** @docsNotRequired */\n public state: RouterStateSnapshot,\n ) {\n super(id, url);\n }\n\n override toString(): string {\n return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;\n }\n}\n\n/**\n * An event triggered at the end of the Guard phase of routing.\n *\n * @see {@link GuardsCheckStart}\n *\n * @publicApi\n */\nexport class GuardsCheckEnd extends RouterEvent {\n readonly type = EventType.GuardsCheckEnd;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /** @docsNotRequired */\n public urlAfterRedirects: string,\n /** @docsNotRequired */\n public state: RouterStateSnapshot,\n /** @docsNotRequired */\n public shouldActivate: boolean,\n ) {\n super(id, url);\n }\n\n override toString(): string {\n return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;\n }\n}\n\n/**\n * An event triggered at the start of the Resolve phase of routing.\n *\n * Runs in the \"resolve\" phase whether or not there is anything to resolve.\n * In future, may change to only run when there are things to be resolved.\n *\n * @see {@link ResolveEnd}\n *\n * @publicApi\n */\nexport class ResolveStart extends RouterEvent {\n readonly type = EventType.ResolveStart;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /** @docsNotRequired */\n public urlAfterRedirects: string,\n /** @docsNotRequired */\n public state: RouterStateSnapshot,\n ) {\n super(id, url);\n }\n\n override toString(): string {\n return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;\n }\n}\n\n/**\n * An event triggered at the end of the Resolve phase of routing.\n * @see {@link ResolveStart}\n *\n * @publicApi\n */\nexport class ResolveEnd extends RouterEvent {\n readonly type = EventType.ResolveEnd;\n\n constructor(\n /** @docsNotRequired */\n id: number,\n /** @docsNotRequired */\n url: string,\n /** @docsNotRequired */\n public urlAfterRedirects: string,\n /** @docsNotRequired */\n public state: RouterStateSnapshot,\n ) {\n super(id, url);\n }\n\n override toString(): string {\n return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;\n }\n}\n\n/**\n * An event triggered before lazy loading a route configuration.\n *\n * @see {@link RouteConfigLoadEnd}\n *\n * @publicApi\n */\nexport class RouteConfigLoadStart {\n readonly type = EventType.RouteConfigLoadStart;\n\n constructor(\n /** @docsNotRequired */\n public route: Route,\n ) {}\n toString(): string {\n return `RouteConfigLoadStart(path: ${this.route.path})`;\n }\n}\n\n/**\n * An event triggered when a route has been lazy loaded.\n *\n * @see {@link RouteConfigLoadStart}\n *\n * @publicApi\n */\nexport class RouteConfigLoadEnd {\n readonly type = EventType.RouteConfigLoadEnd;\n\n constructor(\n /** @docsNotRequired */\n public route: Route,\n ) {}\n toString(): string {\n return `RouteConfigLoadEnd(path: ${this.route.path})`;\n }\n}\n\n/**\n * An event triggered at the start of the child-activation\n * part of the Resolve phase of routing.\n * @see {@link ChildActivationEnd}\n * @see {@link ResolveStart}\n *\n * @publicApi\n */\nexport class ChildActivationStart {\n readonly type = EventType.ChildActivationStart;\n\n constructor(\n /** @docsNotRequired */\n public snapshot: ActivatedRouteSnapshot,\n ) {}\n toString(): string {\n const path = (this.snapshot.routeConfig && this.snapshot.routeConfig.path) || '';\n return `ChildActivationStart(path: '${path}')`;\n }\n}\n\n/**\n * An event triggered at the end of the child-activation part\n * of the Resolve phase of routing.\n * @see {@link ChildActivationStart}\n * @see {@link ResolveStart}\n * @publicApi\n */\nexport class ChildActivationEnd {\n readonly type = EventType.ChildActivationEnd;\n\n constructor(\n /** @docsNotRequired */\n public snapshot: ActivatedRouteSnapshot,\n ) {}\n toString(): string {\n const path = (this.snapshot.routeConfig && this.snapshot.routeConfig.path) || '';\n return `ChildActivationEnd(path: '${path}')`;\n }\n}\n\n/**\n * An event triggered at the start of the activation part\n * of the Resolve phase of routing.\n * @see {@link ActivationEnd}\n * @see {@link ResolveStart}\n *\n * @publicApi\n */\nexport class ActivationStart {\n readonly type = EventType.ActivationStart;\n\n constructor(\n /** @docsNotRequired */\n public snapshot: ActivatedRouteSnapshot,\n ) {}\n toString(): string {\n const path = (this.snapshot.routeConfig && this.snapshot.routeConfig.path) || '';\n return `ActivationStart(path: '${path}')`;\n }\n}\n\n/**\n * An event triggered at the end of the activation part\n * of the Resolve phase of routing.\n * @see {@link ActivationStart}\n * @see {@link ResolveStart}\n *\n * @publicApi\n */\nexport class ActivationEnd {\n readonly type = EventType.ActivationEnd;\n\n constructor(\n /** @docsNotRequired */\n public snapshot: ActivatedRouteSnapshot,\n ) {}\n toString(): string {\n const path = (this.snapshot.routeConfig && this.snapshot.routeConfig.path) || '';\n return `ActivationEnd(path: '${path}')`;\n }\n}\n\n/**\n * An event triggered by scrolling.\n *\n * @publicApi\n */\nexport class Scroll {\n readonly type = EventType.Scroll;\n\n constructor(\n /** @docsNotRequired */\n readonly routerEvent: NavigationEnd | NavigationSkipped,\n\n /** @docsNotRequired */\n readonly position: [number, number] | null,\n\n /** @docsNotRequired */\n readonly anchor: string | null,\n ) {}\n\n toString(): string {\n const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;\n return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;\n }\n}\n\nexport class BeforeActivateRoutes {}\nexport class RedirectRequest {\n constructor(\n readonly url: UrlTree,\n readonly navigationBehaviorOptions: NavigationBehaviorOptions | undefined,\n ) {}\n}\nexport type PrivateRouterEvents = BeforeActivateRoutes | RedirectRequest;\n\n/**\n * Router events that allow you to track the lifecycle of the router.\n *\n * The events occur in the following sequence:\n *\n * * [NavigationStart](api/router/NavigationStart): Navigation starts.\n * * [RouteConfigLoadStart](api/router/RouteConfigLoadStart): Before\n * the router [lazy loads](guide/routing/common-router-tasks#lazy-loading) a route configuration.\n * * [RouteConfigLoadEnd](api/router/RouteConfigLoadEnd): After a route has been lazy loaded.\n * * [RoutesRecognized](api/router/RoutesRecognized): When the router parses the URL\n * and the routes are recognized.\n * * [GuardsCheckStart](api/router/GuardsCheckStart): When the router begins the *guards*\n * phase of routing.\n * * [ChildActivationStart](api/router/ChildActivationStart): When the router\n * begins activating a route's children.\n * * [ActivationStart](api/router/ActivationStart): When the router begins activating a route.\n * * [GuardsCheckEnd](api/router/GuardsCheckEnd): When the router finishes the *guards*\n * phase of routing successfully.\n * * [ResolveStart](api/router/ResolveStart): When the router begins the *resolve*\n * phase of routing.\n * * [ResolveEnd](api/router/ResolveEnd): When the router finishes the *resolve*\n * phase of routing successfully.\n * * [ChildActivationEnd](api/router/ChildActivationEnd): When the router finishes\n * activating a route's children.\n * * [ActivationEnd](api/router/ActivationEnd): When the router finishes activating a route.\n * * [NavigationEnd](api/router/NavigationEnd): When navigation ends successfully.\n * * [NavigationCancel](api/router/NavigationCancel): When navigation is canceled.\n * * [NavigationError](api/router/NavigationError): When navigation fails\n * due to an unexpected error.\n * * [Scroll](api/router/Scroll): When the user scrolls.\n *\n * @publicApi\n */\nexport type Event =\n | NavigationStart\n | NavigationEnd\n | NavigationCancel\n | NavigationError\n | RoutesRecognized\n | GuardsCheckStart\n | GuardsCheckEnd\n | RouteConfigLoadStart\n | RouteConfigLoadEnd\n | ChildActivationStart\n | ChildActivationEnd\n | ActivationStart\n | ActivationEnd\n | Scroll\n | ResolveStart\n | ResolveEnd\n | NavigationSkipped;\n\nexport function stringifyEvent(routerEvent: Event): string {\n switch (routerEvent.type) {\n case EventType.ActivationEnd:\n return `ActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;\n case EventType.ActivationStart:\n return `ActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;\n case EventType.ChildActivationEnd:\n return `ChildActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;\n case EventType.ChildActivationStart:\n return `ChildActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;\n case EventType.GuardsCheckEnd:\n return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;\n case EventType.GuardsCheckStart:\n return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;\n case EventType.NavigationCancel:\n return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;\n case EventType.NavigationSkipped:\n return `NavigationSkipped(id: ${routerEvent.id}, url: '${routerEvent.url}')`;\n case EventType.NavigationEnd:\n return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;\n case EventType.NavigationError:\n return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;\n case EventType.NavigationStart:\n return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;\n case EventType.ResolveEnd:\n return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;\n case EventType.ResolveStart:\n return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;\n case EventType.RouteConfigLoadEnd:\n return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;\n case EventType.RouteConfigLoadStart:\n return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;\n case EventType.RoutesRecognized:\n return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;\n case EventType.Scroll:\n const pos = routerEvent.position\n ? `${routerEvent.position[0]}, ${routerEvent.position[1]}`\n : null;\n return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n createEnvironmentInjector,\n EnvironmentInjector,\n isStandalone,\n Type,\n ɵisNgModule as isNgModule,\n ɵRuntimeError as RuntimeError,\n} from '@angular/core';\n\nimport {RuntimeErrorCode} from '../errors';\nimport {Route, Routes} from '../models';\nimport {ActivatedRouteSnapshot} from '../router_state';\nimport {PRIMARY_OUTLET} from '../shared';\n\n/**\n * Creates an `EnvironmentInjector` if the `Route` has providers and one does not already exist\n * and returns the injector. Otherwise, if the `Route` does not have `providers`, returns the\n * `currentInjector`.\n *\n * @param route The route that might have providers\n * @param currentInjector The parent injector of the `Route`\n */\nexport function getOrCreateRouteInjectorIfNeeded(\n route: Route,\n currentInjector: EnvironmentInjector,\n) {\n if (route.providers && !route._injector) {\n route._injector = createEnvironmentInjector(\n route.providers,\n currentInjector,\n `Route: ${route.path}`,\n );\n }\n return route._injector ?? currentInjector;\n}\n\nexport function getLoadedRoutes(route: Route): Route[] | undefined {\n return route._loadedRoutes;\n}\n\nexport function getLoadedInjector(route: Route): EnvironmentInjector | undefined {\n return route._loadedInjector;\n}\nexport function getLoadedComponent(route: Route): Type | undefined {\n return route._loadedComponent;\n}\n\nexport function getProvidersInjector(route: Route): EnvironmentInjector | undefined {\n return route._injector;\n}\n\nexport function validateConfig(\n config: Routes,\n parentPath: string = '',\n requireStandaloneComponents = false,\n): void {\n // forEach doesn't iterate undefined values\n for (let i = 0; i < config.length; i++) {\n const route: Route = config[i];\n const fullPath: string = getFullPath(parentPath, route);\n validateNode(route, fullPath, requireStandaloneComponents);\n }\n}\n\nexport function assertStandalone(fullPath: string, component: Type | undefined) {\n if (component && isNgModule(component)) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}'. You are using 'loadComponent' with a module, ` +\n `but it must be used with standalone components. Use 'loadChildren' instead.`,\n );\n } else if (component && !isStandalone(component)) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}'. The component must be standalone.`,\n );\n }\n}\n\nfunction validateNode(route: Route, fullPath: string, requireStandaloneComponents: boolean): void {\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n if (!route) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `\n Invalid configuration of route '${fullPath}': Encountered undefined route.\n The reason might be an extra comma.\n\n Example:\n const routes: Routes = [\n { path: '', redirectTo: '/dashboard', pathMatch: 'full' },\n { path: 'dashboard', component: DashboardComponent },, << two commas\n { path: 'detail/:id', component: HeroDetailComponent }\n ];\n `,\n );\n }\n if (Array.isArray(route)) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': Array cannot be specified`,\n );\n }\n if (\n !route.redirectTo &&\n !route.component &&\n !route.loadComponent &&\n !route.children &&\n !route.loadChildren &&\n route.outlet &&\n route.outlet !== PRIMARY_OUTLET\n ) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`,\n );\n }\n if (route.redirectTo && route.children) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': redirectTo and children cannot be used together`,\n );\n }\n if (route.redirectTo && route.loadChildren) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': redirectTo and loadChildren cannot be used together`,\n );\n }\n if (route.children && route.loadChildren) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': children and loadChildren cannot be used together`,\n );\n }\n if (route.redirectTo && (route.component || route.loadComponent)) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': redirectTo and component/loadComponent cannot be used together`,\n );\n }\n if (route.component && route.loadComponent) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': component and loadComponent cannot be used together`,\n );\n }\n if (route.redirectTo && route.canActivate) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': redirectTo and canActivate cannot be used together. Redirects happen before activation ` +\n `so canActivate will never be executed.`,\n );\n }\n if (route.path && route.matcher) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': path and matcher cannot be used together`,\n );\n }\n if (\n route.redirectTo === void 0 &&\n !route.component &&\n !route.loadComponent &&\n !route.children &&\n !route.loadChildren\n ) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}'. One of the following must be provided: component, loadComponent, redirectTo, children or loadChildren`,\n );\n }\n if (route.path === void 0 && route.matcher === void 0) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': routes must have either a path or a matcher specified`,\n );\n }\n if (typeof route.path === 'string' && route.path.charAt(0) === '/') {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '${fullPath}': path cannot start with a slash`,\n );\n }\n if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) {\n const exp = `The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`;\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_ROUTE_CONFIG,\n `Invalid configuration of route '{path: \"${fullPath}\", redirectTo: \"${route.redirectTo}\"}': please provide 'pathMatch'. ${exp}`,\n );\n }\n if (requireStandaloneComponents) {\n assertStandalone(fullPath, route.component);\n }\n }\n if (route.children) {\n validateConfig(route.children, fullPath, requireStandaloneComponents);\n }\n}\n\nfunction getFullPath(parentPath: string, currentRoute: Route): string {\n if (!currentRoute) {\n return parentPath;\n }\n if (!parentPath && !currentRoute.path) {\n return '';\n } else if (parentPath && !currentRoute.path) {\n return `${parentPath}/`;\n } else if (!parentPath && currentRoute.path) {\n return currentRoute.path;\n } else {\n return `${parentPath}/${currentRoute.path}`;\n }\n}\n\n/** Returns the `route.outlet` or PRIMARY_OUTLET if none exists. */\nexport function getOutlet(route: Route): string {\n return route.outlet || PRIMARY_OUTLET;\n}\n\n/**\n * Sorts the `routes` such that the ones with an outlet matching `outletName` come first.\n * The order of the configs is otherwise preserved.\n */\nexport function sortByMatchingOutlets(routes: Routes, outletName: string): Routes {\n const sortedConfig = routes.filter((r) => getOutlet(r) === outletName);\n sortedConfig.push(...routes.filter((r) => getOutlet(r) !== outletName));\n return sortedConfig;\n}\n\n/**\n * Gets the first injector in the snapshot's parent tree.\n *\n * If the `Route` has a static list of providers, the returned injector will be the one created from\n * those. If it does not exist, the returned injector may come from the parents, which may be from a\n * loaded config or their static providers.\n *\n * Returns `null` if there is neither this nor any parents have a stored injector.\n *\n * Generally used for retrieving the injector to use for getting tokens for guards/resolvers and\n * also used for getting the correct injector to use for creating components.\n */\nexport function getClosestRouteInjector(\n snapshot: ActivatedRouteSnapshot | undefined,\n): EnvironmentInjector | null {\n if (!snapshot) return null;\n\n // If the current route has its own injector, which is created from the static providers on the\n // route itself, we should use that. Otherwise, we start at the parent since we do not want to\n // include the lazy loaded injector from this route.\n if (snapshot.routeConfig?._injector) {\n return snapshot.routeConfig._injector;\n }\n\n for (let s = snapshot.parent; s; s = s.parent) {\n const route = s.routeConfig;\n // Note that the order here is important. `_loadedInjector` stored on the route with\n // `loadChildren: () => NgModule` so it applies to child routes with priority. The `_injector`\n // is created from the static providers on that parent route, so it applies to the children as\n // well, but only if there is no lazy loaded NgModuleRef injector.\n if (route?._loadedInjector) return route._loadedInjector;\n if (route?._injector) return route._injector;\n }\n\n return null;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {ComponentRef, EnvironmentInjector, Injectable} from '@angular/core';\n\nimport {RouterOutletContract} from './directives/router_outlet';\nimport {ActivatedRoute} from './router_state';\nimport {getClosestRouteInjector} from './utils/config';\n\n/**\n * Store contextual information about a `RouterOutlet`\n *\n * @publicApi\n */\nexport class OutletContext {\n outlet: RouterOutletContract | null = null;\n route: ActivatedRoute | null = null;\n children = new ChildrenOutletContexts(this.rootInjector);\n attachRef: ComponentRef | null = null;\n get injector(): EnvironmentInjector {\n return getClosestRouteInjector(this.route?.snapshot) ?? this.rootInjector;\n }\n // TODO(atscott): Only here to avoid a \"breaking\" change in a patch/minor. Remove in v19.\n set injector(_: EnvironmentInjector) {}\n\n constructor(private readonly rootInjector: EnvironmentInjector) {}\n}\n\n/**\n * Store contextual information about the children (= nested) `RouterOutlet`\n *\n * @publicApi\n */\n@Injectable({providedIn: 'root'})\nexport class ChildrenOutletContexts {\n // contexts for child outlets, by name.\n private contexts = new Map();\n\n /** @nodoc */\n constructor(private rootInjector: EnvironmentInjector) {}\n\n /** Called when a `RouterOutlet` directive is instantiated */\n onChildOutletCreated(childName: string, outlet: RouterOutletContract): void {\n const context = this.getOrCreateContext(childName);\n context.outlet = outlet;\n this.contexts.set(childName, context);\n }\n\n /**\n * Called when a `RouterOutlet` directive is destroyed.\n * We need to keep the context as the outlet could be destroyed inside a NgIf and might be\n * re-created later.\n */\n onChildOutletDestroyed(childName: string): void {\n const context = this.getContext(childName);\n if (context) {\n context.outlet = null;\n context.attachRef = null;\n }\n }\n\n /**\n * Called when the corresponding route is deactivated during navigation.\n * Because the component get destroyed, all children outlet are destroyed.\n */\n onOutletDeactivated(): Map {\n const contexts = this.contexts;\n this.contexts = new Map();\n return contexts;\n }\n\n onOutletReAttached(contexts: Map) {\n this.contexts = contexts;\n }\n\n getOrCreateContext(childName: string): OutletContext {\n let context = this.getContext(childName);\n\n if (!context) {\n context = new OutletContext(this.rootInjector);\n this.contexts.set(childName, context);\n }\n\n return context;\n }\n\n getContext(childName: string): OutletContext | null {\n return this.contexts.get(childName) || null;\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nexport class Tree {\n /** @internal */\n _root: TreeNode;\n\n constructor(root: TreeNode) {\n this._root = root;\n }\n\n get root(): T {\n return this._root.value;\n }\n\n /**\n * @internal\n */\n parent(t: T): T | null {\n const p = this.pathFromRoot(t);\n return p.length > 1 ? p[p.length - 2] : null;\n }\n\n /**\n * @internal\n */\n children(t: T): T[] {\n const n = findNode(t, this._root);\n return n ? n.children.map((t) => t.value) : [];\n }\n\n /**\n * @internal\n */\n firstChild(t: T): T | null {\n const n = findNode(t, this._root);\n return n && n.children.length > 0 ? n.children[0].value : null;\n }\n\n /**\n * @internal\n */\n siblings(t: T): T[] {\n const p = findPath(t, this._root);\n if (p.length < 2) return [];\n\n const c = p[p.length - 2].children.map((c) => c.value);\n return c.filter((cc) => cc !== t);\n }\n\n /**\n * @internal\n */\n pathFromRoot(t: T): T[] {\n return findPath(t, this._root).map((s) => s.value);\n }\n}\n\n// DFS for the node matching the value\nfunction findNode(value: T, node: TreeNode): TreeNode | null {\n if (value === node.value) return node;\n\n for (const child of node.children) {\n const node = findNode(value, child);\n if (node) return node;\n }\n\n return null;\n}\n\n// Return the path to the node with the given value using DFS\nfunction findPath(value: T, node: TreeNode): TreeNode[] {\n if (value === node.value) return [node];\n\n for (const child of node.children) {\n const path = findPath(value, child);\n if (path.length) {\n path.unshift(node);\n return path;\n }\n }\n\n return [];\n}\n\nexport class TreeNode {\n constructor(\n public value: T,\n public children: TreeNode[],\n ) {}\n\n toString(): string {\n return `TreeNode(${this.value})`;\n }\n}\n\n// Return the list of T indexed by outlet name\nexport function nodeChildrenAsMap(node: TreeNode | null) {\n const map: {[outlet: string]: TreeNode} = {};\n\n if (node) {\n node.children.forEach((child) => (map[child.value.outlet] = child));\n }\n\n return map;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Type} from '@angular/core';\nimport {BehaviorSubject, Observable, of} from 'rxjs';\nimport {map} from 'rxjs/operators';\n\nimport {Data, ResolveData, Route} from './models';\nimport {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET, RouteTitleKey} from './shared';\nimport {equalSegments, UrlSegment} from './url_tree';\nimport {shallowEqual, shallowEqualArrays} from './utils/collection';\nimport {Tree, TreeNode} from './utils/tree';\n\n/**\n * Represents the state of the router as a tree of activated routes.\n *\n * @usageNotes\n *\n * Every node in the route tree is an `ActivatedRoute` instance\n * that knows about the \"consumed\" URL segments, the extracted parameters,\n * and the resolved data.\n * Use the `ActivatedRoute` properties to traverse the tree from any node.\n *\n * The following fragment shows how a component gets the root node\n * of the current state to establish its own route tree:\n *\n * ```\n * @Component({templateUrl:'template.html'})\n * class MyComponent {\n * constructor(router: Router) {\n * const state: RouterState = router.routerState;\n * const root: ActivatedRoute = state.root;\n * const child = root.firstChild;\n * const id: Observable = child.params.map(p => p.id);\n * //...\n * }\n * }\n * ```\n *\n * @see {@link ActivatedRoute}\n * @see [Getting route information](guide/routing/common-router-tasks#getting-route-information)\n *\n * @publicApi\n */\nexport class RouterState extends Tree {\n /** @internal */\n constructor(\n root: TreeNode,\n /** The current snapshot of the router state */\n public snapshot: RouterStateSnapshot,\n ) {\n super(root);\n setRouterState(this, root);\n }\n\n override toString(): string {\n return this.snapshot.toString();\n }\n}\n\nexport function createEmptyState(rootComponent: Type | null): RouterState {\n const snapshot = createEmptyStateSnapshot(rootComponent);\n const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);\n const emptyParams = new BehaviorSubject({});\n const emptyData = new BehaviorSubject({});\n const emptyQueryParams = new BehaviorSubject({});\n const fragment = new BehaviorSubject('');\n const activated = new ActivatedRoute(\n emptyUrl,\n emptyParams,\n emptyQueryParams,\n fragment,\n emptyData,\n PRIMARY_OUTLET,\n rootComponent,\n snapshot.root,\n );\n activated.snapshot = snapshot.root;\n return new RouterState(new TreeNode(activated, []), snapshot);\n}\n\nexport function createEmptyStateSnapshot(rootComponent: Type | null): RouterStateSnapshot {\n const emptyParams = {};\n const emptyData = {};\n const emptyQueryParams = {};\n const fragment = '';\n const activated = new ActivatedRouteSnapshot(\n [],\n emptyParams,\n emptyQueryParams,\n fragment,\n emptyData,\n PRIMARY_OUTLET,\n rootComponent,\n null,\n {},\n );\n return new RouterStateSnapshot('', new TreeNode(activated, []));\n}\n\n/**\n * Provides access to information about a route associated with a component\n * that is loaded in an outlet.\n * Use to traverse the `RouterState` tree and extract information from nodes.\n *\n * The following example shows how to construct a component using information from a\n * currently activated route.\n *\n * Note: the observables in this class only emit when the current and previous values differ based\n * on shallow equality. For example, changing deeply nested properties in resolved `data` will not\n * cause the `ActivatedRoute.data` `Observable` to emit a new value.\n *\n * {@example router/activated-route/module.ts region=\"activated-route\"\n * header=\"activated-route.component.ts\"}\n *\n * @see [Getting route information](guide/routing/common-router-tasks#getting-route-information)\n *\n * @publicApi\n */\nexport class ActivatedRoute {\n /** The current snapshot of this route */\n snapshot!: ActivatedRouteSnapshot;\n /** @internal */\n _futureSnapshot: ActivatedRouteSnapshot;\n /** @internal */\n _routerState!: RouterState;\n /** @internal */\n _paramMap?: Observable;\n /** @internal */\n _queryParamMap?: Observable;\n\n /** An Observable of the resolved route title */\n readonly title: Observable;\n\n /** An observable of the URL segments matched by this route. */\n public url: Observable;\n /** An observable of the matrix parameters scoped to this route. */\n public params: Observable;\n /** An observable of the query parameters shared by all the routes. */\n public queryParams: Observable;\n /** An observable of the URL fragment shared by all the routes. */\n public fragment: Observable;\n /** An observable of the static and resolved data of this route. */\n public data: Observable;\n\n /** @internal */\n constructor(\n /** @internal */\n public urlSubject: BehaviorSubject,\n /** @internal */\n public paramsSubject: BehaviorSubject,\n /** @internal */\n public queryParamsSubject: BehaviorSubject,\n /** @internal */\n public fragmentSubject: BehaviorSubject,\n /** @internal */\n public dataSubject: BehaviorSubject,\n /** The outlet name of the route, a constant. */\n public outlet: string,\n /** The component of the route, a constant. */\n public component: Type | null,\n futureSnapshot: ActivatedRouteSnapshot,\n ) {\n this._futureSnapshot = futureSnapshot;\n this.title = this.dataSubject?.pipe(map((d: Data) => d[RouteTitleKey])) ?? of(undefined);\n // TODO(atscott): Verify that these can be changed to `.asObservable()` with TGP.\n this.url = urlSubject;\n this.params = paramsSubject;\n this.queryParams = queryParamsSubject;\n this.fragment = fragmentSubject;\n this.data = dataSubject;\n }\n\n /** The configuration used to match this route. */\n get routeConfig(): Route | null {\n return this._futureSnapshot.routeConfig;\n }\n\n /** The root of the router state. */\n get root(): ActivatedRoute {\n return this._routerState.root;\n }\n\n /** The parent of this route in the router state tree. */\n get parent(): ActivatedRoute | null {\n return this._routerState.parent(this);\n }\n\n /** The first child of this route in the router state tree. */\n get firstChild(): ActivatedRoute | null {\n return this._routerState.firstChild(this);\n }\n\n /** The children of this route in the router state tree. */\n get children(): ActivatedRoute[] {\n return this._routerState.children(this);\n }\n\n /** The path from the root of the router state tree to this route. */\n get pathFromRoot(): ActivatedRoute[] {\n return this._routerState.pathFromRoot(this);\n }\n\n /**\n * An Observable that contains a map of the required and optional parameters\n * specific to the route.\n * The map supports retrieving single and multiple values from the same parameter.\n */\n get paramMap(): Observable {\n this._paramMap ??= this.params.pipe(map((p: Params): ParamMap => convertToParamMap(p)));\n return this._paramMap;\n }\n\n /**\n * An Observable that contains a map of the query parameters available to all routes.\n * The map supports retrieving single and multiple values from the query parameter.\n */\n get queryParamMap(): Observable {\n this._queryParamMap ??= this.queryParams.pipe(\n map((p: Params): ParamMap => convertToParamMap(p)),\n );\n return this._queryParamMap;\n }\n\n toString(): string {\n return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;\n }\n}\n\nexport type ParamsInheritanceStrategy = 'emptyOnly' | 'always';\n\n/** @internal */\nexport type Inherited = {\n params: Params;\n data: Data;\n resolve: Data;\n};\n\n/**\n * Returns the inherited params, data, and resolve for a given route.\n *\n * By default, we do not inherit parent data unless the current route is path-less or the parent\n * route is component-less.\n */\nexport function getInherited(\n route: ActivatedRouteSnapshot,\n parent: ActivatedRouteSnapshot | null,\n paramsInheritanceStrategy: ParamsInheritanceStrategy = 'emptyOnly',\n): Inherited {\n let inherited: Inherited;\n const {routeConfig} = route;\n if (\n parent !== null &&\n (paramsInheritanceStrategy === 'always' ||\n // inherit parent data if route is empty path\n routeConfig?.path === '' ||\n // inherit parent data if parent was componentless\n (!parent.component && !parent.routeConfig?.loadComponent))\n ) {\n inherited = {\n params: {...parent.params, ...route.params},\n data: {...parent.data, ...route.data},\n resolve: {\n // Snapshots are created with data inherited from parent and guards (i.e. canActivate) can\n // change data because it's not frozen...\n // This first line could be deleted chose to break/disallow mutating the `data` object in\n // guards.\n // Note that data from parents still override this mutated data so anyone relying on this\n // might be surprised that it doesn't work if parent data is inherited but otherwise does.\n ...route.data,\n // Ensure inherited resolved data overrides inherited static data\n ...parent.data,\n // static data from the current route overrides any inherited data\n ...routeConfig?.data,\n // resolved data from current route overrides everything\n ...route._resolvedData,\n },\n };\n } else {\n inherited = {\n params: {...route.params},\n data: {...route.data},\n resolve: {...route.data, ...(route._resolvedData ?? {})},\n };\n }\n\n if (routeConfig && hasStaticTitle(routeConfig)) {\n inherited.resolve[RouteTitleKey] = routeConfig.title;\n }\n return inherited;\n}\n\n/**\n * @description\n *\n * Contains the information about a route associated with a component loaded in an\n * outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to\n * traverse the router state tree.\n *\n * The following example initializes a component with route information extracted\n * from the snapshot of the root node at the time of creation.\n *\n * ```\n * @Component({templateUrl:'./my-component.html'})\n * class MyComponent {\n * constructor(route: ActivatedRoute) {\n * const id: string = route.snapshot.params.id;\n * const url: string = route.snapshot.url.join('');\n * const user = route.snapshot.data.user;\n * }\n * }\n * ```\n *\n * @publicApi\n */\nexport class ActivatedRouteSnapshot {\n /** The configuration used to match this route **/\n public readonly routeConfig: Route | null;\n /** @internal */\n _resolve: ResolveData;\n /** @internal */\n _resolvedData?: Data;\n /** @internal */\n _routerState!: RouterStateSnapshot;\n /** @internal */\n _paramMap?: ParamMap;\n /** @internal */\n _queryParamMap?: ParamMap;\n\n /** The resolved route title */\n get title(): string | undefined {\n // Note: This _must_ be a getter because the data is mutated in the resolvers. Title will not be\n // available at the time of class instantiation.\n return this.data?.[RouteTitleKey];\n }\n\n /** @internal */\n constructor(\n /** The URL segments matched by this route */\n public url: UrlSegment[],\n /**\n * The matrix parameters scoped to this route.\n *\n * You can compute all params (or data) in the router state or to get params outside\n * of an activated component by traversing the `RouterState` tree as in the following\n * example:\n * ```\n * collectRouteParams(router: Router) {\n * let params = {};\n * let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];\n * while (stack.length > 0) {\n * const route = stack.pop()!;\n * params = {...params, ...route.params};\n * stack.push(...route.children);\n * }\n * return params;\n * }\n * ```\n */\n public params: Params,\n /** The query parameters shared by all the routes */\n public queryParams: Params,\n /** The URL fragment shared by all the routes */\n public fragment: string | null,\n /** The static and resolved data of this route */\n public data: Data,\n /** The outlet name of the route */\n public outlet: string,\n /** The component of the route */\n public component: Type | null,\n routeConfig: Route | null,\n resolve: ResolveData,\n ) {\n this.routeConfig = routeConfig;\n this._resolve = resolve;\n }\n\n /** The root of the router state */\n get root(): ActivatedRouteSnapshot {\n return this._routerState.root;\n }\n\n /** The parent of this route in the router state tree */\n get parent(): ActivatedRouteSnapshot | null {\n return this._routerState.parent(this);\n }\n\n /** The first child of this route in the router state tree */\n get firstChild(): ActivatedRouteSnapshot | null {\n return this._routerState.firstChild(this);\n }\n\n /** The children of this route in the router state tree */\n get children(): ActivatedRouteSnapshot[] {\n return this._routerState.children(this);\n }\n\n /** The path from the root of the router state tree to this route */\n get pathFromRoot(): ActivatedRouteSnapshot[] {\n return this._routerState.pathFromRoot(this);\n }\n\n get paramMap(): ParamMap {\n this._paramMap ??= convertToParamMap(this.params);\n return this._paramMap;\n }\n\n get queryParamMap(): ParamMap {\n this._queryParamMap ??= convertToParamMap(this.queryParams);\n return this._queryParamMap;\n }\n\n toString(): string {\n const url = this.url.map((segment) => segment.toString()).join('/');\n const matched = this.routeConfig ? this.routeConfig.path : '';\n return `Route(url:'${url}', path:'${matched}')`;\n }\n}\n\n/**\n * @description\n *\n * Represents the state of the router at a moment in time.\n *\n * This is a tree of activated route snapshots. Every node in this tree knows about\n * the \"consumed\" URL segments, the extracted parameters, and the resolved data.\n *\n * The following example shows how a component is initialized with information\n * from the snapshot of the root node's state at the time of creation.\n *\n * ```\n * @Component({templateUrl:'template.html'})\n * class MyComponent {\n * constructor(router: Router) {\n * const state: RouterState = router.routerState;\n * const snapshot: RouterStateSnapshot = state.snapshot;\n * const root: ActivatedRouteSnapshot = snapshot.root;\n * const child = root.firstChild;\n * const id: Observable = child.params.map(p => p.id);\n * //...\n * }\n * }\n * ```\n *\n * @publicApi\n */\nexport class RouterStateSnapshot extends Tree {\n /** @internal */\n constructor(\n /** The url from which this snapshot was created */\n public url: string,\n root: TreeNode,\n ) {\n super(root);\n setRouterState(this, root);\n }\n\n override toString(): string {\n return serializeNode(this._root);\n }\n}\n\nfunction setRouterState(state: U, node: TreeNode): void {\n node.value._routerState = state;\n node.children.forEach((c) => setRouterState(state, c));\n}\n\nfunction serializeNode(node: TreeNode): string {\n const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : '';\n return `${node.value}${c}`;\n}\n\n/**\n * The expectation is that the activate route is created with the right set of parameters.\n * So we push new values into the observables only when they are not the initial values.\n * And we detect that by checking if the snapshot field is set.\n */\nexport function advanceActivatedRoute(route: ActivatedRoute): void {\n if (route.snapshot) {\n const currentSnapshot = route.snapshot;\n const nextSnapshot = route._futureSnapshot;\n route.snapshot = nextSnapshot;\n if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {\n route.queryParamsSubject.next(nextSnapshot.queryParams);\n }\n if (currentSnapshot.fragment !== nextSnapshot.fragment) {\n route.fragmentSubject.next(nextSnapshot.fragment);\n }\n if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {\n route.paramsSubject.next(nextSnapshot.params);\n }\n if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {\n route.urlSubject.next(nextSnapshot.url);\n }\n if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {\n route.dataSubject.next(nextSnapshot.data);\n }\n } else {\n route.snapshot = route._futureSnapshot;\n\n // this is for resolved data\n route.dataSubject.next(route._futureSnapshot.data);\n }\n}\n\nexport function equalParamsAndUrlSegments(\n a: ActivatedRouteSnapshot,\n b: ActivatedRouteSnapshot,\n): boolean {\n const equalUrlParams = shallowEqual(a.params, b.params) && equalSegments(a.url, b.url);\n const parentsMismatch = !a.parent !== !b.parent;\n\n return (\n equalUrlParams &&\n !parentsMismatch &&\n (!a.parent || equalParamsAndUrlSegments(a.parent, b.parent!))\n );\n}\n\nexport function hasStaticTitle(config: Route) {\n return typeof config.title === 'string' || config.title === null;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n ChangeDetectorRef,\n ComponentRef,\n Directive,\n EnvironmentInjector,\n EventEmitter,\n inject,\n Injectable,\n InjectionToken,\n Injector,\n Input,\n OnDestroy,\n OnInit,\n Output,\n reflectComponentType,\n SimpleChanges,\n ViewContainerRef,\n ɵRuntimeError as RuntimeError,\n} from '@angular/core';\nimport {combineLatest, of, Subscription} from 'rxjs';\nimport {switchMap} from 'rxjs/operators';\n\nimport {RuntimeErrorCode} from '../errors';\nimport {Data} from '../models';\nimport {ChildrenOutletContexts} from '../router_outlet_context';\nimport {ActivatedRoute} from '../router_state';\nimport {PRIMARY_OUTLET} from '../shared';\n\n/**\n * An interface that defines the contract for developing a component outlet for the `Router`.\n *\n * An outlet acts as a placeholder that Angular dynamically fills based on the current router state.\n *\n * A router outlet should register itself with the `Router` via\n * `ChildrenOutletContexts#onChildOutletCreated` and unregister with\n * `ChildrenOutletContexts#onChildOutletDestroyed`. When the `Router` identifies a matched `Route`,\n * it looks for a registered outlet in the `ChildrenOutletContexts` and activates it.\n *\n * @see {@link ChildrenOutletContexts}\n * @publicApi\n */\nexport interface RouterOutletContract {\n /**\n * Whether the given outlet is activated.\n *\n * An outlet is considered \"activated\" if it has an active component.\n */\n isActivated: boolean;\n\n /** The instance of the activated component or `null` if the outlet is not activated. */\n component: Object | null;\n\n /**\n * The `Data` of the `ActivatedRoute` snapshot.\n */\n activatedRouteData: Data;\n\n /**\n * The `ActivatedRoute` for the outlet or `null` if the outlet is not activated.\n */\n activatedRoute: ActivatedRoute | null;\n\n /**\n * Called by the `Router` when the outlet should activate (create a component).\n */\n activateWith(activatedRoute: ActivatedRoute, environmentInjector: EnvironmentInjector): void;\n\n /**\n * A request to destroy the currently activated component.\n *\n * When a `RouteReuseStrategy` indicates that an `ActivatedRoute` should be removed but stored for\n * later re-use rather than destroyed, the `Router` will call `detach` instead.\n */\n deactivate(): void;\n\n /**\n * Called when the `RouteReuseStrategy` instructs to detach the subtree.\n *\n * This is similar to `deactivate`, but the activated component should _not_ be destroyed.\n * Instead, it is returned so that it can be reattached later via the `attach` method.\n */\n detach(): ComponentRef;\n\n /**\n * Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree.\n */\n attach(ref: ComponentRef, activatedRoute: ActivatedRoute): void;\n\n /**\n * Emits an activate event when a new component is instantiated\n **/\n activateEvents?: EventEmitter;\n\n /**\n * Emits a deactivate event when a component is destroyed.\n */\n deactivateEvents?: EventEmitter;\n\n /**\n * Emits an attached component instance when the `RouteReuseStrategy` instructs to re-attach a\n * previously detached subtree.\n **/\n attachEvents?: EventEmitter;\n\n /**\n * Emits a detached component instance when the `RouteReuseStrategy` instructs to detach the\n * subtree.\n */\n detachEvents?: EventEmitter;\n\n /**\n * Used to indicate that the outlet is able to bind data from the `Router` to the outlet\n * component's inputs.\n *\n * When this is `undefined` or `false` and the developer has opted in to the\n * feature using `withComponentInputBinding`, a warning will be logged in dev mode if this outlet\n * is used in the application.\n */\n readonly supportsBindingToComponentInputs?: true;\n}\n\n/**\n * @description\n *\n * Acts as a placeholder that Angular dynamically fills based on the current router state.\n *\n * Each outlet can have a unique name, determined by the optional `name` attribute.\n * The name cannot be set or changed dynamically. If not set, default value is \"primary\".\n *\n * ```\n * \n * \n * \n * ```\n *\n * Named outlets can be the targets of secondary routes.\n * The `Route` object for a secondary route has an `outlet` property to identify the target outlet:\n *\n * `{path: , component: , outlet: }`\n *\n * Using named outlets and secondary routes, you can target multiple outlets in\n * the same `RouterLink` directive.\n *\n * The router keeps track of separate branches in a navigation tree for each named outlet and\n * generates a representation of that tree in the URL.\n * The URL for a secondary route uses the following syntax to specify both the primary and secondary\n * routes at the same time:\n *\n * `http://base-path/primary-route-path(outlet-name:route-path)`\n *\n * A router outlet emits an activate event when a new component is instantiated,\n * deactivate event when a component is destroyed.\n * An attached event emits when the `RouteReuseStrategy` instructs the outlet to reattach the\n * subtree, and the detached event emits when the `RouteReuseStrategy` instructs the outlet to\n * detach the subtree.\n *\n * ```\n * \n * ```\n *\n * @see {@link RouterLink}\n * @see {@link Route}\n * @ngModule RouterModule\n *\n * @publicApi\n */\n@Directive({\n selector: 'router-outlet',\n exportAs: 'outlet',\n standalone: true,\n})\nexport class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract {\n private activated: ComponentRef | null = null;\n /** @internal */\n get activatedComponentRef(): ComponentRef | null {\n return this.activated;\n }\n private _activatedRoute: ActivatedRoute | null = null;\n /**\n * The name of the outlet\n *\n */\n @Input() name = PRIMARY_OUTLET;\n\n @Output('activate') activateEvents = new EventEmitter();\n @Output('deactivate') deactivateEvents = new EventEmitter();\n /**\n * Emits an attached component instance when the `RouteReuseStrategy` instructs to re-attach a\n * previously detached subtree.\n **/\n @Output('attach') attachEvents = new EventEmitter();\n /**\n * Emits a detached component instance when the `RouteReuseStrategy` instructs to detach the\n * subtree.\n */\n @Output('detach') detachEvents = new EventEmitter();\n\n private parentContexts = inject(ChildrenOutletContexts);\n private location = inject(ViewContainerRef);\n private changeDetector = inject(ChangeDetectorRef);\n private inputBinder = inject(INPUT_BINDER, {optional: true});\n /** @nodoc */\n readonly supportsBindingToComponentInputs = true;\n\n /** @nodoc */\n ngOnChanges(changes: SimpleChanges) {\n if (changes['name']) {\n const {firstChange, previousValue} = changes['name'];\n if (firstChange) {\n // The first change is handled by ngOnInit. Because ngOnChanges doesn't get called when no\n // input is set at all, we need to centrally handle the first change there.\n return;\n }\n\n // unregister with the old name\n if (this.isTrackedInParentContexts(previousValue)) {\n this.deactivate();\n this.parentContexts.onChildOutletDestroyed(previousValue);\n }\n // register the new name\n this.initializeOutletWithName();\n }\n }\n\n /** @nodoc */\n ngOnDestroy(): void {\n // Ensure that the registered outlet is this one before removing it on the context.\n if (this.isTrackedInParentContexts(this.name)) {\n this.parentContexts.onChildOutletDestroyed(this.name);\n }\n this.inputBinder?.unsubscribeFromRouteData(this);\n }\n\n private isTrackedInParentContexts(outletName: string) {\n return this.parentContexts.getContext(outletName)?.outlet === this;\n }\n\n /** @nodoc */\n ngOnInit(): void {\n this.initializeOutletWithName();\n }\n\n private initializeOutletWithName() {\n this.parentContexts.onChildOutletCreated(this.name, this);\n if (this.activated) {\n return;\n }\n\n // If the outlet was not instantiated at the time the route got activated we need to populate\n // the outlet when it is initialized (ie inside a NgIf)\n const context = this.parentContexts.getContext(this.name);\n if (context?.route) {\n if (context.attachRef) {\n // `attachRef` is populated when there is an existing component to mount\n this.attach(context.attachRef, context.route);\n } else {\n // otherwise the component defined in the configuration is created\n this.activateWith(context.route, context.injector);\n }\n }\n }\n\n get isActivated(): boolean {\n return !!this.activated;\n }\n\n /**\n * @returns The currently activated component instance.\n * @throws An error if the outlet is not activated.\n */\n get component(): Object {\n if (!this.activated)\n throw new RuntimeError(\n RuntimeErrorCode.OUTLET_NOT_ACTIVATED,\n (typeof ngDevMode === 'undefined' || ngDevMode) && 'Outlet is not activated',\n );\n return this.activated.instance;\n }\n\n get activatedRoute(): ActivatedRoute {\n if (!this.activated)\n throw new RuntimeError(\n RuntimeErrorCode.OUTLET_NOT_ACTIVATED,\n (typeof ngDevMode === 'undefined' || ngDevMode) && 'Outlet is not activated',\n );\n return this._activatedRoute as ActivatedRoute;\n }\n\n get activatedRouteData(): Data {\n if (this._activatedRoute) {\n return this._activatedRoute.snapshot.data;\n }\n return {};\n }\n\n /**\n * Called when the `RouteReuseStrategy` instructs to detach the subtree\n */\n detach(): ComponentRef {\n if (!this.activated)\n throw new RuntimeError(\n RuntimeErrorCode.OUTLET_NOT_ACTIVATED,\n (typeof ngDevMode === 'undefined' || ngDevMode) && 'Outlet is not activated',\n );\n this.location.detach();\n const cmp = this.activated;\n this.activated = null;\n this._activatedRoute = null;\n this.detachEvents.emit(cmp.instance);\n return cmp;\n }\n\n /**\n * Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree\n */\n attach(ref: ComponentRef, activatedRoute: ActivatedRoute) {\n this.activated = ref;\n this._activatedRoute = activatedRoute;\n this.location.insert(ref.hostView);\n this.inputBinder?.bindActivatedRouteToOutletComponent(this);\n this.attachEvents.emit(ref.instance);\n }\n\n deactivate(): void {\n if (this.activated) {\n const c = this.component;\n this.activated.destroy();\n this.activated = null;\n this._activatedRoute = null;\n this.deactivateEvents.emit(c);\n }\n }\n\n activateWith(activatedRoute: ActivatedRoute, environmentInjector: EnvironmentInjector) {\n if (this.isActivated) {\n throw new RuntimeError(\n RuntimeErrorCode.OUTLET_ALREADY_ACTIVATED,\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n 'Cannot activate an already activated outlet',\n );\n }\n this._activatedRoute = activatedRoute;\n const location = this.location;\n const snapshot = activatedRoute.snapshot;\n const component = snapshot.component!;\n const childContexts = this.parentContexts.getOrCreateContext(this.name).children;\n const injector = new OutletInjector(activatedRoute, childContexts, location.injector);\n\n this.activated = location.createComponent(component, {\n index: location.length,\n injector,\n environmentInjector: environmentInjector,\n });\n // Calling `markForCheck` to make sure we will run the change detection when the\n // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.\n this.changeDetector.markForCheck();\n this.inputBinder?.bindActivatedRouteToOutletComponent(this);\n this.activateEvents.emit(this.activated.instance);\n }\n}\n\nclass OutletInjector implements Injector {\n /**\n * This injector has a special handing for the `ActivatedRoute` and\n * `ChildrenOutletContexts` tokens: it returns corresponding values for those\n * tokens dynamically. This behavior is different from the regular injector logic,\n * when we initialize and store a value, which is later returned for all inject\n * requests.\n *\n * In some cases (e.g. when using `@defer`), this dynamic behavior requires special\n * handling. This function allows to identify an instance of the `OutletInjector` and\n * create an instance of it without referring to the class itself (so this logic can\n * be invoked from the `core` package). This helps to retain dynamic behavior for the\n * mentioned tokens.\n *\n * Note: it's a temporary solution and we should explore how to support this case better.\n */\n private __ngOutletInjector(parentInjector: Injector) {\n return new OutletInjector(this.route, this.childContexts, parentInjector);\n }\n\n constructor(\n private route: ActivatedRoute,\n private childContexts: ChildrenOutletContexts,\n private parent: Injector,\n ) {}\n\n get(token: any, notFoundValue?: any): any {\n if (token === ActivatedRoute) {\n return this.route;\n }\n\n if (token === ChildrenOutletContexts) {\n return this.childContexts;\n }\n\n return this.parent.get(token, notFoundValue);\n }\n}\n\nexport const INPUT_BINDER = new InjectionToken('');\n\n/**\n * Injectable used as a tree-shakable provider for opting in to binding router data to component\n * inputs.\n *\n * The RouterOutlet registers itself with this service when an `ActivatedRoute` is attached or\n * activated. When this happens, the service subscribes to the `ActivatedRoute` observables (params,\n * queryParams, data) and sets the inputs of the component using `ComponentRef.setInput`.\n * Importantly, when an input does not have an item in the route data with a matching key, this\n * input is set to `undefined`. If it were not done this way, the previous information would be\n * retained if the data got removed from the route (i.e. if a query parameter is removed).\n *\n * The `RouterOutlet` should unregister itself when destroyed via `unsubscribeFromRouteData` so that\n * the subscriptions are cleaned up.\n */\n@Injectable()\nexport class RoutedComponentInputBinder {\n private outletDataSubscriptions = new Map();\n\n bindActivatedRouteToOutletComponent(outlet: RouterOutlet) {\n this.unsubscribeFromRouteData(outlet);\n this.subscribeToRouteData(outlet);\n }\n\n unsubscribeFromRouteData(outlet: RouterOutlet) {\n this.outletDataSubscriptions.get(outlet)?.unsubscribe();\n this.outletDataSubscriptions.delete(outlet);\n }\n\n private subscribeToRouteData(outlet: RouterOutlet) {\n const {activatedRoute} = outlet;\n const dataSubscription = combineLatest([\n activatedRoute.queryParams,\n activatedRoute.params,\n activatedRoute.data,\n ])\n .pipe(\n switchMap(([queryParams, params, data], index) => {\n data = {...queryParams, ...params, ...data};\n // Get the first result from the data subscription synchronously so it's available to\n // the component as soon as possible (and doesn't require a second change detection).\n if (index === 0) {\n return of(data);\n }\n // Promise.resolve is used to avoid synchronously writing the wrong data when\n // two of the Observables in the `combineLatest` stream emit one after\n // another.\n return Promise.resolve(data);\n }),\n )\n .subscribe((data) => {\n // Outlet may have been deactivated or changed names to be associated with a different\n // route\n if (\n !outlet.isActivated ||\n !outlet.activatedComponentRef ||\n outlet.activatedRoute !== activatedRoute ||\n activatedRoute.component === null\n ) {\n this.unsubscribeFromRouteData(outlet);\n return;\n }\n\n const mirror = reflectComponentType(activatedRoute.component);\n if (!mirror) {\n this.unsubscribeFromRouteData(outlet);\n return;\n }\n\n for (const {templateName} of mirror.inputs) {\n outlet.activatedComponentRef.setInput(templateName, data[templateName]);\n }\n });\n\n this.outletDataSubscriptions.set(outlet, dataSubscription);\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {BehaviorSubject} from 'rxjs';\n\nimport {DetachedRouteHandleInternal, RouteReuseStrategy} from './route_reuse_strategy';\nimport {\n ActivatedRoute,\n ActivatedRouteSnapshot,\n RouterState,\n RouterStateSnapshot,\n} from './router_state';\nimport {TreeNode} from './utils/tree';\n\nexport function createRouterState(\n routeReuseStrategy: RouteReuseStrategy,\n curr: RouterStateSnapshot,\n prevState: RouterState,\n): RouterState {\n const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);\n return new RouterState(root, curr);\n}\n\nfunction createNode(\n routeReuseStrategy: RouteReuseStrategy,\n curr: TreeNode,\n prevState?: TreeNode,\n): TreeNode {\n // reuse an activated route that is currently displayed on the screen\n if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {\n const value = prevState.value;\n value._futureSnapshot = curr.value;\n const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);\n return new TreeNode(value, children);\n } else {\n if (routeReuseStrategy.shouldAttach(curr.value)) {\n // retrieve an activated route that is used to be displayed, but is not currently displayed\n const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);\n if (detachedRouteHandle !== null) {\n const tree = (detachedRouteHandle as DetachedRouteHandleInternal).route;\n tree.value._futureSnapshot = curr.value;\n tree.children = curr.children.map((c) => createNode(routeReuseStrategy, c));\n return tree;\n }\n }\n\n const value = createActivatedRoute(curr.value);\n const children = curr.children.map((c) => createNode(routeReuseStrategy, c));\n return new TreeNode(value, children);\n }\n}\n\nfunction createOrReuseChildren(\n routeReuseStrategy: RouteReuseStrategy,\n curr: TreeNode,\n prevState: TreeNode,\n) {\n return curr.children.map((child) => {\n for (const p of prevState.children) {\n if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) {\n return createNode(routeReuseStrategy, child, p);\n }\n }\n return createNode(routeReuseStrategy, child);\n });\n}\n\nfunction createActivatedRoute(c: ActivatedRouteSnapshot) {\n return new ActivatedRoute(\n new BehaviorSubject(c.url),\n new BehaviorSubject(c.params),\n new BehaviorSubject(c.queryParams),\n new BehaviorSubject(c.fragment),\n new BehaviorSubject(c.data),\n c.outlet,\n c.component,\n c,\n );\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n EnvironmentInjector,\n EnvironmentProviders,\n NgModuleFactory,\n Provider,\n ProviderToken,\n Type,\n} from '@angular/core';\nimport {Observable} from 'rxjs';\n\nimport {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';\nimport {UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';\n\n/**\n * How to handle a navigation request to the current URL. One of:\n *\n * - `'ignore'` : The router ignores the request it is the same as the current state.\n * - `'reload'` : The router processes the URL even if it is not different from the current state.\n * One example of when you might want this option is if a `canMatch` guard depends on\n * application state and initially rejects navigation to a route. After fixing the state, you want\n * to re-navigate to the same URL so the route with the `canMatch` guard can activate.\n *\n * Note that this only configures whether the Route reprocesses the URL and triggers related\n * action and events like redirects, guards, and resolvers. By default, the router re-uses a\n * component instance when it re-navigates to the same component type without visiting a different\n * component first. This behavior is configured by the `RouteReuseStrategy`. In order to reload\n * routed components on same url navigation, you need to set `onSameUrlNavigation` to `'reload'`\n * _and_ provide a `RouteReuseStrategy` which returns `false` for `shouldReuseRoute`. Additionally,\n * resolvers and most guards for routes do not run unless the path or path params changed\n * (configured by `runGuardsAndResolvers`).\n *\n * @publicApi\n * @see {@link RouteReuseStrategy}\n * @see {@link RunGuardsAndResolvers}\n * @see {@link NavigationBehaviorOptions}\n * @see {@link RouterConfigOptions}\n */\nexport type OnSameUrlNavigation = 'reload' | 'ignore';\n\n/**\n * The `InjectionToken` and `@Injectable` classes for guards and resolvers are deprecated in favor\n * of plain JavaScript functions instead.. Dependency injection can still be achieved using the\n * [`inject`](api/core/inject) function from `@angular/core` and an injectable class can be used as\n * a functional guard using [`inject`](api/core/inject): `canActivate: [() =>\n * inject(myGuard).canActivate()]`.\n *\n * @deprecated\n * @see {@link CanMatchFn}\n * @see {@link CanLoadFn}\n * @see {@link CanActivateFn}\n * @see {@link CanActivateChildFn}\n * @see {@link CanDeactivateFn}\n * @see {@link ResolveFn}\n * @see {@link core/inject}\n * @publicApi\n */\nexport type DeprecatedGuard = ProviderToken | any;\n\n/**\n * The supported types that can be returned from a `Router` guard.\n *\n * @see [Routing guide](guide/routing/common-router-tasks#preventing-unauthorized-access)\n * @publicApi\n */\nexport type GuardResult = boolean | UrlTree | RedirectCommand;\n\n/**\n * Can be returned by a `Router` guard to instruct the `Router` to redirect rather than continue\n * processing the path of the in-flight navigation. The `redirectTo` indicates _where_ the new\n * navigation should go to and the optional `navigationBehaviorOptions` can provide more information\n * about _how_ to perform the navigation.\n *\n * ```ts\n * const route: Route = {\n * path: \"user/:userId\",\n * component: User,\n * canActivate: [\n * () => {\n * const router = inject(Router);\n * const authService = inject(AuthenticationService);\n *\n * if (!authService.isLoggedIn()) {\n * const loginPath = router.parseUrl(\"/login\");\n * return new RedirectCommand(loginPath, {\n * skipLocationChange: \"true\",\n * });\n * }\n *\n * return true;\n * },\n * ],\n * };\n * ```\n * @see [Routing guide](guide/routing/common-router-tasks#preventing-unauthorized-access)\n *\n * @publicApi\n */\nexport class RedirectCommand {\n constructor(\n readonly redirectTo: UrlTree,\n readonly navigationBehaviorOptions?: NavigationBehaviorOptions,\n ) {}\n}\n\n/**\n * Type used to represent a value which may be synchronous or async.\n *\n * @publicApi\n */\nexport type MaybeAsync = T | Observable | Promise;\n\n/**\n * Represents a route configuration for the Router service.\n * An array of `Route` objects, used in `Router.config` and for nested route configurations\n * in `Route.children`.\n *\n * @see {@link Route}\n * @see {@link Router}\n * @see [Router configuration guide](guide/routing/router-reference#configuration)\n * @publicApi\n */\nexport type Routes = Route[];\n\n/**\n * Represents the result of matching URLs with a custom matching function.\n *\n * * `consumed` is an array of the consumed URL segments.\n * * `posParams` is a map of positional parameters.\n *\n * @see {@link UrlMatcher}\n * @publicApi\n */\nexport type UrlMatchResult = {\n consumed: UrlSegment[];\n posParams?: {[name: string]: UrlSegment};\n};\n\n/**\n * A function for matching a route against URLs. Implement a custom URL matcher\n * for `Route.matcher` when a combination of `path` and `pathMatch`\n * is not expressive enough. Cannot be used together with `path` and `pathMatch`.\n *\n * The function takes the following arguments and returns a `UrlMatchResult` object.\n * * *segments* : An array of URL segments.\n * * *group* : A segment group.\n * * *route* : The route to match against.\n *\n * The following example implementation matches HTML files.\n *\n * ```\n * export function htmlFiles(url: UrlSegment[]) {\n * return url.length === 1 && url[0].path.endsWith('.html') ? ({consumed: url}) : null;\n * }\n *\n * export const routes = [{ matcher: htmlFiles, component: AnyComponent }];\n * ```\n *\n * @publicApi\n */\nexport type UrlMatcher = (\n segments: UrlSegment[],\n group: UrlSegmentGroup,\n route: Route,\n) => UrlMatchResult | null;\n\n/**\n *\n * Represents static data associated with a particular route.\n *\n * @see {@link Route#data}\n *\n * @publicApi\n */\nexport type Data = {\n [key: string | symbol]: any;\n};\n\n/**\n *\n * Represents the resolved data associated with a particular route.\n *\n * Returning a `RedirectCommand` directs the router to cancel the current navigation and redirect to\n * the location provided in the `RedirectCommand`. Note that there are no ordering guarantees when\n * resolvers execute. If multiple resolvers would return a `RedirectCommand`, only the first one\n * returned will be used.\n *\n * @see {@link Route#resolve}\n *\n * @publicApi\n */\nexport type ResolveData = {\n [key: string | symbol]: ResolveFn | DeprecatedGuard;\n};\n\n/**\n * An ES Module object with a default export of the given type.\n *\n * @see {@link Route#loadComponent}\n * @see {@link LoadChildrenCallback}\n *\n * @publicApi\n */\nexport interface DefaultExport {\n /**\n * Default exports are bound under the name `\"default\"`, per the ES Module spec:\n * https://tc39.es/ecma262/#table-export-forms-mapping-to-exportentry-records\n */\n default: T;\n}\n\n/**\n *\n * A function that is called to resolve a collection of lazy-loaded routes.\n * Must be an arrow function of the following form:\n * `() => import('...').then(mod => mod.MODULE)`\n * or\n * `() => import('...').then(mod => mod.ROUTES)`\n *\n * For example:\n *\n * ```\n * [{\n * path: 'lazy',\n * loadChildren: () => import('./lazy-route/lazy.module').then(mod => mod.LazyModule),\n * }];\n * ```\n * or\n * ```\n * [{\n * path: 'lazy',\n * loadChildren: () => import('./lazy-route/lazy.routes').then(mod => mod.ROUTES),\n * }];\n * ```\n *\n * If the lazy-loaded routes are exported via a `default` export, the `.then` can be omitted:\n * ```\n * [{\n * path: 'lazy',\n * loadChildren: () => import('./lazy-route/lazy.routes'),\n * }];\n * ```\n *\n * @see {@link Route#loadChildren}\n * @publicApi\n */\nexport type LoadChildrenCallback = () =>\n | Type\n | NgModuleFactory\n | Routes\n | Observable | Routes | DefaultExport> | DefaultExport>\n | Promise<\n NgModuleFactory | Type | Routes | DefaultExport> | DefaultExport\n >;\n\n/**\n *\n * A function that returns a set of routes to load.\n *\n * @see {@link LoadChildrenCallback}\n * @publicApi\n */\nexport type LoadChildren = LoadChildrenCallback;\n\n/**\n *\n * How to handle query parameters in a router link.\n * One of:\n * - `\"merge\"` : Merge new parameters with current parameters.\n * - `\"preserve\"` : Preserve current parameters.\n * - `\"replace\"` : Replace current parameters with new parameters. This is the default behavior.\n * - `\"\"` : For legacy reasons, the same as `'replace'`.\n *\n * @see {@link UrlCreationOptions#queryParamsHandling}\n * @see {@link RouterLink}\n * @publicApi\n */\nexport type QueryParamsHandling = 'merge' | 'preserve' | 'replace' | '';\n\n/**\n * The type for the function that can be used to handle redirects when the path matches a `Route` config.\n *\n * The `RedirectFunction` does have access to the full\n * `ActivatedRouteSnapshot` interface. Some data are not accurately known\n * at the route matching phase. For example, resolvers are not run until\n * later, so any resolved title would not be populated. The same goes for lazy\n * loaded components. This is also true for all the snapshots up to the\n * root, so properties that include parents (root, parent, pathFromRoot)\n * are also excluded. And naturally, the full route matching hasn't yet\n * happened so firstChild and children are not available either.\n *\n * @see {@link Route#redirectTo}\n * @publicApi\n */\nexport type RedirectFunction = (\n redirectData: Pick<\n ActivatedRouteSnapshot,\n 'routeConfig' | 'url' | 'params' | 'queryParams' | 'fragment' | 'data' | 'outlet' | 'title'\n >,\n) => string | UrlTree;\n\n/**\n * A policy for when to run guards and resolvers on a route.\n *\n * Guards and/or resolvers will always run when a route is activated or deactivated. When a route is\n * unchanged, the default behavior is the same as `paramsChange`.\n *\n * `paramsChange` : Rerun the guards and resolvers when path or\n * path param changes. This does not include query parameters. This option is the default.\n * - `always` : Run on every execution.\n * - `pathParamsChange` : Rerun guards and resolvers when the path params\n * change. This does not compare matrix or query parameters.\n * - `paramsOrQueryParamsChange` : Run when path, matrix, or query parameters change.\n * - `pathParamsOrQueryParamsChange` : Rerun guards and resolvers when the path params\n * change or query params have changed. This does not include matrix parameters.\n *\n * @see {@link Route#runGuardsAndResolvers}\n * @publicApi\n */\nexport type RunGuardsAndResolvers =\n | 'pathParamsChange'\n | 'pathParamsOrQueryParamsChange'\n | 'paramsChange'\n | 'paramsOrQueryParamsChange'\n | 'always'\n | ((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);\n\n/**\n * A configuration object that defines a single route.\n * A set of routes are collected in a `Routes` array to define a `Router` configuration.\n * The router attempts to match segments of a given URL against each route,\n * using the configuration options defined in this object.\n *\n * Supports static, parameterized, redirect, and wildcard routes, as well as\n * custom route data and resolve methods.\n *\n * For detailed usage information, see the [Routing Guide](guide/routing/common-router-tasks).\n *\n * @usageNotes\n *\n * ### Simple Configuration\n *\n * The following route specifies that when navigating to, for example,\n * `/team/11/user/bob`, the router creates the 'Team' component\n * with the 'User' child component in it.\n *\n * ```\n * [{\n * path: 'team/:id',\n * component: Team,\n * children: [{\n * path: 'user/:name',\n * component: User\n * }]\n * }]\n * ```\n *\n * ### Multiple Outlets\n *\n * The following route creates sibling components with multiple outlets.\n * When navigating to `/team/11(aux:chat/jim)`, the router creates the 'Team' component next to\n * the 'Chat' component. The 'Chat' component is placed into the 'aux' outlet.\n *\n * ```\n * [{\n * path: 'team/:id',\n * component: Team\n * }, {\n * path: 'chat/:user',\n * component: Chat\n * outlet: 'aux'\n * }]\n * ```\n *\n * ### Wild Cards\n *\n * The following route uses wild-card notation to specify a component\n * that is always instantiated regardless of where you navigate to.\n *\n * ```\n * [{\n * path: '**',\n * component: WildcardComponent\n * }]\n * ```\n *\n * ### Redirects\n *\n * The following route uses the `redirectTo` property to ignore a segment of\n * a given URL when looking for a child path.\n *\n * When navigating to '/team/11/legacy/user/jim', the router changes the URL segment\n * '/team/11/legacy/user/jim' to '/team/11/user/jim', and then instantiates\n * the Team component with the User child component in it.\n *\n * ```\n * [{\n * path: 'team/:id',\n * component: Team,\n * children: [{\n * path: 'legacy/user/:name',\n * redirectTo: 'user/:name'\n * }, {\n * path: 'user/:name',\n * component: User\n * }]\n * }]\n * ```\n *\n * The redirect path can be relative, as shown in this example, or absolute.\n * If we change the `redirectTo` value in the example to the absolute URL segment '/user/:name',\n * the result URL is also absolute, '/user/jim'.\n\n * ### Empty Path\n *\n * Empty-path route configurations can be used to instantiate components that do not 'consume'\n * any URL segments.\n *\n * In the following configuration, when navigating to\n * `/team/11`, the router instantiates the 'AllUsers' component.\n *\n * ```\n * [{\n * path: 'team/:id',\n * component: Team,\n * children: [{\n * path: '',\n * component: AllUsers\n * }, {\n * path: 'user/:name',\n * component: User\n * }]\n * }]\n * ```\n *\n * Empty-path routes can have children. In the following example, when navigating\n * to `/team/11/user/jim`, the router instantiates the wrapper component with\n * the user component in it.\n *\n * Note that an empty path route inherits its parent's parameters and data.\n *\n * ```\n * [{\n * path: 'team/:id',\n * component: Team,\n * children: [{\n * path: '',\n * component: WrapperCmp,\n * children: [{\n * path: 'user/:name',\n * component: User\n * }]\n * }]\n * }]\n * ```\n *\n * ### Matching Strategy\n *\n * The default path-match strategy is 'prefix', which means that the router\n * checks URL elements from the left to see if the URL matches a specified path.\n * For example, '/team/11/user' matches 'team/:id'.\n *\n * ```\n * [{\n * path: '',\n * pathMatch: 'prefix', //default\n * redirectTo: 'main'\n * }, {\n * path: 'main',\n * component: Main\n * }]\n * ```\n *\n * You can specify the path-match strategy 'full' to make sure that the path\n * covers the whole unconsumed URL. It is important to do this when redirecting\n * empty-path routes. Otherwise, because an empty path is a prefix of any URL,\n * the router would apply the redirect even when navigating to the redirect destination,\n * creating an endless loop.\n *\n * In the following example, supplying the 'full' `pathMatch` strategy ensures\n * that the router applies the redirect if and only if navigating to '/'.\n *\n * ```\n * [{\n * path: '',\n * pathMatch: 'full',\n * redirectTo: 'main'\n * }, {\n * path: 'main',\n * component: Main\n * }]\n * ```\n *\n * ### Componentless Routes\n *\n * You can share parameters between sibling components.\n * For example, suppose that two sibling components should go next to each other,\n * and both of them require an ID parameter. You can accomplish this using a route\n * that does not specify a component at the top level.\n *\n * In the following example, 'MainChild' and 'AuxChild' are siblings.\n * When navigating to 'parent/10/(a//aux:b)', the route instantiates\n * the main child and aux child components next to each other.\n * For this to work, the application component must have the primary and aux outlets defined.\n *\n * ```\n * [{\n * path: 'parent/:id',\n * children: [\n * { path: 'a', component: MainChild },\n * { path: 'b', component: AuxChild, outlet: 'aux' }\n * ]\n * }]\n * ```\n *\n * The router merges the parameters, data, and resolve of the componentless\n * parent into the parameters, data, and resolve of the children.\n *\n * This is especially useful when child components are defined\n * with an empty path string, as in the following example.\n * With this configuration, navigating to '/parent/10' creates\n * the main child and aux components.\n *\n * ```\n * [{\n * path: 'parent/:id',\n * children: [\n * { path: '', component: MainChild },\n * { path: '', component: AuxChild, outlet: 'aux' }\n * ]\n * }]\n * ```\n *\n * ### Lazy Loading\n *\n * Lazy loading speeds up application load time by splitting the application\n * into multiple bundles and loading them on demand.\n * To use lazy loading, provide the `loadChildren` property in the `Route` object,\n * instead of the `children` property.\n *\n * Given the following example route, the router will lazy load\n * the associated module on demand using the browser native import system.\n *\n * ```\n * [{\n * path: 'lazy',\n * loadChildren: () => import('./lazy-route/lazy.module').then(mod => mod.LazyModule),\n * }];\n * ```\n *\n * @publicApi\n */\nexport interface Route {\n /**\n * Used to define a page title for the route. This can be a static string or an `Injectable` that\n * implements `Resolve`.\n *\n * @see {@link TitleStrategy}\n */\n title?: string | Type> | ResolveFn;\n\n /**\n * The path to match against. Cannot be used together with a custom `matcher` function.\n * A URL string that uses router matching notation.\n * Can be a wild card (`**`) that matches any URL (see Usage Notes below).\n * Default is \"/\" (the root path).\n *\n */\n path?: string;\n /**\n * The path-matching strategy, one of 'prefix' or 'full'.\n * Default is 'prefix'.\n *\n * By default, the router checks URL elements from the left to see if the URL\n * matches a given path and stops when there is a config match. Importantly there must still be a\n * config match for each segment of the URL. For example, '/team/11/user' matches the prefix\n * 'team/:id' if one of the route's children matches the segment 'user'. That is, the URL\n * '/team/11/user' matches the config\n * `{path: 'team/:id', children: [{path: ':user', component: User}]}`\n * but does not match when there are no children as in `{path: 'team/:id', component: Team}`.\n *\n * The path-match strategy 'full' matches against the entire URL.\n * It is important to do this when redirecting empty-path routes.\n * Otherwise, because an empty path is a prefix of any URL,\n * the router would apply the redirect even when navigating\n * to the redirect destination, creating an endless loop.\n *\n */\n pathMatch?: 'prefix' | 'full';\n /**\n * A custom URL-matching function. Cannot be used together with `path`.\n */\n matcher?: UrlMatcher;\n /**\n * The component to instantiate when the path matches.\n * Can be empty if child routes specify components.\n */\n component?: Type;\n\n /**\n * An object specifying a lazy-loaded component.\n */\n loadComponent?: () =>\n | Type\n | Observable | DefaultExport>>\n | Promise | DefaultExport>>;\n /**\n * Filled for routes `loadComponent` once the component is loaded.\n * @internal\n */\n _loadedComponent?: Type;\n\n /**\n * A URL or function that returns a URL to redirect to when the path matches.\n *\n * Absolute if the URL begins with a slash (/) or the function returns a `UrlTree`, otherwise\n * relative to the path URL.\n *\n * The `RedirectFunction` is run in an injection context so it can call `inject` to get any\n * required dependencies.\n *\n * When not present, router does not redirect.\n */\n redirectTo?: string | RedirectFunction;\n /**\n * Name of a `RouterOutlet` object where the component can be placed\n * when the path matches.\n */\n outlet?: string;\n /**\n * An array of `CanActivateFn` or DI tokens used to look up `CanActivate()`\n * handlers, in order to determine if the current user is allowed to\n * activate the component. By default, any user can activate.\n *\n * When using a function rather than DI tokens, the function can call `inject` to get any required\n * dependencies. This `inject` call must be done in a synchronous context.\n */\n canActivate?: Array;\n /**\n * An array of `CanMatchFn` or DI tokens used to look up `CanMatch()`\n * handlers, in order to determine if the current user is allowed to\n * match the `Route`. By default, any route can match.\n *\n * When using a function rather than DI tokens, the function can call `inject` to get any required\n * dependencies. This `inject` call must be done in a synchronous context.\n */\n canMatch?: Array;\n /**\n * An array of `CanActivateChildFn` or DI tokens used to look up `CanActivateChild()` handlers,\n * in order to determine if the current user is allowed to activate\n * a child of the component. By default, any user can activate a child.\n *\n * When using a function rather than DI tokens, the function can call `inject` to get any required\n * dependencies. This `inject` call must be done in a synchronous context.\n */\n canActivateChild?: Array;\n /**\n * An array of `CanDeactivateFn` or DI tokens used to look up `CanDeactivate()`\n * handlers, in order to determine if the current user is allowed to\n * deactivate the component. By default, any user can deactivate.\n *\n * When using a function rather than DI tokens, the function can call `inject` to get any required\n * dependencies. This `inject` call must be done in a synchronous context.\n */\n canDeactivate?: Array | DeprecatedGuard>;\n /**\n * An array of `CanLoadFn` or DI tokens used to look up `CanLoad()`\n * handlers, in order to determine if the current user is allowed to\n * load the component. By default, any user can load.\n *\n * When using a function rather than DI tokens, the function can call `inject` to get any required\n * dependencies. This `inject` call must be done in a synchronous context.\n * @deprecated Use `canMatch` instead\n */\n canLoad?: Array;\n /**\n * Additional developer-defined data provided to the component via\n * `ActivatedRoute`. By default, no additional data is passed.\n */\n data?: Data;\n /**\n * A map of DI tokens used to look up data resolvers. See `Resolve`.\n */\n resolve?: ResolveData;\n /**\n * An array of child `Route` objects that specifies a nested route\n * configuration.\n */\n children?: Routes;\n /**\n * An object specifying lazy-loaded child routes.\n */\n loadChildren?: LoadChildren;\n\n /**\n * A policy for when to run guards and resolvers on a route.\n *\n * Guards and/or resolvers will always run when a route is activated or deactivated. When a route\n * is unchanged, the default behavior is the same as `paramsChange`.\n *\n * `paramsChange` : Rerun the guards and resolvers when path or\n * path param changes. This does not include query parameters. This option is the default.\n * - `always` : Run on every execution.\n * - `pathParamsChange` : Rerun guards and resolvers when the path params\n * change. This does not compare matrix or query parameters.\n * - `paramsOrQueryParamsChange` : Run when path, matrix, or query parameters change.\n * - `pathParamsOrQueryParamsChange` : Rerun guards and resolvers when the path params\n * change or query params have changed. This does not include matrix parameters.\n *\n * @see {@link RunGuardsAndResolvers}\n */\n runGuardsAndResolvers?: RunGuardsAndResolvers;\n\n /**\n * A `Provider` array to use for this `Route` and its `children`.\n *\n * The `Router` will create a new `EnvironmentInjector` for this\n * `Route` and use it for this `Route` and its `children`. If this\n * route also has a `loadChildren` function which returns an `NgModuleRef`, this injector will be\n * used as the parent of the lazy loaded module.\n */\n providers?: Array;\n\n /**\n * Injector created from the static route providers\n * @internal\n */\n _injector?: EnvironmentInjector;\n\n /**\n * Filled for routes with `loadChildren` once the routes are loaded.\n * @internal\n */\n _loadedRoutes?: Route[];\n\n /**\n * Filled for routes with `loadChildren` once the routes are loaded\n * @internal\n */\n _loadedInjector?: EnvironmentInjector;\n}\n\nexport interface LoadedRouterConfig {\n routes: Route[];\n injector: EnvironmentInjector | undefined;\n}\n\n/**\n * @description\n *\n * Interface that a class can implement to be a guard deciding if a route can be activated.\n * If all guards return `true`, navigation continues. If any guard returns `false`,\n * navigation is cancelled. If any guard returns a `UrlTree`, the current navigation\n * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.\n *\n * The following example implements a `CanActivate` function that checks whether the\n * current user has permission to activate the requested route.\n *\n * ```\n * class UserToken {}\n * class Permissions {\n * canActivate(): boolean {\n * return true;\n * }\n * }\n *\n * @Injectable()\n * class CanActivateTeam implements CanActivate {\n * constructor(private permissions: Permissions, private currentUser: UserToken) {}\n *\n * canActivate(\n * route: ActivatedRouteSnapshot,\n * state: RouterStateSnapshot\n * ): MaybeAsync {\n * return this.permissions.canActivate(this.currentUser, route.params.id);\n * }\n * }\n * ```\n *\n * Here, the defined guard function is provided as part of the `Route` object\n * in the router configuration:\n *\n * ```\n * @NgModule({\n * imports: [\n * RouterModule.forRoot([\n * {\n * path: 'team/:id',\n * component: TeamComponent,\n * canActivate: [CanActivateTeam]\n * }\n * ])\n * ],\n * providers: [CanActivateTeam, UserToken, Permissions]\n * })\n * class AppModule {}\n * ```\n *\n * @publicApi\n */\nexport interface CanActivate {\n canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): MaybeAsync;\n}\n\n/**\n * The signature of a function used as a `canActivate` guard on a `Route`.\n *\n * If all guards return `true`, navigation continues. If any guard returns `false`,\n * navigation is cancelled. If any guard returns a `UrlTree`, the current navigation\n * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.\n *\n * The following example implements and uses a `CanActivateFn` that checks whether the\n * current user has permission to activate the requested route.\n *\n * ```ts\n * @Injectable()\n * class UserToken {}\n *\n * @Injectable()\n * class PermissionsService {\n * canActivate(currentUser: UserToken, userId: string): boolean {\n * return true;\n * }\n * canMatch(currentUser: UserToken): boolean {\n * return true;\n * }\n * }\n *\n * const canActivateTeam: CanActivateFn = (\n * route: ActivatedRouteSnapshot,\n * state: RouterStateSnapshot,\n * ) => {\n * return inject(PermissionsService).canActivate(inject(UserToken), route.params['id']);\n * };\n * ```\n *\n * Here, the defined guard function is provided as part of the `Route` object\n * in the router configuration:\n *\n * ```ts\n * bootstrapApplication(App, {\n * providers: [\n * provideRouter([\n * {\n * path: 'team/:id',\n * component: TeamComponent,\n * canActivate: [canActivateTeam],\n * },\n * ]),\n * ],\n * });\n * ```\n *\n * @publicApi\n * @see {@link Route}\n */\nexport type CanActivateFn = (\n route: ActivatedRouteSnapshot,\n state: RouterStateSnapshot,\n) => MaybeAsync;\n\n/**\n * @description\n *\n * Interface that a class can implement to be a guard deciding if a child route can be activated.\n * If all guards return `true`, navigation continues. If any guard returns `false`,\n * navigation is cancelled. If any guard returns a `UrlTree`, current navigation\n * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.\n *\n * The following example implements a `CanActivateChild` function that checks whether the\n * current user has permission to activate the requested child route.\n *\n * ```\n * class UserToken {}\n * class Permissions {\n * canActivate(user: UserToken, id: string): boolean {\n * return true;\n * }\n * }\n *\n * @Injectable()\n * class CanActivateTeam implements CanActivateChild {\n * constructor(private permissions: Permissions, private currentUser: UserToken) {}\n *\n * canActivateChild(\n * route: ActivatedRouteSnapshot,\n * state: RouterStateSnapshot\n * ): MaybeAsync {\n * return this.permissions.canActivate(this.currentUser, route.params.id);\n * }\n * }\n * ```\n *\n * Here, the defined guard function is provided as part of the `Route` object\n * in the router configuration:\n *\n * ```\n * @NgModule({\n * imports: [\n * RouterModule.forRoot([\n * {\n * path: 'root',\n * canActivateChild: [CanActivateTeam],\n * children: [\n * {\n * path: 'team/:id',\n * component: TeamComponent\n * }\n * ]\n * }\n * ])\n * ],\n * providers: [CanActivateTeam, UserToken, Permissions]\n * })\n * class AppModule {}\n * ```\n *\n * @publicApi\n */\nexport interface CanActivateChild {\n canActivateChild(\n childRoute: ActivatedRouteSnapshot,\n state: RouterStateSnapshot,\n ): MaybeAsync;\n}\n\n/**\n * The signature of a function used as a `canActivateChild` guard on a `Route`.\n *\n * If all guards return `true`, navigation continues. If any guard returns `false`,\n * navigation is cancelled. If any guard returns a `UrlTree`, the current navigation\n * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.\n *\n * The following example implements a `canActivate` function that checks whether the\n * current user has permission to activate the requested route.\n *\n * {@example router/route_functional_guards.ts region=\"CanActivateChildFn\"}\n *\n * @publicApi\n * @see {@link Route}\n */\nexport type CanActivateChildFn = (\n childRoute: ActivatedRouteSnapshot,\n state: RouterStateSnapshot,\n) => MaybeAsync;\n\n/**\n * @description\n *\n * Interface that a class can implement to be a guard deciding if a route can be deactivated.\n * If all guards return `true`, navigation continues. If any guard returns `false`,\n * navigation is cancelled. If any guard returns a `UrlTree`, current navigation\n * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.\n *\n * The following example implements a `CanDeactivate` function that checks whether the\n * current user has permission to deactivate the requested route.\n *\n * ```\n * class UserToken {}\n * class Permissions {\n * canDeactivate(user: UserToken, id: string): boolean {\n * return true;\n * }\n * }\n * ```\n *\n * Here, the defined guard function is provided as part of the `Route` object\n * in the router configuration:\n *\n * ```\n *\n * @Injectable()\n * class CanDeactivateTeam implements CanDeactivate {\n * constructor(private permissions: Permissions, private currentUser: UserToken) {}\n *\n * canDeactivate(\n * component: TeamComponent,\n * currentRoute: ActivatedRouteSnapshot,\n * currentState: RouterStateSnapshot,\n * nextState: RouterStateSnapshot\n * ): MaybeAsync {\n * return this.permissions.canDeactivate(this.currentUser, route.params.id);\n * }\n * }\n *\n * @NgModule({\n * imports: [\n * RouterModule.forRoot([\n * {\n * path: 'team/:id',\n * component: TeamComponent,\n * canDeactivate: [CanDeactivateTeam]\n * }\n * ])\n * ],\n * providers: [CanDeactivateTeam, UserToken, Permissions]\n * })\n * class AppModule {}\n * ```\n *\n * @publicApi\n */\nexport interface CanDeactivate {\n canDeactivate(\n component: T,\n currentRoute: ActivatedRouteSnapshot,\n currentState: RouterStateSnapshot,\n nextState: RouterStateSnapshot,\n ): MaybeAsync;\n}\n\n/**\n * The signature of a function used as a `canDeactivate` guard on a `Route`.\n *\n * If all guards return `true`, navigation continues. If any guard returns `false`,\n * navigation is cancelled. If any guard returns a `UrlTree`, the current navigation\n * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.\n *\n * The following example implements and uses a `CanDeactivateFn` that checks whether the\n * user component has unsaved changes before navigating away from the route.\n *\n * {@example router/route_functional_guards.ts region=\"CanDeactivateFn\"}\n *\n * @publicApi\n * @see {@link Route}\n */\nexport type CanDeactivateFn = (\n component: T,\n currentRoute: ActivatedRouteSnapshot,\n currentState: RouterStateSnapshot,\n nextState: RouterStateSnapshot,\n) => MaybeAsync;\n\n/**\n * @description\n *\n * Interface that a class can implement to be a guard deciding if a `Route` can be matched.\n * If all guards return `true`, navigation continues and the `Router` will use the `Route` during\n * activation. If any guard returns `false`, the `Route` is skipped for matching and other `Route`\n * configurations are processed instead.\n *\n * The following example implements a `CanMatch` function that decides whether the\n * current user has permission to access the users page.\n *\n *\n * ```\n * class UserToken {}\n * class Permissions {\n * canAccess(user: UserToken, route: Route, segments: UrlSegment[]): boolean {\n * return true;\n * }\n * }\n *\n * @Injectable()\n * class CanMatchTeamSection implements CanMatch {\n * constructor(private permissions: Permissions, private currentUser: UserToken) {}\n *\n * canMatch(route: Route, segments: UrlSegment[]): Observable|Promise|boolean {\n * return this.permissions.canAccess(this.currentUser, route, segments);\n * }\n * }\n * ```\n *\n * Here, the defined guard function is provided as part of the `Route` object\n * in the router configuration:\n *\n * ```\n *\n * @NgModule({\n * imports: [\n * RouterModule.forRoot([\n * {\n * path: 'team/:id',\n * component: TeamComponent,\n * loadChildren: () => import('./team').then(mod => mod.TeamModule),\n * canMatch: [CanMatchTeamSection]\n * },\n * {\n * path: '**',\n * component: NotFoundComponent\n * }\n * ])\n * ],\n * providers: [CanMatchTeamSection, UserToken, Permissions]\n * })\n * class AppModule {}\n * ```\n *\n * If the `CanMatchTeamSection` were to return `false`, the router would continue navigating to the\n * `team/:id` URL, but would load the `NotFoundComponent` because the `Route` for `'team/:id'`\n * could not be used for a URL match but the catch-all `**` `Route` did instead.\n *\n * @publicApi\n */\nexport interface CanMatch {\n canMatch(route: Route, segments: UrlSegment[]): MaybeAsync;\n}\n\n/**\n * The signature of a function used as a `canMatch` guard on a `Route`.\n *\n * If all guards return `true`, navigation continues and the `Router` will use the `Route` during\n * activation. If any guard returns `false`, the `Route` is skipped for matching and other `Route`\n * configurations are processed instead.\n *\n * The following example implements and uses a `CanMatchFn` that checks whether the\n * current user has permission to access the team page.\n *\n * {@example router/route_functional_guards.ts region=\"CanMatchFn\"}\n *\n * @publicApi\n * @see {@link Route}\n */\nexport type CanMatchFn = (route: Route, segments: UrlSegment[]) => MaybeAsync;\n\n/**\n * @description\n *\n * Interface that classes can implement to be a data provider.\n * A data provider class can be used with the router to resolve data during navigation.\n * The interface defines a `resolve()` method that is invoked right after the `ResolveStart`\n * router event. The router waits for the data to be resolved before the route is finally activated.\n *\n * The following example implements a `resolve()` method that retrieves the data\n * needed to activate the requested route.\n *\n * ```\n * @Injectable({ providedIn: 'root' })\n * export class HeroResolver implements Resolve {\n * constructor(private service: HeroService) {}\n *\n * resolve(\n * route: ActivatedRouteSnapshot,\n * state: RouterStateSnapshot\n * ): Observable|Promise|Hero {\n * return this.service.getHero(route.paramMap.get('id'));\n * }\n * }\n * ```\n *\n * Here, the defined `resolve()` function is provided as part of the `Route` object\n * in the router configuration:\n *\n * ```\n\n * @NgModule({\n * imports: [\n * RouterModule.forRoot([\n * {\n * path: 'detail/:id',\n * component: HeroDetailComponent,\n * resolve: {\n * hero: HeroResolver\n * }\n * }\n * ])\n * ],\n * exports: [RouterModule]\n * })\n * export class AppRoutingModule {}\n * ```\n *\n * And you can access to your resolved data from `HeroComponent`:\n *\n * ```\n * @Component({\n * selector: \"app-hero\",\n * templateUrl: \"hero.component.html\",\n * })\n * export class HeroComponent {\n *\n * constructor(private activatedRoute: ActivatedRoute) {}\n *\n * ngOnInit() {\n * this.activatedRoute.data.subscribe(({ hero }) => {\n * // do something with your resolved data ...\n * })\n * }\n *\n * }\n * ```\n *\n * @usageNotes\n *\n * When both guard and resolvers are specified, the resolvers are not executed until\n * all guards have run and succeeded.\n * For example, consider the following route configuration:\n *\n * ```\n * {\n * path: 'base'\n * canActivate: [BaseGuard],\n * resolve: {data: BaseDataResolver}\n * children: [\n * {\n * path: 'child',\n * guards: [ChildGuard],\n * component: ChildComponent,\n * resolve: {childData: ChildDataResolver}\n * }\n * ]\n * }\n * ```\n * The order of execution is: BaseGuard, ChildGuard, BaseDataResolver, ChildDataResolver.\n *\n * @publicApi\n * @see {@link ResolveFn}\n */\nexport interface Resolve {\n resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): MaybeAsync;\n}\n\n/**\n * Function type definition for a data provider.\n *\n * A data provider can be used with the router to resolve data during navigation.\n * The router waits for the data to be resolved before the route is finally activated.\n *\n * A resolver can also redirect a `RedirectCommand` and the Angular router will use\n * it to redirect the current navigation to the new destination.\n *\n * @usageNotes\n *\n * The following example implements a function that retrieves the data\n * needed to activate the requested route.\n *\n * ```ts\n * interface Hero {\n * name: string;\n * }\n * @Injectable()\n * export class HeroService {\n * getHero(id: string) {\n * return {name: `Superman-${id}`};\n * }\n * }\n *\n * export const heroResolver: ResolveFn = (\n * route: ActivatedRouteSnapshot,\n * state: RouterStateSnapshot,\n * ) => {\n * return inject(HeroService).getHero(route.paramMap.get('id')!);\n * };\n *\n * bootstrapApplication(App, {\n * providers: [\n * provideRouter([\n * {\n * path: 'detail/:id',\n * component: HeroDetailComponent,\n * resolve: {hero: heroResolver},\n * },\n * ]),\n * ],\n * });\n * ```\n *\n * And you can access to your resolved data from `HeroComponent`:\n *\n * ```ts\n * @Component({template: ''})\n * export class HeroDetailComponent {\n * private activatedRoute = inject(ActivatedRoute);\n *\n * ngOnInit() {\n * this.activatedRoute.data.subscribe(({hero}) => {\n * // do something with your resolved data ...\n * });\n * }\n * }\n * ```\n *\n * If resolved data cannot be retrieved, you may want to redirect the user\n * to a new page instead:\n *\n * ```ts\n * export const heroResolver: ResolveFn = async (\n * route: ActivatedRouteSnapshot,\n * state: RouterStateSnapshot,\n * ) => {\n * const router = inject(Router);\n * const heroService = inject(HeroService);\n * try {\n * return await heroService.getHero(route.paramMap.get('id')!);\n * } catch {\n * return new RedirectCommand(router.parseUrl('/404'));\n * }\n * };\n * ```\n *\n * When both guard and resolvers are specified, the resolvers are not executed until\n * all guards have run and succeeded.\n * For example, consider the following route configuration:\n *\n * ```\n * {\n * path: 'base'\n * canActivate: [baseGuard],\n * resolve: {data: baseDataResolver}\n * children: [\n * {\n * path: 'child',\n * canActivate: [childGuard],\n * component: ChildComponent,\n * resolve: {childData: childDataResolver}\n * }\n * ]\n * }\n * ```\n * The order of execution is: baseGuard, childGuard, baseDataResolver, childDataResolver.\n *\n * @publicApi\n * @see {@link Route}\n */\nexport type ResolveFn = (\n route: ActivatedRouteSnapshot,\n state: RouterStateSnapshot,\n) => MaybeAsync;\n\n/**\n * @description\n *\n * Interface that a class can implement to be a guard deciding if children can be loaded.\n * If all guards return `true`, navigation continues. If any guard returns `false`,\n * navigation is cancelled. If any guard returns a `UrlTree`, current navigation\n * is cancelled and a new navigation starts to the `UrlTree` returned from the guard.\n *\n * The following example implements a `CanLoad` function that decides whether the\n * current user has permission to load requested child routes.\n *\n *\n * ```\n * class UserToken {}\n * class Permissions {\n * canLoadChildren(user: UserToken, id: string, segments: UrlSegment[]): boolean {\n * return true;\n * }\n * }\n *\n * @Injectable()\n * class CanLoadTeamSection implements CanLoad {\n * constructor(private permissions: Permissions, private currentUser: UserToken) {}\n *\n * canLoad(route: Route, segments: UrlSegment[]): Observable|Promise|boolean {\n * return this.permissions.canLoadChildren(this.currentUser, route, segments);\n * }\n * }\n * ```\n *\n * Here, the defined guard function is provided as part of the `Route` object\n * in the router configuration:\n *\n * ```\n *\n * @NgModule({\n * imports: [\n * RouterModule.forRoot([\n * {\n * path: 'team/:id',\n * component: TeamComponent,\n * loadChildren: () => import('./team').then(mod => mod.TeamModule),\n * canLoad: [CanLoadTeamSection]\n * }\n * ])\n * ],\n * providers: [CanLoadTeamSection, UserToken, Permissions]\n * })\n * class AppModule {}\n * ```\n *\n * @publicApi\n * @deprecated Use {@link CanMatch} instead\n */\nexport interface CanLoad {\n canLoad(route: Route, segments: UrlSegment[]): MaybeAsync;\n}\n\n/**\n * The signature of a function used as a `canLoad` guard on a `Route`.\n *\n * @publicApi\n * @see {@link CanLoad}\n * @see {@link Route}\n * @see {@link CanMatch}\n * @deprecated Use `Route.canMatch` and `CanMatchFn` instead\n */\nexport type CanLoadFn = (route: Route, segments: UrlSegment[]) => MaybeAsync;\n\n/**\n * @description\n *\n * Options that modify the `Router` navigation strategy.\n * Supply an object containing any of these properties to a `Router` navigation function to\n * control how the navigation should be handled.\n *\n * @see {@link Router#navigate}\n * @see {@link Router#navigateByUrl}\n * @see [Routing and Navigation guide](guide/routing/common-router-tasks)\n *\n * @publicApi\n */\nexport interface NavigationBehaviorOptions {\n /**\n * How to handle a navigation request to the current URL.\n *\n * This value is a subset of the options available in `OnSameUrlNavigation` and\n * will take precedence over the default value set for the `Router`.\n *\n * @see {@link OnSameUrlNavigation}\n * @see {@link RouterConfigOptions}\n */\n onSameUrlNavigation?: OnSameUrlNavigation;\n\n /**\n * When true, navigates without pushing a new state into history.\n *\n * ```\n * // Navigate silently to /view\n * this.router.navigate(['/view'], { skipLocationChange: true });\n * ```\n */\n skipLocationChange?: boolean;\n\n /**\n * When true, navigates while replacing the current state in history.\n *\n * ```\n * // Navigate to /view\n * this.router.navigate(['/view'], { replaceUrl: true });\n * ```\n */\n replaceUrl?: boolean;\n\n /**\n * Developer-defined state that can be passed to any navigation.\n * Access this value through the `Navigation.extras` object\n * returned from the [Router.getCurrentNavigation()\n * method](api/router/Router#getcurrentnavigation) while a navigation is executing.\n *\n * After a navigation completes, the router writes an object containing this\n * value together with a `navigationId` to `history.state`.\n * The value is written when `location.go()` or `location.replaceState()`\n * is called before activating this route.\n *\n * Note that `history.state` does not pass an object equality test because\n * the router adds the `navigationId` on each navigation.\n *\n */\n state?: {[k: string]: any};\n\n /**\n * Use this to convey transient information about this particular navigation, such as how it\n * happened. In this way, it's different from the persisted value `state` that will be set to\n * `history.state`. This object is assigned directly to the Router's current `Navigation`\n * (it is not copied or cloned), so it should be mutated with caution.\n *\n * One example of how this might be used is to trigger different single-page navigation animations\n * depending on how a certain route was reached. For example, consider a photo gallery app, where\n * you can reach the same photo URL and state via various routes:\n *\n * - Clicking on it in a gallery view\n * - Clicking\n * - \"next\" or \"previous\" when viewing another photo in the album\n * - Etc.\n *\n * Each of these wants a different animation at navigate time. This information doesn't make sense\n * to store in the persistent URL or history entry state, but it's still important to communicate\n * from the rest of the application, into the router.\n *\n * This information could be used in coordination with the View Transitions feature and the\n * `onViewTransitionCreated` callback. The information might be used in the callback to set\n * classes on the document in order to control the transition animations and remove the classes\n * when the transition has finished animating.\n */\n readonly info?: unknown;\n\n /**\n * When set, the Router will update the browser's address bar to match the given `UrlTree` instead\n * of the one used for route matching.\n *\n *\n * @usageNotes\n *\n * This feature is useful for redirects, such as redirecting to an error page, without changing\n * the value that will be displayed in the browser's address bar.\n *\n * ```\n * const canActivate: CanActivateFn = (route: ActivatedRouteSnapshot) => {\n * const userService = inject(UserService);\n * const router = inject(Router);\n * if (!userService.isLoggedIn()) {\n * const targetOfCurrentNavigation = router.getCurrentNavigation()?.finalUrl;\n * const redirect = router.parseUrl('/404');\n * return new RedirectCommand(redirect, {browserUrl: targetOfCurrentNavigation});\n * }\n * return true;\n * };\n * ```\n *\n * This value is used directly, without considering any `UrlHandingStrategy`. In this way,\n * `browserUrl` can also be used to use a different value for the browser URL than what would have\n * been produced by from the navigation due to `UrlHandlingStrategy.merge`.\n *\n * This value only affects the path presented in the browser's address bar. It does not apply to\n * the internal `Router` state. Information such as `params` and `data` will match the internal\n * state used to match routes which will be different from the browser URL when using this feature\n * The same is true when using other APIs that cause the browser URL the differ from the Router\n * state, such as `skipLocationChange`.\n */\n readonly browserUrl?: UrlTree | string;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {NavigationCancellationCode} from './events';\nimport {NavigationBehaviorOptions, RedirectCommand} from './models';\nimport {isUrlTree, UrlSerializer, UrlTree} from './url_tree';\n\nexport const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';\n\nexport type NavigationCancelingError = Error & {\n [NAVIGATION_CANCELING_ERROR]: true;\n cancellationCode: NavigationCancellationCode;\n};\nexport type RedirectingNavigationCancelingError = NavigationCancelingError & {\n url: UrlTree;\n navigationBehaviorOptions?: NavigationBehaviorOptions;\n cancellationCode: NavigationCancellationCode.Redirect;\n};\n\nexport function redirectingNavigationError(\n urlSerializer: UrlSerializer,\n redirect: UrlTree | RedirectCommand,\n): RedirectingNavigationCancelingError {\n const {redirectTo, navigationBehaviorOptions} = isUrlTree(redirect)\n ? {redirectTo: redirect, navigationBehaviorOptions: undefined}\n : redirect;\n const error = navigationCancelingError(\n ngDevMode && `Redirecting to \"${urlSerializer.serialize(redirectTo)}\"`,\n NavigationCancellationCode.Redirect,\n ) as RedirectingNavigationCancelingError;\n error.url = redirectTo;\n error.navigationBehaviorOptions = navigationBehaviorOptions;\n return error;\n}\n\nexport function navigationCancelingError(\n message: string | null | false,\n code: NavigationCancellationCode,\n) {\n const error = new Error(`NavigationCancelingError: ${message || ''}`) as NavigationCancelingError;\n error[NAVIGATION_CANCELING_ERROR] = true;\n error.cancellationCode = code;\n return error;\n}\n\nexport function isRedirectingNavigationCancelingError(\n error: unknown | RedirectingNavigationCancelingError,\n): error is RedirectingNavigationCancelingError {\n return (\n isNavigationCancelingError(error) &&\n isUrlTree((error as RedirectingNavigationCancelingError).url)\n );\n}\n\nexport function isNavigationCancelingError(error: unknown): error is NavigationCancelingError {\n return !!error && (error as NavigationCancelingError)[NAVIGATION_CANCELING_ERROR];\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {MonoTypeOperatorFunction} from 'rxjs';\nimport {map} from 'rxjs/operators';\n\nimport {ActivationEnd, ChildActivationEnd, Event} from '../events';\nimport {NavigationTransition} from '../navigation_transition';\nimport {DetachedRouteHandleInternal, RouteReuseStrategy} from '../route_reuse_strategy';\nimport {ChildrenOutletContexts} from '../router_outlet_context';\nimport {ActivatedRoute, advanceActivatedRoute, RouterState} from '../router_state';\nimport {getClosestRouteInjector} from '../utils/config';\nimport {nodeChildrenAsMap, TreeNode} from '../utils/tree';\n\nlet warnedAboutUnsupportedInputBinding = false;\n\nexport const activateRoutes = (\n rootContexts: ChildrenOutletContexts,\n routeReuseStrategy: RouteReuseStrategy,\n forwardEvent: (evt: Event) => void,\n inputBindingEnabled: boolean,\n): MonoTypeOperatorFunction =>\n map((t) => {\n new ActivateRoutes(\n routeReuseStrategy,\n t.targetRouterState!,\n t.currentRouterState,\n forwardEvent,\n inputBindingEnabled,\n ).activate(rootContexts);\n return t;\n });\n\nexport class ActivateRoutes {\n constructor(\n private routeReuseStrategy: RouteReuseStrategy,\n private futureState: RouterState,\n private currState: RouterState,\n private forwardEvent: (evt: Event) => void,\n private inputBindingEnabled: boolean,\n ) {}\n\n activate(parentContexts: ChildrenOutletContexts): void {\n const futureRoot = this.futureState._root;\n const currRoot = this.currState ? this.currState._root : null;\n\n this.deactivateChildRoutes(futureRoot, currRoot, parentContexts);\n advanceActivatedRoute(this.futureState.root);\n this.activateChildRoutes(futureRoot, currRoot, parentContexts);\n }\n\n // De-activate the child route that are not re-used for the future state\n private deactivateChildRoutes(\n futureNode: TreeNode,\n currNode: TreeNode | null,\n contexts: ChildrenOutletContexts,\n ): void {\n const children: {[outletName: string]: TreeNode} = nodeChildrenAsMap(currNode);\n\n // Recurse on the routes active in the future state to de-activate deeper children\n futureNode.children.forEach((futureChild) => {\n const childOutletName = futureChild.value.outlet;\n this.deactivateRoutes(futureChild, children[childOutletName], contexts);\n delete children[childOutletName];\n });\n\n // De-activate the routes that will not be re-used\n Object.values(children).forEach((v: TreeNode) => {\n this.deactivateRouteAndItsChildren(v, contexts);\n });\n }\n\n private deactivateRoutes(\n futureNode: TreeNode,\n currNode: TreeNode,\n parentContext: ChildrenOutletContexts,\n ): void {\n const future = futureNode.value;\n const curr = currNode ? currNode.value : null;\n\n if (future === curr) {\n // Reusing the node, check to see if the children need to be de-activated\n if (future.component) {\n // If we have a normal route, we need to go through an outlet.\n const context = parentContext.getContext(future.outlet);\n if (context) {\n this.deactivateChildRoutes(futureNode, currNode, context.children);\n }\n } else {\n // if we have a componentless route, we recurse but keep the same outlet map.\n this.deactivateChildRoutes(futureNode, currNode, parentContext);\n }\n } else {\n if (curr) {\n // Deactivate the current route which will not be re-used\n this.deactivateRouteAndItsChildren(currNode, parentContext);\n }\n }\n }\n\n private deactivateRouteAndItsChildren(\n route: TreeNode,\n parentContexts: ChildrenOutletContexts,\n ): void {\n // If there is no component, the Route is never attached to an outlet (because there is no\n // component to attach).\n if (route.value.component && this.routeReuseStrategy.shouldDetach(route.value.snapshot)) {\n this.detachAndStoreRouteSubtree(route, parentContexts);\n } else {\n this.deactivateRouteAndOutlet(route, parentContexts);\n }\n }\n\n private detachAndStoreRouteSubtree(\n route: TreeNode,\n parentContexts: ChildrenOutletContexts,\n ): void {\n const context = parentContexts.getContext(route.value.outlet);\n const contexts = context && route.value.component ? context.children : parentContexts;\n const children: {[outletName: string]: TreeNode} = nodeChildrenAsMap(route);\n\n for (const treeNode of Object.values(children)) {\n this.deactivateRouteAndItsChildren(treeNode, contexts);\n }\n\n if (context && context.outlet) {\n const componentRef = context.outlet.detach();\n const contexts = context.children.onOutletDeactivated();\n this.routeReuseStrategy.store(route.value.snapshot, {componentRef, route, contexts});\n }\n }\n\n private deactivateRouteAndOutlet(\n route: TreeNode,\n parentContexts: ChildrenOutletContexts,\n ): void {\n const context = parentContexts.getContext(route.value.outlet);\n // The context could be `null` if we are on a componentless route but there may still be\n // children that need deactivating.\n const contexts = context && route.value.component ? context.children : parentContexts;\n const children: {[outletName: string]: TreeNode} = nodeChildrenAsMap(route);\n\n for (const treeNode of Object.values(children)) {\n this.deactivateRouteAndItsChildren(treeNode, contexts);\n }\n\n if (context) {\n if (context.outlet) {\n // Destroy the component\n context.outlet.deactivate();\n // Destroy the contexts for all the outlets that were in the component\n context.children.onOutletDeactivated();\n }\n // Clear the information about the attached component on the context but keep the reference to\n // the outlet. Clear even if outlet was not yet activated to avoid activating later with old\n // info\n context.attachRef = null;\n context.route = null;\n }\n }\n\n private activateChildRoutes(\n futureNode: TreeNode,\n currNode: TreeNode | null,\n contexts: ChildrenOutletContexts,\n ): void {\n const children: {[outlet: string]: TreeNode} = nodeChildrenAsMap(currNode);\n futureNode.children.forEach((c) => {\n this.activateRoutes(c, children[c.value.outlet], contexts);\n this.forwardEvent(new ActivationEnd(c.value.snapshot));\n });\n if (futureNode.children.length) {\n this.forwardEvent(new ChildActivationEnd(futureNode.value.snapshot));\n }\n }\n\n private activateRoutes(\n futureNode: TreeNode,\n currNode: TreeNode,\n parentContexts: ChildrenOutletContexts,\n ): void {\n const future = futureNode.value;\n const curr = currNode ? currNode.value : null;\n\n advanceActivatedRoute(future);\n\n // reusing the node\n if (future === curr) {\n if (future.component) {\n // If we have a normal route, we need to go through an outlet.\n const context = parentContexts.getOrCreateContext(future.outlet);\n this.activateChildRoutes(futureNode, currNode, context.children);\n } else {\n // if we have a componentless route, we recurse but keep the same outlet map.\n this.activateChildRoutes(futureNode, currNode, parentContexts);\n }\n } else {\n if (future.component) {\n // if we have a normal route, we need to place the component into the outlet and recurse.\n const context = parentContexts.getOrCreateContext(future.outlet);\n\n if (this.routeReuseStrategy.shouldAttach(future.snapshot)) {\n const stored = (\n this.routeReuseStrategy.retrieve(future.snapshot)\n );\n this.routeReuseStrategy.store(future.snapshot, null);\n context.children.onOutletReAttached(stored.contexts);\n context.attachRef = stored.componentRef;\n context.route = stored.route.value;\n if (context.outlet) {\n // Attach right away when the outlet has already been instantiated\n // Otherwise attach from `RouterOutlet.ngOnInit` when it is instantiated\n context.outlet.attach(stored.componentRef, stored.route.value);\n }\n\n advanceActivatedRoute(stored.route.value);\n this.activateChildRoutes(futureNode, null, context.children);\n } else {\n context.attachRef = null;\n context.route = future;\n if (context.outlet) {\n // Activate the outlet when it has already been instantiated\n // Otherwise it will get activated from its `ngOnInit` when instantiated\n context.outlet.activateWith(future, context.injector);\n }\n\n this.activateChildRoutes(futureNode, null, context.children);\n }\n } else {\n // if we have a componentless route, we recurse but keep the same outlet map.\n this.activateChildRoutes(futureNode, null, parentContexts);\n }\n }\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n const context = parentContexts.getOrCreateContext(future.outlet);\n const outlet = context.outlet;\n if (\n outlet &&\n this.inputBindingEnabled &&\n !outlet.supportsBindingToComponentInputs &&\n !warnedAboutUnsupportedInputBinding\n ) {\n console.warn(\n `'withComponentInputBinding' feature is enabled but ` +\n `this application is using an outlet that may not support binding to component inputs.`,\n );\n warnedAboutUnsupportedInputBinding = true;\n }\n }\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Injector, ProviderToken, ɵisInjectable as isInjectable} from '@angular/core';\n\nimport {RunGuardsAndResolvers} from '../models';\nimport {ChildrenOutletContexts, OutletContext} from '../router_outlet_context';\nimport {\n ActivatedRouteSnapshot,\n equalParamsAndUrlSegments,\n RouterStateSnapshot,\n} from '../router_state';\nimport {equalPath} from '../url_tree';\nimport {shallowEqual} from '../utils/collection';\nimport {nodeChildrenAsMap, TreeNode} from '../utils/tree';\n\nexport class CanActivate {\n readonly route: ActivatedRouteSnapshot;\n constructor(public path: ActivatedRouteSnapshot[]) {\n this.route = this.path[this.path.length - 1];\n }\n}\n\nexport class CanDeactivate {\n constructor(\n public component: Object | null,\n public route: ActivatedRouteSnapshot,\n ) {}\n}\n\nexport declare type Checks = {\n canDeactivateChecks: CanDeactivate[];\n canActivateChecks: CanActivate[];\n};\n\nexport function getAllRouteGuards(\n future: RouterStateSnapshot,\n curr: RouterStateSnapshot,\n parentContexts: ChildrenOutletContexts,\n) {\n const futureRoot = future._root;\n const currRoot = curr ? curr._root : null;\n\n return getChildRouteGuards(futureRoot, currRoot, parentContexts, [futureRoot.value]);\n}\n\nexport function getCanActivateChild(\n p: ActivatedRouteSnapshot,\n): {node: ActivatedRouteSnapshot; guards: any[]} | null {\n const canActivateChild = p.routeConfig ? p.routeConfig.canActivateChild : null;\n if (!canActivateChild || canActivateChild.length === 0) return null;\n return {node: p, guards: canActivateChild};\n}\n\nexport function getTokenOrFunctionIdentity(\n tokenOrFunction: Function | ProviderToken,\n injector: Injector,\n): Function | T {\n const NOT_FOUND = Symbol();\n const result = injector.get(tokenOrFunction, NOT_FOUND);\n if (result === NOT_FOUND) {\n if (typeof tokenOrFunction === 'function' && !isInjectable(tokenOrFunction)) {\n // We think the token is just a function so return it as-is\n return tokenOrFunction;\n } else {\n // This will throw the not found error\n return injector.get(tokenOrFunction);\n }\n }\n return result as T;\n}\n\nfunction getChildRouteGuards(\n futureNode: TreeNode,\n currNode: TreeNode | null,\n contexts: ChildrenOutletContexts | null,\n futurePath: ActivatedRouteSnapshot[],\n checks: Checks = {\n canDeactivateChecks: [],\n canActivateChecks: [],\n },\n): Checks {\n const prevChildren = nodeChildrenAsMap(currNode);\n\n // Process the children of the future route\n futureNode.children.forEach((c) => {\n getRouteGuards(c, prevChildren[c.value.outlet], contexts, futurePath.concat([c.value]), checks);\n delete prevChildren[c.value.outlet];\n });\n\n // Process any children left from the current route (not active for the future route)\n Object.entries(prevChildren).forEach(([k, v]: [string, TreeNode]) =>\n deactivateRouteAndItsChildren(v, contexts!.getContext(k), checks),\n );\n\n return checks;\n}\n\nfunction getRouteGuards(\n futureNode: TreeNode,\n currNode: TreeNode,\n parentContexts: ChildrenOutletContexts | null,\n futurePath: ActivatedRouteSnapshot[],\n checks: Checks = {\n canDeactivateChecks: [],\n canActivateChecks: [],\n },\n): Checks {\n const future = futureNode.value;\n const curr = currNode ? currNode.value : null;\n const context = parentContexts ? parentContexts.getContext(futureNode.value.outlet) : null;\n\n // reusing the node\n if (curr && future.routeConfig === curr.routeConfig) {\n const shouldRun = shouldRunGuardsAndResolvers(\n curr,\n future,\n future.routeConfig!.runGuardsAndResolvers,\n );\n if (shouldRun) {\n checks.canActivateChecks.push(new CanActivate(futurePath));\n } else {\n // we need to set the data\n future.data = curr.data;\n future._resolvedData = curr._resolvedData;\n }\n\n // If we have a component, we need to go through an outlet.\n if (future.component) {\n getChildRouteGuards(\n futureNode,\n currNode,\n context ? context.children : null,\n futurePath,\n checks,\n );\n\n // if we have a componentless route, we recurse but keep the same outlet map.\n } else {\n getChildRouteGuards(futureNode, currNode, parentContexts, futurePath, checks);\n }\n\n if (shouldRun && context && context.outlet && context.outlet.isActivated) {\n checks.canDeactivateChecks.push(new CanDeactivate(context.outlet.component, curr));\n }\n } else {\n if (curr) {\n deactivateRouteAndItsChildren(currNode, context, checks);\n }\n\n checks.canActivateChecks.push(new CanActivate(futurePath));\n // If we have a component, we need to go through an outlet.\n if (future.component) {\n getChildRouteGuards(futureNode, null, context ? context.children : null, futurePath, checks);\n\n // if we have a componentless route, we recurse but keep the same outlet map.\n } else {\n getChildRouteGuards(futureNode, null, parentContexts, futurePath, checks);\n }\n }\n\n return checks;\n}\n\nfunction shouldRunGuardsAndResolvers(\n curr: ActivatedRouteSnapshot,\n future: ActivatedRouteSnapshot,\n mode: RunGuardsAndResolvers | undefined,\n): boolean {\n if (typeof mode === 'function') {\n return mode(curr, future);\n }\n switch (mode) {\n case 'pathParamsChange':\n return !equalPath(curr.url, future.url);\n\n case 'pathParamsOrQueryParamsChange':\n return (\n !equalPath(curr.url, future.url) || !shallowEqual(curr.queryParams, future.queryParams)\n );\n\n case 'always':\n return true;\n\n case 'paramsOrQueryParamsChange':\n return (\n !equalParamsAndUrlSegments(curr, future) ||\n !shallowEqual(curr.queryParams, future.queryParams)\n );\n\n case 'paramsChange':\n default:\n return !equalParamsAndUrlSegments(curr, future);\n }\n}\n\nfunction deactivateRouteAndItsChildren(\n route: TreeNode,\n context: OutletContext | null,\n checks: Checks,\n): void {\n const children = nodeChildrenAsMap(route);\n const r = route.value;\n\n Object.entries(children).forEach(([childName, node]) => {\n if (!r.component) {\n deactivateRouteAndItsChildren(node, context, checks);\n } else if (context) {\n deactivateRouteAndItsChildren(node, context.children.getContext(childName), checks);\n } else {\n deactivateRouteAndItsChildren(node, null, checks);\n }\n });\n\n if (!r.component) {\n checks.canDeactivateChecks.push(new CanDeactivate(null, r));\n } else if (context && context.outlet && context.outlet.isActivated) {\n checks.canDeactivateChecks.push(new CanDeactivate(context.outlet.component, r));\n } else {\n checks.canDeactivateChecks.push(new CanDeactivate(null, r));\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {EmptyError} from 'rxjs';\n\nimport {CanActivateChildFn, CanActivateFn, CanDeactivateFn, CanLoadFn, CanMatchFn} from '../models';\nimport {\n NAVIGATION_CANCELING_ERROR,\n NavigationCancelingError,\n RedirectingNavigationCancelingError,\n} from '../navigation_canceling_error';\nimport {isUrlTree} from '../url_tree';\n\n/**\n * Simple function check, but generic so type inference will flow. Example:\n *\n * function product(a: number, b: number) {\n * return a * b;\n * }\n *\n * if (isFunction(fn)) {\n * return fn(1, 2);\n * } else {\n * throw \"Must provide the `product` function\";\n * }\n */\nexport function isFunction(v: any): v is T {\n return typeof v === 'function';\n}\n\nexport function isBoolean(v: any): v is boolean {\n return typeof v === 'boolean';\n}\n\nexport function isCanLoad(guard: any): guard is {canLoad: CanLoadFn} {\n return guard && isFunction(guard.canLoad);\n}\n\nexport function isCanActivate(guard: any): guard is {canActivate: CanActivateFn} {\n return guard && isFunction(guard.canActivate);\n}\n\nexport function isCanActivateChild(guard: any): guard is {canActivateChild: CanActivateChildFn} {\n return guard && isFunction(guard.canActivateChild);\n}\n\nexport function isCanDeactivate(guard: any): guard is {canDeactivate: CanDeactivateFn} {\n return guard && isFunction>(guard.canDeactivate);\n}\nexport function isCanMatch(guard: any): guard is {canMatch: CanMatchFn} {\n return guard && isFunction(guard.canMatch);\n}\n\nexport function isEmptyError(e: Error): e is EmptyError {\n return e instanceof EmptyError || e?.name === 'EmptyError';\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {combineLatest, Observable, OperatorFunction} from 'rxjs';\nimport {filter, map, startWith, switchMap, take} from 'rxjs/operators';\n\nimport {GuardResult, RedirectCommand} from '../models';\nimport {isUrlTree, UrlTree} from '../url_tree';\n\nconst INITIAL_VALUE = /* @__PURE__ */ Symbol('INITIAL_VALUE');\ndeclare type INTERIM_VALUES = typeof INITIAL_VALUE | GuardResult;\n\nexport function prioritizedGuardValue(): OperatorFunction[], GuardResult> {\n return switchMap((obs) => {\n return combineLatest(\n obs.map((o) => o.pipe(take(1), startWith(INITIAL_VALUE as INTERIM_VALUES))),\n ).pipe(\n map((results: INTERIM_VALUES[]) => {\n for (const result of results) {\n if (result === true) {\n // If result is true, check the next one\n continue;\n } else if (result === INITIAL_VALUE) {\n // If guard has not finished, we need to stop processing.\n return INITIAL_VALUE;\n } else if (result === false || isRedirect(result)) {\n // Result finished and was not true. Return the result.\n // Note that we only allow false/UrlTree/RedirectCommand. Other values are considered invalid and\n // ignored.\n return result;\n }\n }\n // Everything resolved to true. Return true.\n return true;\n }),\n filter((item): item is GuardResult => item !== INITIAL_VALUE),\n take(1),\n );\n });\n}\n\nfunction isRedirect(val: INTERIM_VALUES): val is UrlTree | RedirectCommand {\n return isUrlTree(val) || val instanceof RedirectCommand;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {EnvironmentInjector, ProviderToken, runInInjectionContext} from '@angular/core';\nimport {\n concat,\n defer,\n from,\n MonoTypeOperatorFunction,\n Observable,\n of,\n OperatorFunction,\n pipe,\n} from 'rxjs';\nimport {concatMap, first, map, mergeMap, tap} from 'rxjs/operators';\n\nimport {ActivationStart, ChildActivationStart, Event} from '../events';\nimport {\n CanActivateChildFn,\n CanActivateFn,\n CanDeactivateFn,\n GuardResult,\n CanLoadFn,\n CanMatchFn,\n Route,\n} from '../models';\nimport {navigationCancelingError, redirectingNavigationError} from '../navigation_canceling_error';\nimport {NavigationTransition} from '../navigation_transition';\nimport {ActivatedRouteSnapshot, RouterStateSnapshot} from '../router_state';\nimport {isUrlTree, UrlSegment, UrlSerializer, UrlTree} from '../url_tree';\nimport {wrapIntoObservable} from '../utils/collection';\nimport {getClosestRouteInjector} from '../utils/config';\nimport {\n CanActivate,\n CanDeactivate,\n getCanActivateChild,\n getTokenOrFunctionIdentity,\n} from '../utils/preactivation';\nimport {\n isBoolean,\n isCanActivate,\n isCanActivateChild,\n isCanDeactivate,\n isCanLoad,\n isCanMatch,\n} from '../utils/type_guards';\n\nimport {prioritizedGuardValue} from './prioritized_guard_value';\n\nexport function checkGuards(\n injector: EnvironmentInjector,\n forwardEvent?: (evt: Event) => void,\n): MonoTypeOperatorFunction {\n return mergeMap((t) => {\n const {\n targetSnapshot,\n currentSnapshot,\n guards: {canActivateChecks, canDeactivateChecks},\n } = t;\n if (canDeactivateChecks.length === 0 && canActivateChecks.length === 0) {\n return of({...t, guardsResult: true});\n }\n\n return runCanDeactivateChecks(\n canDeactivateChecks,\n targetSnapshot!,\n currentSnapshot,\n injector,\n ).pipe(\n mergeMap((canDeactivate) => {\n return canDeactivate && isBoolean(canDeactivate)\n ? runCanActivateChecks(targetSnapshot!, canActivateChecks, injector, forwardEvent)\n : of(canDeactivate);\n }),\n map((guardsResult) => ({...t, guardsResult})),\n );\n });\n}\n\nfunction runCanDeactivateChecks(\n checks: CanDeactivate[],\n futureRSS: RouterStateSnapshot,\n currRSS: RouterStateSnapshot,\n injector: EnvironmentInjector,\n) {\n return from(checks).pipe(\n mergeMap((check) =>\n runCanDeactivate(check.component, check.route, currRSS, futureRSS, injector),\n ),\n first((result) => {\n return result !== true;\n }, true),\n );\n}\n\nfunction runCanActivateChecks(\n futureSnapshot: RouterStateSnapshot,\n checks: CanActivate[],\n injector: EnvironmentInjector,\n forwardEvent?: (evt: Event) => void,\n) {\n return from(checks).pipe(\n concatMap((check: CanActivate) => {\n return concat(\n fireChildActivationStart(check.route.parent, forwardEvent),\n fireActivationStart(check.route, forwardEvent),\n runCanActivateChild(futureSnapshot, check.path, injector),\n runCanActivate(futureSnapshot, check.route, injector),\n );\n }),\n first((result) => {\n return result !== true;\n }, true),\n );\n}\n\n/**\n * This should fire off `ActivationStart` events for each route being activated at this\n * level.\n * In other words, if you're activating `a` and `b` below, `path` will contain the\n * `ActivatedRouteSnapshot`s for both and we will fire `ActivationStart` for both. Always\n * return\n * `true` so checks continue to run.\n */\nfunction fireActivationStart(\n snapshot: ActivatedRouteSnapshot | null,\n forwardEvent?: (evt: Event) => void,\n): Observable {\n if (snapshot !== null && forwardEvent) {\n forwardEvent(new ActivationStart(snapshot));\n }\n return of(true);\n}\n\n/**\n * This should fire off `ChildActivationStart` events for each route being activated at this\n * level.\n * In other words, if you're activating `a` and `b` below, `path` will contain the\n * `ActivatedRouteSnapshot`s for both and we will fire `ChildActivationStart` for both. Always\n * return\n * `true` so checks continue to run.\n */\nfunction fireChildActivationStart(\n snapshot: ActivatedRouteSnapshot | null,\n forwardEvent?: (evt: Event) => void,\n): Observable {\n if (snapshot !== null && forwardEvent) {\n forwardEvent(new ChildActivationStart(snapshot));\n }\n return of(true);\n}\n\nfunction runCanActivate(\n futureRSS: RouterStateSnapshot,\n futureARS: ActivatedRouteSnapshot,\n injector: EnvironmentInjector,\n): Observable {\n const canActivate = futureARS.routeConfig ? futureARS.routeConfig.canActivate : null;\n if (!canActivate || canActivate.length === 0) return of(true);\n\n const canActivateObservables = canActivate.map(\n (canActivate: CanActivateFn | ProviderToken) => {\n return defer(() => {\n const closestInjector = getClosestRouteInjector(futureARS) ?? injector;\n const guard = getTokenOrFunctionIdentity(canActivate, closestInjector);\n const guardVal = isCanActivate(guard)\n ? guard.canActivate(futureARS, futureRSS)\n : runInInjectionContext(closestInjector, () =>\n (guard as CanActivateFn)(futureARS, futureRSS),\n );\n return wrapIntoObservable(guardVal).pipe(first());\n });\n },\n );\n return of(canActivateObservables).pipe(prioritizedGuardValue());\n}\n\nfunction runCanActivateChild(\n futureRSS: RouterStateSnapshot,\n path: ActivatedRouteSnapshot[],\n injector: EnvironmentInjector,\n): Observable {\n const futureARS = path[path.length - 1];\n\n const canActivateChildGuards = path\n .slice(0, path.length - 1)\n .reverse()\n .map((p) => getCanActivateChild(p))\n .filter((_) => _ !== null);\n\n const canActivateChildGuardsMapped = canActivateChildGuards.map((d: any) => {\n return defer(() => {\n const guardsMapped = d.guards.map(\n (canActivateChild: CanActivateChildFn | ProviderToken) => {\n const closestInjector = getClosestRouteInjector(d.node) ?? injector;\n const guard = getTokenOrFunctionIdentity<{canActivateChild: CanActivateChildFn}>(\n canActivateChild,\n closestInjector,\n );\n const guardVal = isCanActivateChild(guard)\n ? guard.canActivateChild(futureARS, futureRSS)\n : runInInjectionContext(closestInjector, () =>\n (guard as CanActivateChildFn)(futureARS, futureRSS),\n );\n return wrapIntoObservable(guardVal).pipe(first());\n },\n );\n return of(guardsMapped).pipe(prioritizedGuardValue());\n });\n });\n return of(canActivateChildGuardsMapped).pipe(prioritizedGuardValue());\n}\n\nfunction runCanDeactivate(\n component: Object | null,\n currARS: ActivatedRouteSnapshot,\n currRSS: RouterStateSnapshot,\n futureRSS: RouterStateSnapshot,\n injector: EnvironmentInjector,\n): Observable {\n const canDeactivate = currARS && currARS.routeConfig ? currARS.routeConfig.canDeactivate : null;\n if (!canDeactivate || canDeactivate.length === 0) return of(true);\n const canDeactivateObservables = canDeactivate.map((c: any) => {\n const closestInjector = getClosestRouteInjector(currARS) ?? injector;\n const guard = getTokenOrFunctionIdentity(c, closestInjector);\n const guardVal = isCanDeactivate(guard)\n ? guard.canDeactivate(component, currARS, currRSS, futureRSS)\n : runInInjectionContext(closestInjector, () =>\n (guard as CanDeactivateFn)(component, currARS, currRSS, futureRSS),\n );\n return wrapIntoObservable(guardVal).pipe(first());\n });\n return of(canDeactivateObservables).pipe(prioritizedGuardValue());\n}\n\nexport function runCanLoadGuards(\n injector: EnvironmentInjector,\n route: Route,\n segments: UrlSegment[],\n urlSerializer: UrlSerializer,\n): Observable {\n const canLoad = route.canLoad;\n if (canLoad === undefined || canLoad.length === 0) {\n return of(true);\n }\n\n const canLoadObservables = canLoad.map((injectionToken: any) => {\n const guard = getTokenOrFunctionIdentity(injectionToken, injector);\n const guardVal = isCanLoad(guard)\n ? guard.canLoad(route, segments)\n : runInInjectionContext(injector, () => (guard as CanLoadFn)(route, segments));\n return wrapIntoObservable(guardVal);\n });\n\n return of(canLoadObservables).pipe(prioritizedGuardValue(), redirectIfUrlTree(urlSerializer));\n}\n\nfunction redirectIfUrlTree(urlSerializer: UrlSerializer): OperatorFunction {\n return pipe(\n tap((result: GuardResult) => {\n if (typeof result === 'boolean') return;\n\n throw redirectingNavigationError(urlSerializer, result);\n }),\n map((result) => result === true),\n );\n}\n\nexport function runCanMatchGuards(\n injector: EnvironmentInjector,\n route: Route,\n segments: UrlSegment[],\n urlSerializer: UrlSerializer,\n): Observable {\n const canMatch = route.canMatch;\n if (!canMatch || canMatch.length === 0) return of(true);\n\n const canMatchObservables = canMatch.map((injectionToken) => {\n const guard = getTokenOrFunctionIdentity(injectionToken, injector);\n const guardVal = isCanMatch(guard)\n ? guard.canMatch(route, segments)\n : runInInjectionContext(injector, () => (guard as CanMatchFn)(route, segments));\n return wrapIntoObservable(guardVal);\n });\n\n return of(canMatchObservables).pipe(prioritizedGuardValue(), redirectIfUrlTree(urlSerializer));\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Injector, runInInjectionContext, ɵRuntimeError as RuntimeError} from '@angular/core';\nimport {Observable, of, throwError} from 'rxjs';\n\nimport {RuntimeErrorCode} from './errors';\nimport {NavigationCancellationCode} from './events';\nimport {LoadedRouterConfig, RedirectFunction, Route} from './models';\nimport {navigationCancelingError} from './navigation_canceling_error';\nimport {ActivatedRouteSnapshot} from './router_state';\nimport {Params, PRIMARY_OUTLET} from './shared';\nimport {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';\n\nexport class NoMatch {\n public segmentGroup: UrlSegmentGroup | null;\n\n constructor(segmentGroup?: UrlSegmentGroup) {\n this.segmentGroup = segmentGroup || null;\n }\n}\n\nexport class AbsoluteRedirect extends Error {\n constructor(public urlTree: UrlTree) {\n super();\n }\n}\n\nexport function noMatch(segmentGroup: UrlSegmentGroup): Observable {\n return throwError(new NoMatch(segmentGroup));\n}\n\nexport function absoluteRedirect(newTree: UrlTree): Observable {\n return throwError(new AbsoluteRedirect(newTree));\n}\n\nexport function namedOutletsRedirect(redirectTo: string): Observable {\n return throwError(\n new RuntimeError(\n RuntimeErrorCode.NAMED_OUTLET_REDIRECT,\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n `Only absolute redirects can have named outlets. redirectTo: '${redirectTo}'`,\n ),\n );\n}\n\nexport function canLoadFails(route: Route): Observable {\n return throwError(\n navigationCancelingError(\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n `Cannot load children because the guard of the route \"path: '${route.path}'\" returned false`,\n NavigationCancellationCode.GuardRejected,\n ),\n );\n}\n\nexport class ApplyRedirects {\n constructor(\n private urlSerializer: UrlSerializer,\n private urlTree: UrlTree,\n ) {}\n\n lineralizeSegments(route: Route, urlTree: UrlTree): Observable {\n let res: UrlSegment[] = [];\n let c = urlTree.root;\n while (true) {\n res = res.concat(c.segments);\n if (c.numberOfChildren === 0) {\n return of(res);\n }\n\n if (c.numberOfChildren > 1 || !c.children[PRIMARY_OUTLET]) {\n return namedOutletsRedirect(`${route.redirectTo!}`);\n }\n\n c = c.children[PRIMARY_OUTLET];\n }\n }\n\n applyRedirectCommands(\n segments: UrlSegment[],\n redirectTo: string | RedirectFunction,\n posParams: {[k: string]: UrlSegment},\n currentSnapshot: ActivatedRouteSnapshot,\n injector: Injector,\n ): UrlTree {\n if (typeof redirectTo !== 'string') {\n const redirectToFn = redirectTo;\n const {queryParams, fragment, routeConfig, url, outlet, params, data, title} =\n currentSnapshot;\n const newRedirect = runInInjectionContext(injector, () =>\n redirectToFn({params, data, queryParams, fragment, routeConfig, url, outlet, title}),\n );\n if (newRedirect instanceof UrlTree) {\n throw new AbsoluteRedirect(newRedirect);\n }\n\n redirectTo = newRedirect;\n }\n\n const newTree = this.applyRedirectCreateUrlTree(\n redirectTo,\n this.urlSerializer.parse(redirectTo),\n segments,\n posParams,\n );\n if (redirectTo[0] === '/') {\n throw new AbsoluteRedirect(newTree);\n }\n return newTree;\n }\n\n applyRedirectCreateUrlTree(\n redirectTo: string,\n urlTree: UrlTree,\n segments: UrlSegment[],\n posParams: {[k: string]: UrlSegment},\n ): UrlTree {\n const newRoot = this.createSegmentGroup(redirectTo, urlTree.root, segments, posParams);\n return new UrlTree(\n newRoot,\n this.createQueryParams(urlTree.queryParams, this.urlTree.queryParams),\n urlTree.fragment,\n );\n }\n\n createQueryParams(redirectToParams: Params, actualParams: Params): Params {\n const res: Params = {};\n Object.entries(redirectToParams).forEach(([k, v]) => {\n const copySourceValue = typeof v === 'string' && v[0] === ':';\n if (copySourceValue) {\n const sourceName = v.substring(1);\n res[k] = actualParams[sourceName];\n } else {\n res[k] = v;\n }\n });\n return res;\n }\n\n createSegmentGroup(\n redirectTo: string,\n group: UrlSegmentGroup,\n segments: UrlSegment[],\n posParams: {[k: string]: UrlSegment},\n ): UrlSegmentGroup {\n const updatedSegments = this.createSegments(redirectTo, group.segments, segments, posParams);\n\n let children: {[n: string]: UrlSegmentGroup} = {};\n Object.entries(group.children).forEach(([name, child]) => {\n children[name] = this.createSegmentGroup(redirectTo, child, segments, posParams);\n });\n\n return new UrlSegmentGroup(updatedSegments, children);\n }\n\n createSegments(\n redirectTo: string,\n redirectToSegments: UrlSegment[],\n actualSegments: UrlSegment[],\n posParams: {[k: string]: UrlSegment},\n ): UrlSegment[] {\n return redirectToSegments.map((s) =>\n s.path[0] === ':'\n ? this.findPosParam(redirectTo, s, posParams)\n : this.findOrReturn(s, actualSegments),\n );\n }\n\n findPosParam(\n redirectTo: string,\n redirectToUrlSegment: UrlSegment,\n posParams: {[k: string]: UrlSegment},\n ): UrlSegment {\n const pos = posParams[redirectToUrlSegment.path.substring(1)];\n if (!pos)\n throw new RuntimeError(\n RuntimeErrorCode.MISSING_REDIRECT,\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n `Cannot redirect to '${redirectTo}'. Cannot find '${redirectToUrlSegment.path}'.`,\n );\n return pos;\n }\n\n findOrReturn(redirectToUrlSegment: UrlSegment, actualSegments: UrlSegment[]): UrlSegment {\n let idx = 0;\n for (const s of actualSegments) {\n if (s.path === redirectToUrlSegment.path) {\n actualSegments.splice(idx);\n return s;\n }\n idx++;\n }\n return redirectToUrlSegment;\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {EnvironmentInjector} from '@angular/core';\nimport {Observable, of} from 'rxjs';\nimport {map} from 'rxjs/operators';\n\nimport {Route} from '../models';\nimport {runCanMatchGuards} from '../operators/check_guards';\nimport {defaultUrlMatcher, PRIMARY_OUTLET} from '../shared';\nimport {UrlSegment, UrlSegmentGroup, UrlSerializer} from '../url_tree';\n\nimport {last} from './collection';\nimport {getOrCreateRouteInjectorIfNeeded, getOutlet} from './config';\n\nexport interface MatchResult {\n matched: boolean;\n consumedSegments: UrlSegment[];\n remainingSegments: UrlSegment[];\n parameters: {[k: string]: string};\n positionalParamSegments: {[k: string]: UrlSegment};\n}\n\nconst noMatch: MatchResult = {\n matched: false,\n consumedSegments: [],\n remainingSegments: [],\n parameters: {},\n positionalParamSegments: {},\n};\n\nexport function matchWithChecks(\n segmentGroup: UrlSegmentGroup,\n route: Route,\n segments: UrlSegment[],\n injector: EnvironmentInjector,\n urlSerializer: UrlSerializer,\n): Observable {\n const result = match(segmentGroup, route, segments);\n if (!result.matched) {\n return of(result);\n }\n\n // Only create the Route's `EnvironmentInjector` if it matches the attempted\n // navigation\n injector = getOrCreateRouteInjectorIfNeeded(route, injector);\n return runCanMatchGuards(injector, route, segments, urlSerializer).pipe(\n map((v) => (v === true ? result : {...noMatch})),\n );\n}\n\nexport function match(\n segmentGroup: UrlSegmentGroup,\n route: Route,\n segments: UrlSegment[],\n): MatchResult {\n if (route.path === '**') {\n return createWildcardMatchResult(segments);\n }\n\n if (route.path === '') {\n if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || segments.length > 0)) {\n return {...noMatch};\n }\n\n return {\n matched: true,\n consumedSegments: [],\n remainingSegments: segments,\n parameters: {},\n positionalParamSegments: {},\n };\n }\n\n const matcher = route.matcher || defaultUrlMatcher;\n const res = matcher(segments, segmentGroup, route);\n if (!res) return {...noMatch};\n\n const posParams: {[n: string]: string} = {};\n Object.entries(res.posParams ?? {}).forEach(([k, v]) => {\n posParams[k] = v.path;\n });\n const parameters =\n res.consumed.length > 0\n ? {...posParams, ...res.consumed[res.consumed.length - 1].parameters}\n : posParams;\n\n return {\n matched: true,\n consumedSegments: res.consumed,\n remainingSegments: segments.slice(res.consumed.length),\n // TODO(atscott): investigate combining parameters and positionalParamSegments\n parameters,\n positionalParamSegments: res.posParams ?? {},\n };\n}\n\nfunction createWildcardMatchResult(segments: UrlSegment[]): MatchResult {\n return {\n matched: true,\n parameters: segments.length > 0 ? last(segments)!.parameters : {},\n consumedSegments: segments,\n remainingSegments: [],\n positionalParamSegments: {},\n };\n}\n\nexport function split(\n segmentGroup: UrlSegmentGroup,\n consumedSegments: UrlSegment[],\n slicedSegments: UrlSegment[],\n config: Route[],\n) {\n if (\n slicedSegments.length > 0 &&\n containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, config)\n ) {\n const s = new UrlSegmentGroup(\n consumedSegments,\n createChildrenForEmptyPaths(\n config,\n new UrlSegmentGroup(slicedSegments, segmentGroup.children),\n ),\n );\n return {segmentGroup: s, slicedSegments: []};\n }\n\n if (\n slicedSegments.length === 0 &&\n containsEmptyPathMatches(segmentGroup, slicedSegments, config)\n ) {\n const s = new UrlSegmentGroup(\n segmentGroup.segments,\n addEmptyPathsToChildrenIfNeeded(segmentGroup, slicedSegments, config, segmentGroup.children),\n );\n return {segmentGroup: s, slicedSegments};\n }\n\n const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children);\n return {segmentGroup: s, slicedSegments};\n}\n\nfunction addEmptyPathsToChildrenIfNeeded(\n segmentGroup: UrlSegmentGroup,\n slicedSegments: UrlSegment[],\n routes: Route[],\n children: {[name: string]: UrlSegmentGroup},\n): {[name: string]: UrlSegmentGroup} {\n const res: {[name: string]: UrlSegmentGroup} = {};\n for (const r of routes) {\n if (emptyPathMatch(segmentGroup, slicedSegments, r) && !children[getOutlet(r)]) {\n const s = new UrlSegmentGroup([], {});\n res[getOutlet(r)] = s;\n }\n }\n return {...children, ...res};\n}\n\nfunction createChildrenForEmptyPaths(\n routes: Route[],\n primarySegment: UrlSegmentGroup,\n): {[name: string]: UrlSegmentGroup} {\n const res: {[name: string]: UrlSegmentGroup} = {};\n res[PRIMARY_OUTLET] = primarySegment;\n\n for (const r of routes) {\n if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) {\n const s = new UrlSegmentGroup([], {});\n res[getOutlet(r)] = s;\n }\n }\n return res;\n}\n\nfunction containsEmptyPathMatchesWithNamedOutlets(\n segmentGroup: UrlSegmentGroup,\n slicedSegments: UrlSegment[],\n routes: Route[],\n): boolean {\n return routes.some(\n (r) => emptyPathMatch(segmentGroup, slicedSegments, r) && getOutlet(r) !== PRIMARY_OUTLET,\n );\n}\n\nfunction containsEmptyPathMatches(\n segmentGroup: UrlSegmentGroup,\n slicedSegments: UrlSegment[],\n routes: Route[],\n): boolean {\n return routes.some((r) => emptyPathMatch(segmentGroup, slicedSegments, r));\n}\n\nexport function emptyPathMatch(\n segmentGroup: UrlSegmentGroup,\n slicedSegments: UrlSegment[],\n r: Route,\n): boolean {\n if ((segmentGroup.hasChildren() || slicedSegments.length > 0) && r.pathMatch === 'full') {\n return false;\n }\n\n return r.path === '';\n}\n\nexport function noLeftoversInUrl(\n segmentGroup: UrlSegmentGroup,\n segments: UrlSegment[],\n outlet: string,\n): boolean {\n return segments.length === 0 && !segmentGroup.children[outlet];\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {EnvironmentInjector, Type, ɵRuntimeError as RuntimeError} from '@angular/core';\nimport {from, Observable, of} from 'rxjs';\nimport {\n catchError,\n concatMap,\n defaultIfEmpty,\n first,\n last,\n map,\n mergeMap,\n scan,\n switchMap,\n tap,\n} from 'rxjs/operators';\n\nimport {AbsoluteRedirect, ApplyRedirects, canLoadFails, noMatch, NoMatch} from './apply_redirects';\nimport {createUrlTreeFromSnapshot} from './create_url_tree';\nimport {RuntimeErrorCode} from './errors';\nimport {Data, LoadedRouterConfig, ResolveData, Route, Routes} from './models';\nimport {runCanLoadGuards} from './operators/check_guards';\nimport {RouterConfigLoader} from './router_config_loader';\nimport {\n ActivatedRouteSnapshot,\n getInherited,\n ParamsInheritanceStrategy,\n RouterStateSnapshot,\n} from './router_state';\nimport {PRIMARY_OUTLET} from './shared';\nimport {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';\nimport {getOutlet, sortByMatchingOutlets} from './utils/config';\nimport {\n emptyPathMatch,\n match,\n matchWithChecks,\n noLeftoversInUrl,\n split,\n} from './utils/config_matching';\nimport {TreeNode} from './utils/tree';\nimport {isEmptyError} from './utils/type_guards';\n\n/**\n * Class used to indicate there were no additional route config matches but that all segments of\n * the URL were consumed during matching so the route was URL matched. When this happens, we still\n * try to match child configs in case there are empty path children.\n */\nclass NoLeftoversInUrl {}\n\nexport function recognize(\n injector: EnvironmentInjector,\n configLoader: RouterConfigLoader,\n rootComponentType: Type | null,\n config: Routes,\n urlTree: UrlTree,\n urlSerializer: UrlSerializer,\n paramsInheritanceStrategy: ParamsInheritanceStrategy = 'emptyOnly',\n): Observable<{state: RouterStateSnapshot; tree: UrlTree}> {\n return new Recognizer(\n injector,\n configLoader,\n rootComponentType,\n config,\n urlTree,\n paramsInheritanceStrategy,\n urlSerializer,\n ).recognize();\n}\n\nconst MAX_ALLOWED_REDIRECTS = 31;\n\nexport class Recognizer {\n private applyRedirects = new ApplyRedirects(this.urlSerializer, this.urlTree);\n private absoluteRedirectCount = 0;\n allowRedirects = true;\n\n constructor(\n private injector: EnvironmentInjector,\n private configLoader: RouterConfigLoader,\n private rootComponentType: Type | null,\n private config: Routes,\n private urlTree: UrlTree,\n private paramsInheritanceStrategy: ParamsInheritanceStrategy,\n private readonly urlSerializer: UrlSerializer,\n ) {}\n\n private noMatchError(e: NoMatch): RuntimeError {\n return new RuntimeError(\n RuntimeErrorCode.NO_MATCH,\n typeof ngDevMode === 'undefined' || ngDevMode\n ? `Cannot match any routes. URL Segment: '${e.segmentGroup}'`\n : `'${e.segmentGroup}'`,\n );\n }\n\n recognize(): Observable<{state: RouterStateSnapshot; tree: UrlTree}> {\n const rootSegmentGroup = split(this.urlTree.root, [], [], this.config).segmentGroup;\n\n return this.match(rootSegmentGroup).pipe(\n map(({children, rootSnapshot}) => {\n const rootNode = new TreeNode(rootSnapshot, children);\n const routeState = new RouterStateSnapshot('', rootNode);\n const tree = createUrlTreeFromSnapshot(\n rootSnapshot,\n [],\n this.urlTree.queryParams,\n this.urlTree.fragment,\n );\n // https://github.com/angular/angular/issues/47307\n // Creating the tree stringifies the query params\n // We don't want to do this here so reassign them to the original.\n tree.queryParams = this.urlTree.queryParams;\n routeState.url = this.urlSerializer.serialize(tree);\n return {state: routeState, tree};\n }),\n );\n }\n\n private match(rootSegmentGroup: UrlSegmentGroup): Observable<{\n children: TreeNode[];\n rootSnapshot: ActivatedRouteSnapshot;\n }> {\n // Use Object.freeze to prevent readers of the Router state from modifying it outside\n // of a navigation, resulting in the router being out of sync with the browser.\n const rootSnapshot = new ActivatedRouteSnapshot(\n [],\n Object.freeze({}),\n Object.freeze({...this.urlTree.queryParams}),\n this.urlTree.fragment,\n Object.freeze({}),\n PRIMARY_OUTLET,\n this.rootComponentType,\n null,\n {},\n );\n return this.processSegmentGroup(\n this.injector,\n this.config,\n rootSegmentGroup,\n PRIMARY_OUTLET,\n rootSnapshot,\n ).pipe(\n map((children) => {\n return {children, rootSnapshot};\n }),\n catchError((e: any) => {\n if (e instanceof AbsoluteRedirect) {\n this.urlTree = e.urlTree;\n return this.match(e.urlTree.root);\n }\n if (e instanceof NoMatch) {\n throw this.noMatchError(e);\n }\n\n throw e;\n }),\n );\n }\n\n processSegmentGroup(\n injector: EnvironmentInjector,\n config: Route[],\n segmentGroup: UrlSegmentGroup,\n outlet: string,\n parentRoute: ActivatedRouteSnapshot,\n ): Observable[]> {\n if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {\n return this.processChildren(injector, config, segmentGroup, parentRoute);\n }\n\n return this.processSegment(\n injector,\n config,\n segmentGroup,\n segmentGroup.segments,\n outlet,\n true,\n parentRoute,\n ).pipe(map((child) => (child instanceof TreeNode ? [child] : [])));\n }\n\n /**\n * Matches every child outlet in the `segmentGroup` to a `Route` in the config. Returns `null` if\n * we cannot find a match for _any_ of the children.\n *\n * @param config - The `Routes` to match against\n * @param segmentGroup - The `UrlSegmentGroup` whose children need to be matched against the\n * config.\n */\n processChildren(\n injector: EnvironmentInjector,\n config: Route[],\n segmentGroup: UrlSegmentGroup,\n parentRoute: ActivatedRouteSnapshot,\n ): Observable[]> {\n // Expand outlets one at a time, starting with the primary outlet. We need to do it this way\n // because an absolute redirect from the primary outlet takes precedence.\n const childOutlets: string[] = [];\n for (const child of Object.keys(segmentGroup.children)) {\n if (child === 'primary') {\n childOutlets.unshift(child);\n } else {\n childOutlets.push(child);\n }\n }\n return from(childOutlets).pipe(\n concatMap((childOutlet) => {\n const child = segmentGroup.children[childOutlet];\n // Sort the config so that routes with outlets that match the one being activated\n // appear first, followed by routes for other outlets, which might match if they have\n // an empty path.\n const sortedConfig = sortByMatchingOutlets(config, childOutlet);\n return this.processSegmentGroup(injector, sortedConfig, child, childOutlet, parentRoute);\n }),\n scan((children, outletChildren) => {\n children.push(...outletChildren);\n return children;\n }),\n defaultIfEmpty(null as TreeNode[] | null),\n last(),\n mergeMap((children) => {\n if (children === null) return noMatch(segmentGroup);\n // Because we may have matched two outlets to the same empty path segment, we can have\n // multiple activated results for the same outlet. We should merge the children of\n // these results so the final return value is only one `TreeNode` per outlet.\n const mergedChildren = mergeEmptyPathMatches(children);\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n // This should really never happen - we are only taking the first match for each\n // outlet and merge the empty path matches.\n checkOutletNameUniqueness(mergedChildren);\n }\n sortActivatedRouteSnapshots(mergedChildren);\n return of(mergedChildren);\n }),\n );\n }\n\n processSegment(\n injector: EnvironmentInjector,\n routes: Route[],\n segmentGroup: UrlSegmentGroup,\n segments: UrlSegment[],\n outlet: string,\n allowRedirects: boolean,\n parentRoute: ActivatedRouteSnapshot,\n ): Observable | NoLeftoversInUrl> {\n return from(routes).pipe(\n concatMap((r) => {\n return this.processSegmentAgainstRoute(\n r._injector ?? injector,\n routes,\n r,\n segmentGroup,\n segments,\n outlet,\n allowRedirects,\n parentRoute,\n ).pipe(\n catchError((e: any) => {\n if (e instanceof NoMatch) {\n return of(null);\n }\n throw e;\n }),\n );\n }),\n first((x): x is TreeNode | NoLeftoversInUrl => !!x),\n catchError((e) => {\n if (isEmptyError(e)) {\n if (noLeftoversInUrl(segmentGroup, segments, outlet)) {\n return of(new NoLeftoversInUrl());\n }\n return noMatch(segmentGroup);\n }\n throw e;\n }),\n );\n }\n\n processSegmentAgainstRoute(\n injector: EnvironmentInjector,\n routes: Route[],\n route: Route,\n rawSegment: UrlSegmentGroup,\n segments: UrlSegment[],\n outlet: string,\n allowRedirects: boolean,\n parentRoute: ActivatedRouteSnapshot,\n ): Observable | NoLeftoversInUrl> {\n // We allow matches to empty paths when the outlets differ so we can match a url like `/(b:b)` to\n // a config like\n // * `{path: '', children: [{path: 'b', outlet: 'b'}]}`\n // or even\n // * `{path: '', outlet: 'a', children: [{path: 'b', outlet: 'b'}]`\n //\n // The exception here is when the segment outlet is for the primary outlet. This would\n // result in a match inside the named outlet because all children there are written as primary\n // outlets. So we need to prevent child named outlet matches in a url like `/b` in a config like\n // * `{path: '', outlet: 'x' children: [{path: 'b'}]}`\n // This should only match if the url is `/(x:b)`.\n if (\n getOutlet(route) !== outlet &&\n (outlet === PRIMARY_OUTLET || !emptyPathMatch(rawSegment, segments, route))\n ) {\n return noMatch(rawSegment);\n }\n\n if (route.redirectTo === undefined) {\n return this.matchSegmentAgainstRoute(\n injector,\n rawSegment,\n route,\n segments,\n outlet,\n parentRoute,\n );\n }\n\n if (this.allowRedirects && allowRedirects) {\n return this.expandSegmentAgainstRouteUsingRedirect(\n injector,\n rawSegment,\n routes,\n route,\n segments,\n outlet,\n parentRoute,\n );\n }\n\n return noMatch(rawSegment);\n }\n\n private expandSegmentAgainstRouteUsingRedirect(\n injector: EnvironmentInjector,\n segmentGroup: UrlSegmentGroup,\n routes: Route[],\n route: Route,\n segments: UrlSegment[],\n outlet: string,\n parentRoute: ActivatedRouteSnapshot,\n ): Observable | NoLeftoversInUrl> {\n const {matched, parameters, consumedSegments, positionalParamSegments, remainingSegments} =\n match(segmentGroup, route, segments);\n if (!matched) return noMatch(segmentGroup);\n\n // TODO(atscott): Move all of this under an if(ngDevMode) as a breaking change and allow stack\n // size exceeded in production\n if (typeof route.redirectTo === 'string' && route.redirectTo[0] === '/') {\n this.absoluteRedirectCount++;\n if (this.absoluteRedirectCount > MAX_ALLOWED_REDIRECTS) {\n if (ngDevMode) {\n throw new RuntimeError(\n RuntimeErrorCode.INFINITE_REDIRECT,\n `Detected possible infinite redirect when redirecting from '${this.urlTree}' to '${route.redirectTo}'.\\n` +\n `This is currently a dev mode only error but will become a` +\n ` call stack size exceeded error in production in a future major version.`,\n );\n }\n this.allowRedirects = false;\n }\n }\n const currentSnapshot = new ActivatedRouteSnapshot(\n segments,\n parameters,\n Object.freeze({...this.urlTree.queryParams}),\n this.urlTree.fragment,\n getData(route),\n getOutlet(route),\n route.component ?? route._loadedComponent ?? null,\n route,\n getResolve(route),\n );\n const inherited = getInherited(currentSnapshot, parentRoute, this.paramsInheritanceStrategy);\n currentSnapshot.params = Object.freeze(inherited.params);\n currentSnapshot.data = Object.freeze(inherited.data);\n const newTree = this.applyRedirects.applyRedirectCommands(\n consumedSegments,\n route.redirectTo!,\n positionalParamSegments,\n currentSnapshot,\n injector,\n );\n\n return this.applyRedirects.lineralizeSegments(route, newTree).pipe(\n mergeMap((newSegments: UrlSegment[]) => {\n return this.processSegment(\n injector,\n routes,\n segmentGroup,\n newSegments.concat(remainingSegments),\n outlet,\n false,\n parentRoute,\n );\n }),\n );\n }\n\n matchSegmentAgainstRoute(\n injector: EnvironmentInjector,\n rawSegment: UrlSegmentGroup,\n route: Route,\n segments: UrlSegment[],\n outlet: string,\n parentRoute: ActivatedRouteSnapshot,\n ): Observable> {\n const matchResult = matchWithChecks(rawSegment, route, segments, injector, this.urlSerializer);\n if (route.path === '**') {\n // Prior versions of the route matching algorithm would stop matching at the wildcard route.\n // We should investigate a better strategy for any existing children. Otherwise, these\n // child segments are silently dropped from the navigation.\n // https://github.com/angular/angular/issues/40089\n rawSegment.children = {};\n }\n\n return matchResult.pipe(\n switchMap((result) => {\n if (!result.matched) {\n return noMatch(rawSegment);\n }\n // If the route has an injector created from providers, we should start using that.\n injector = route._injector ?? injector;\n return this.getChildConfig(injector, route, segments).pipe(\n switchMap(({routes: childConfig}) => {\n const childInjector = route._loadedInjector ?? injector;\n\n const {parameters, consumedSegments, remainingSegments} = result;\n const snapshot = new ActivatedRouteSnapshot(\n consumedSegments,\n parameters,\n Object.freeze({...this.urlTree.queryParams}),\n this.urlTree.fragment,\n getData(route),\n getOutlet(route),\n route.component ?? route._loadedComponent ?? null,\n route,\n getResolve(route),\n );\n const inherited = getInherited(snapshot, parentRoute, this.paramsInheritanceStrategy);\n snapshot.params = Object.freeze(inherited.params);\n snapshot.data = Object.freeze(inherited.data);\n\n const {segmentGroup, slicedSegments} = split(\n rawSegment,\n consumedSegments,\n remainingSegments,\n childConfig,\n );\n\n if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {\n return this.processChildren(childInjector, childConfig, segmentGroup, snapshot).pipe(\n map((children) => {\n return new TreeNode(snapshot, children);\n }),\n );\n }\n\n if (childConfig.length === 0 && slicedSegments.length === 0) {\n return of(new TreeNode(snapshot, []));\n }\n\n const matchedOnOutlet = getOutlet(route) === outlet;\n // If we matched a config due to empty path match on a different outlet, we need to\n // continue passing the current outlet for the segment rather than switch to PRIMARY.\n // Note that we switch to primary when we have a match because outlet configs look like\n // this: {path: 'a', outlet: 'a', children: [\n // {path: 'b', component: B},\n // {path: 'c', component: C},\n // ]}\n // Notice that the children of the named outlet are configured with the primary outlet\n return this.processSegment(\n childInjector,\n childConfig,\n segmentGroup,\n slicedSegments,\n matchedOnOutlet ? PRIMARY_OUTLET : outlet,\n true,\n snapshot,\n ).pipe(\n map((child) => {\n return new TreeNode(snapshot, child instanceof TreeNode ? [child] : []);\n }),\n );\n }),\n );\n }),\n );\n }\n private getChildConfig(\n injector: EnvironmentInjector,\n route: Route,\n segments: UrlSegment[],\n ): Observable {\n if (route.children) {\n // The children belong to the same module\n return of({routes: route.children, injector});\n }\n\n if (route.loadChildren) {\n // lazy children belong to the loaded module\n if (route._loadedRoutes !== undefined) {\n return of({routes: route._loadedRoutes, injector: route._loadedInjector});\n }\n\n return runCanLoadGuards(injector, route, segments, this.urlSerializer).pipe(\n mergeMap((shouldLoadResult: boolean) => {\n if (shouldLoadResult) {\n return this.configLoader.loadChildren(injector, route).pipe(\n tap((cfg: LoadedRouterConfig) => {\n route._loadedRoutes = cfg.routes;\n route._loadedInjector = cfg.injector;\n }),\n );\n }\n return canLoadFails(route);\n }),\n );\n }\n\n return of({routes: [], injector});\n }\n}\n\nfunction sortActivatedRouteSnapshots(nodes: TreeNode[]): void {\n nodes.sort((a, b) => {\n if (a.value.outlet === PRIMARY_OUTLET) return -1;\n if (b.value.outlet === PRIMARY_OUTLET) return 1;\n return a.value.outlet.localeCompare(b.value.outlet);\n });\n}\n\nfunction hasEmptyPathConfig(node: TreeNode) {\n const config = node.value.routeConfig;\n return config && config.path === '';\n}\n\n/**\n * Finds `TreeNode`s with matching empty path route configs and merges them into `TreeNode` with\n * the children from each duplicate. This is necessary because different outlets can match a\n * single empty path route config and the results need to then be merged.\n */\nfunction mergeEmptyPathMatches(\n nodes: Array>,\n): Array> {\n const result: Array> = [];\n // The set of nodes which contain children that were merged from two duplicate empty path nodes.\n const mergedNodes: Set> = new Set();\n\n for (const node of nodes) {\n if (!hasEmptyPathConfig(node)) {\n result.push(node);\n continue;\n }\n\n const duplicateEmptyPathNode = result.find(\n (resultNode) => node.value.routeConfig === resultNode.value.routeConfig,\n );\n if (duplicateEmptyPathNode !== undefined) {\n duplicateEmptyPathNode.children.push(...node.children);\n mergedNodes.add(duplicateEmptyPathNode);\n } else {\n result.push(node);\n }\n }\n // For each node which has children from multiple sources, we need to recompute a new `TreeNode`\n // by also merging those children. This is necessary when there are multiple empty path configs\n // in a row. Put another way: whenever we combine children of two nodes, we need to also check\n // if any of those children can be combined into a single node as well.\n for (const mergedNode of mergedNodes) {\n const mergedChildren = mergeEmptyPathMatches(mergedNode.children);\n result.push(new TreeNode(mergedNode.value, mergedChildren));\n }\n return result.filter((n) => !mergedNodes.has(n));\n}\n\nfunction checkOutletNameUniqueness(nodes: TreeNode[]): void {\n const names: {[k: string]: ActivatedRouteSnapshot} = {};\n nodes.forEach((n) => {\n const routeWithSameOutletName = names[n.value.outlet];\n if (routeWithSameOutletName) {\n const p = routeWithSameOutletName.url.map((s) => s.toString()).join('/');\n const c = n.value.url.map((s) => s.toString()).join('/');\n throw new RuntimeError(\n RuntimeErrorCode.TWO_SEGMENTS_WITH_SAME_OUTLET,\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n `Two segments cannot have the same outlet name: '${p}' and '${c}'.`,\n );\n }\n names[n.value.outlet] = n.value;\n });\n}\n\nfunction getData(route: Route): Data {\n return route.data || {};\n}\n\nfunction getResolve(route: Route): ResolveData {\n return route.resolve || {};\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {EnvironmentInjector, Type} from '@angular/core';\nimport {MonoTypeOperatorFunction} from 'rxjs';\nimport {map, mergeMap} from 'rxjs/operators';\n\nimport {Route} from '../models';\nimport {NavigationTransition} from '../navigation_transition';\nimport {recognize as recognizeFn} from '../recognize';\nimport {RouterConfigLoader} from '../router_config_loader';\nimport {UrlSerializer} from '../url_tree';\n\nexport function recognize(\n injector: EnvironmentInjector,\n configLoader: RouterConfigLoader,\n rootComponentType: Type | null,\n config: Route[],\n serializer: UrlSerializer,\n paramsInheritanceStrategy: 'emptyOnly' | 'always',\n): MonoTypeOperatorFunction {\n return mergeMap((t) =>\n recognizeFn(\n injector,\n configLoader,\n rootComponentType,\n config,\n t.extractedUrl,\n serializer,\n paramsInheritanceStrategy,\n ).pipe(\n map(({state: targetSnapshot, tree: urlAfterRedirects}) => {\n return {...t, targetSnapshot, urlAfterRedirects};\n }),\n ),\n );\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {EnvironmentInjector, ProviderToken, runInInjectionContext} from '@angular/core';\nimport {EMPTY, from, MonoTypeOperatorFunction, Observable, of, throwError} from 'rxjs';\nimport {catchError, concatMap, first, map, mapTo, mergeMap, takeLast, tap} from 'rxjs/operators';\n\nimport {RedirectCommand, ResolveData} from '../models';\nimport {NavigationTransition} from '../navigation_transition';\nimport {\n ActivatedRouteSnapshot,\n getInherited,\n hasStaticTitle,\n RouterStateSnapshot,\n} from '../router_state';\nimport {RouteTitleKey} from '../shared';\nimport {getDataKeys, wrapIntoObservable} from '../utils/collection';\nimport {getClosestRouteInjector} from '../utils/config';\nimport {getTokenOrFunctionIdentity} from '../utils/preactivation';\nimport {isEmptyError} from '../utils/type_guards';\nimport {redirectingNavigationError} from '../navigation_canceling_error';\nimport {DefaultUrlSerializer} from '../url_tree';\n\nexport function resolveData(\n paramsInheritanceStrategy: 'emptyOnly' | 'always',\n injector: EnvironmentInjector,\n): MonoTypeOperatorFunction {\n return mergeMap((t) => {\n const {\n targetSnapshot,\n guards: {canActivateChecks},\n } = t;\n\n if (!canActivateChecks.length) {\n return of(t);\n }\n // Iterating a Set in javascript happens in insertion order so it is safe to use a `Set` to\n // preserve the correct order that the resolvers should run in.\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#description\n const routesWithResolversToRun = new Set(canActivateChecks.map((check) => check.route));\n const routesNeedingDataUpdates = new Set();\n for (const route of routesWithResolversToRun) {\n if (routesNeedingDataUpdates.has(route)) {\n continue;\n }\n // All children under the route with a resolver to run need to recompute inherited data.\n for (const newRoute of flattenRouteTree(route)) {\n routesNeedingDataUpdates.add(newRoute);\n }\n }\n let routesProcessed = 0;\n return from(routesNeedingDataUpdates).pipe(\n concatMap((route) => {\n if (routesWithResolversToRun.has(route)) {\n return runResolve(route, targetSnapshot!, paramsInheritanceStrategy, injector);\n } else {\n route.data = getInherited(route, route.parent, paramsInheritanceStrategy).resolve;\n return of(void 0);\n }\n }),\n tap(() => routesProcessed++),\n takeLast(1),\n mergeMap((_) => (routesProcessed === routesNeedingDataUpdates.size ? of(t) : EMPTY)),\n );\n });\n}\n\n/**\n * Returns the `ActivatedRouteSnapshot` tree as an array, using DFS to traverse the route tree.\n */\nfunction flattenRouteTree(route: ActivatedRouteSnapshot): ActivatedRouteSnapshot[] {\n const descendants = route.children.map((child) => flattenRouteTree(child)).flat();\n return [route, ...descendants];\n}\n\nfunction runResolve(\n futureARS: ActivatedRouteSnapshot,\n futureRSS: RouterStateSnapshot,\n paramsInheritanceStrategy: 'emptyOnly' | 'always',\n injector: EnvironmentInjector,\n) {\n const config = futureARS.routeConfig;\n const resolve = futureARS._resolve;\n if (config?.title !== undefined && !hasStaticTitle(config)) {\n resolve[RouteTitleKey] = config.title;\n }\n return resolveNode(resolve, futureARS, futureRSS, injector).pipe(\n map((resolvedData: any) => {\n futureARS._resolvedData = resolvedData;\n futureARS.data = getInherited(futureARS, futureARS.parent, paramsInheritanceStrategy).resolve;\n return null;\n }),\n );\n}\n\nfunction resolveNode(\n resolve: ResolveData,\n futureARS: ActivatedRouteSnapshot,\n futureRSS: RouterStateSnapshot,\n injector: EnvironmentInjector,\n): Observable {\n const keys = getDataKeys(resolve);\n if (keys.length === 0) {\n return of({});\n }\n const data: {[k: string | symbol]: any} = {};\n return from(keys).pipe(\n mergeMap((key) =>\n getResolver(resolve[key], futureARS, futureRSS, injector).pipe(\n first(),\n tap((value: any) => {\n if (value instanceof RedirectCommand) {\n throw redirectingNavigationError(new DefaultUrlSerializer(), value);\n }\n data[key] = value;\n }),\n ),\n ),\n takeLast(1),\n mapTo(data),\n catchError((e: unknown) => (isEmptyError(e as Error) ? EMPTY : throwError(e))),\n );\n}\n\nfunction getResolver(\n injectionToken: ProviderToken | Function,\n futureARS: ActivatedRouteSnapshot,\n futureRSS: RouterStateSnapshot,\n injector: EnvironmentInjector,\n): Observable {\n const closestInjector = getClosestRouteInjector(futureARS) ?? injector;\n const resolver = getTokenOrFunctionIdentity(injectionToken, closestInjector);\n const resolverValue = resolver.resolve\n ? resolver.resolve(futureARS, futureRSS)\n : runInInjectionContext(closestInjector, () => resolver(futureARS, futureRSS));\n return wrapIntoObservable(resolverValue);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {from, MonoTypeOperatorFunction, ObservableInput, of} from 'rxjs';\nimport {map, switchMap} from 'rxjs/operators';\n\n/**\n * Perform a side effect through a switchMap for every emission on the source Observable,\n * but return an Observable that is identical to the source. It's essentially the same as\n * the `tap` operator, but if the side effectful `next` function returns an ObservableInput,\n * it will wait before continuing with the original value.\n */\nexport function switchTap(\n next: (x: T) => void | ObservableInput,\n): MonoTypeOperatorFunction {\n return switchMap((v) => {\n const nextResult = next(v);\n if (nextResult) {\n return from(nextResult).pipe(map(() => v));\n }\n return of(v);\n });\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {inject, Injectable} from '@angular/core';\nimport {Title} from '@angular/platform-browser';\n\nimport {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';\nimport {PRIMARY_OUTLET, RouteTitleKey} from './shared';\n\n/**\n * Provides a strategy for setting the page title after a router navigation.\n *\n * The built-in implementation traverses the router state snapshot and finds the deepest primary\n * outlet with `title` property. Given the `Routes` below, navigating to\n * `/base/child(popup:aux)` would result in the document title being set to \"child\".\n * ```\n * [\n * {path: 'base', title: 'base', children: [\n * {path: 'child', title: 'child'},\n * ],\n * {path: 'aux', outlet: 'popup', title: 'popupTitle'}\n * ]\n * ```\n *\n * This class can be used as a base class for custom title strategies. That is, you can create your\n * own class that extends the `TitleStrategy`. Note that in the above example, the `title`\n * from the named outlet is never used. However, a custom strategy might be implemented to\n * incorporate titles in named outlets.\n *\n * @publicApi\n * @see [Page title guide](guide/routing/common-router-tasks#setting-the-page-title)\n */\n@Injectable({providedIn: 'root', useFactory: () => inject(DefaultTitleStrategy)})\nexport abstract class TitleStrategy {\n /** Performs the application title update. */\n abstract updateTitle(snapshot: RouterStateSnapshot): void;\n\n /**\n * @returns The `title` of the deepest primary route.\n */\n buildTitle(snapshot: RouterStateSnapshot): string | undefined {\n let pageTitle: string | undefined;\n let route: ActivatedRouteSnapshot | undefined = snapshot.root;\n while (route !== undefined) {\n pageTitle = this.getResolvedTitleForRoute(route) ?? pageTitle;\n route = route.children.find((child) => child.outlet === PRIMARY_OUTLET);\n }\n return pageTitle;\n }\n\n /**\n * Given an `ActivatedRouteSnapshot`, returns the final value of the\n * `Route.title` property, which can either be a static string or a resolved value.\n */\n getResolvedTitleForRoute(snapshot: ActivatedRouteSnapshot) {\n return snapshot.data[RouteTitleKey];\n }\n}\n\n/**\n * The default `TitleStrategy` used by the router that updates the title using the `Title` service.\n */\n@Injectable({providedIn: 'root'})\nexport class DefaultTitleStrategy extends TitleStrategy {\n constructor(readonly title: Title) {\n super();\n }\n\n /**\n * Sets the title of the browser to the given value.\n *\n * @param title The `pageTitle` from the deepest primary route.\n */\n override updateTitle(snapshot: RouterStateSnapshot): void {\n const title = this.buildTitle(snapshot);\n if (title !== undefined) {\n this.title.setTitle(title);\n }\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {InjectionToken} from '@angular/core';\n\nimport {OnSameUrlNavigation, QueryParamsHandling} from './models';\n\n/**\n * Error handler that is invoked when a navigation error occurs.\n *\n * If the handler returns a value, the navigation Promise is resolved with this value.\n * If the handler throws an exception, the navigation Promise is rejected with\n * the exception.\n *\n * @publicApi\n * @deprecated Subscribe to the `Router` events and watch for `NavigationError` instead.\n * If the ErrorHandler is used to prevent unhandled promise rejections when navigation\n * errors occur, use the `resolveNavigationPromiseOnError` option instead.\n *\n * @see RouterConfigOptions\n */\nexport type ErrorHandler = (error: any) => any;\n\n/**\n * Allowed values in an `ExtraOptions` object that configure\n * when the router performs the initial navigation operation.\n *\n * * 'enabledNonBlocking' - (default) The initial navigation starts after the\n * root component has been created. The bootstrap is not blocked on the completion of the initial\n * navigation.\n * * 'enabledBlocking' - The initial navigation starts before the root component is created.\n * The bootstrap is blocked until the initial navigation is complete. This value should be set in\n * case you use [server-side rendering](guide/ssr), but do not enable [hydration](guide/hydration)\n * for your application.\n * * 'disabled' - The initial navigation is not performed. The location listener is set up before\n * the root component gets created. Use if there is a reason to have\n * more control over when the router starts its initial navigation due to some complex\n * initialization logic.\n *\n * @see {@link forRoot()}\n *\n * @publicApi\n */\nexport type InitialNavigation = 'disabled' | 'enabledBlocking' | 'enabledNonBlocking';\n\n/**\n * Extra configuration options that can be used with the `withRouterConfig` function.\n *\n * @publicApi\n */\nexport interface RouterConfigOptions {\n /**\n * Configures how the Router attempts to restore state when a navigation is cancelled.\n *\n * 'replace' - Always uses `location.replaceState` to set the browser state to the state of the\n * router before the navigation started. This means that if the URL of the browser is updated\n * _before_ the navigation is canceled, the Router will simply replace the item in history rather\n * than trying to restore to the previous location in the session history. This happens most\n * frequently with `urlUpdateStrategy: 'eager'` and navigations with the browser back/forward\n * buttons.\n *\n * 'computed' - Will attempt to return to the same index in the session history that corresponds\n * to the Angular route when the navigation gets cancelled. For example, if the browser back\n * button is clicked and the navigation is cancelled, the Router will trigger a forward navigation\n * and vice versa.\n *\n * Note: the 'computed' option is incompatible with any `UrlHandlingStrategy` which only\n * handles a portion of the URL because the history restoration navigates to the previous place in\n * the browser history rather than simply resetting a portion of the URL.\n *\n * The default value is `replace` when not set.\n */\n canceledNavigationResolution?: 'replace' | 'computed';\n\n /**\n * Configures the default for handling a navigation request to the current URL.\n *\n * If unset, the `Router` will use `'ignore'`.\n *\n * @see {@link OnSameUrlNavigation}\n */\n onSameUrlNavigation?: OnSameUrlNavigation;\n\n /**\n * Defines how the router merges parameters, data, and resolved data from parent to child\n * routes.\n *\n * By default ('emptyOnly'), a route inherits the parent route's parameters when the route itself\n * has an empty path (meaning its configured with path: '') or when the parent route doesn't have\n * any component set.\n *\n * Set to 'always' to enable unconditional inheritance of parent parameters.\n *\n * Note that when dealing with matrix parameters, \"parent\" refers to the parent `Route`\n * config which does not necessarily mean the \"URL segment to the left\". When the `Route` `path`\n * contains multiple segments, the matrix parameters must appear on the last segment. For example,\n * matrix parameters for `{path: 'a/b', component: MyComp}` should appear as `a/b;foo=bar` and not\n * `a;foo=bar/b`.\n *\n */\n paramsInheritanceStrategy?: 'emptyOnly' | 'always';\n\n /**\n * Defines when the router updates the browser URL. By default ('deferred'),\n * update after successful navigation.\n * Set to 'eager' if prefer to update the URL at the beginning of navigation.\n * Updating the URL early allows you to handle a failure of navigation by\n * showing an error message with the URL that failed.\n */\n urlUpdateStrategy?: 'deferred' | 'eager';\n\n /**\n * The default strategy to use for handling query params in `Router.createUrlTree` when one is not provided.\n *\n * The `createUrlTree` method is used internally by `Router.navigate` and `RouterLink`.\n * Note that `QueryParamsHandling` does not apply to `Router.navigateByUrl`.\n *\n * When neither the default nor the queryParamsHandling option is specified in the call to `createUrlTree`,\n * the current parameters will be replaced by new parameters.\n *\n * @see {@link Router#createUrlTree}\n * @see {@link QueryParamsHandling}\n */\n defaultQueryParamsHandling?: QueryParamsHandling;\n\n /**\n * When `true`, the `Promise` will instead resolve with `false`, as it does with other failed\n * navigations (for example, when guards are rejected).\n\n * Otherwise the `Promise` returned by the Router's navigation with be rejected\n * if an error occurs.\n */\n resolveNavigationPromiseOnError?: boolean;\n}\n\n/**\n * Configuration options for the scrolling feature which can be used with `withInMemoryScrolling`\n * function.\n *\n * @publicApi\n */\nexport interface InMemoryScrollingOptions {\n /**\n * When set to 'enabled', scrolls to the anchor element when the URL has a fragment.\n * Anchor scrolling is disabled by default.\n *\n * Anchor scrolling does not happen on 'popstate'. Instead, we restore the position\n * that we stored or scroll to the top.\n */\n anchorScrolling?: 'disabled' | 'enabled';\n\n /**\n * Configures if the scroll position needs to be restored when navigating back.\n *\n * * 'disabled'- (Default) Does nothing. Scroll position is maintained on navigation.\n * * 'top'- Sets the scroll position to x = 0, y = 0 on all navigation.\n * * 'enabled'- Restores the previous scroll position on backward navigation, else sets the\n * position to the anchor if one is provided, or sets the scroll position to [0, 0] (forward\n * navigation). This option will be the default in the future.\n *\n * You can implement custom scroll restoration behavior by adapting the enabled behavior as\n * in the following example.\n *\n * ```typescript\n * class AppComponent {\n * movieData: any;\n *\n * constructor(private router: Router, private viewportScroller: ViewportScroller,\n * changeDetectorRef: ChangeDetectorRef) {\n * router.events.pipe(filter((event: Event): event is Scroll => event instanceof Scroll)\n * ).subscribe(e => {\n * fetch('http://example.com/movies.json').then(response => {\n * this.movieData = response.json();\n * // update the template with the data before restoring scroll\n * changeDetectorRef.detectChanges();\n *\n * if (e.position) {\n * viewportScroller.scrollToPosition(e.position);\n * }\n * });\n * });\n * }\n * }\n * ```\n */\n scrollPositionRestoration?: 'disabled' | 'enabled' | 'top';\n}\n\n/**\n * A set of configuration options for a router module, provided in the\n * `forRoot()` method.\n *\n * @see {@link forRoot()}\n *\n *\n * @publicApi\n */\nexport interface ExtraOptions extends InMemoryScrollingOptions, RouterConfigOptions {\n /**\n * When true, log all internal navigation events to the console.\n * Use for debugging.\n */\n enableTracing?: boolean;\n\n /**\n * When true, enable the location strategy that uses the URL fragment\n * instead of the history API.\n */\n useHash?: boolean;\n\n /**\n * One of `enabled`, `enabledBlocking`, `enabledNonBlocking` or `disabled`.\n * When set to `enabled` or `enabledBlocking`, the initial navigation starts before the root\n * component is created. The bootstrap is blocked until the initial navigation is complete. This\n * value should be set in case you use [server-side rendering](guide/ssr), but do not enable\n * [hydration](guide/hydration) for your application. When set to `enabledNonBlocking`,\n * the initial navigation starts after the root component has been created.\n * The bootstrap is not blocked on the completion of the initial navigation. When set to\n * `disabled`, the initial navigation is not performed. The location listener is set up before the\n * root component gets created. Use if there is a reason to have more control over when the router\n * starts its initial navigation due to some complex initialization logic.\n */\n initialNavigation?: InitialNavigation;\n\n /**\n * When true, enables binding information from the `Router` state directly to the inputs of the\n * component in `Route` configurations.\n */\n bindToComponentInputs?: boolean;\n\n /**\n * When true, enables view transitions in the Router by running the route activation and\n * deactivation inside of `document.startViewTransition`.\n *\n * @see https://developer.chrome.com/docs/web-platform/view-transitions/\n * @see https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API\n * @experimental\n */\n enableViewTransitions?: boolean;\n\n /**\n * A custom error handler for failed navigations.\n * If the handler returns a value, the navigation Promise is resolved with this value.\n * If the handler throws an exception, the navigation Promise is rejected with the exception.\n *\n * @deprecated Subscribe to the `Router` events and watch for `NavigationError` instead.\n * If the ErrorHandler is used to prevent unhandled promise rejections when navigation\n * errors occur, use the `resolveNavigationPromiseOnError` option instead.\n *\n * @see RouterConfigOptions\n */\n errorHandler?: (error: any) => any;\n\n /**\n * Configures a preloading strategy.\n * One of `PreloadAllModules` or `NoPreloading` (the default).\n */\n preloadingStrategy?: any;\n\n /**\n * Configures the scroll offset the router will use when scrolling to an element.\n *\n * When given a tuple with x and y position value,\n * the router uses that offset each time it scrolls.\n * When given a function, the router invokes the function every time\n * it restores scroll position.\n */\n scrollOffset?: [number, number] | (() => [number, number]);\n}\n\n/**\n * A DI token for the router service.\n *\n * @publicApi\n */\nexport const ROUTER_CONFIGURATION = new InjectionToken(\n typeof ngDevMode === 'undefined' || ngDevMode ? 'router config' : '',\n {\n providedIn: 'root',\n factory: () => ({}),\n },\n);\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Component} from '@angular/core';\n\nimport {RouterOutlet} from '../directives/router_outlet';\nimport {PRIMARY_OUTLET} from '../shared';\nimport {Route} from '../models';\nexport {ɵEmptyOutletComponent as EmptyOutletComponent};\n\n/**\n * This component is used internally within the router to be a placeholder when an empty\n * router-outlet is needed. For example, with a config such as:\n *\n * `{path: 'parent', outlet: 'nav', children: [...]}`\n *\n * In order to render, there needs to be a component on this config, which will default\n * to this `EmptyOutletComponent`.\n */\n@Component({\n template: ` `,\n imports: [RouterOutlet],\n standalone: true,\n})\nexport class ɵEmptyOutletComponent {}\n\n/**\n * Makes a copy of the config and adds any default required properties.\n */\nexport function standardizeConfig(r: Route): Route {\n const children = r.children && r.children.map(standardizeConfig);\n const c = children ? {...r, children} : {...r};\n if (\n !c.component &&\n !c.loadComponent &&\n (children || c.loadChildren) &&\n c.outlet &&\n c.outlet !== PRIMARY_OUTLET\n ) {\n c.component = ɵEmptyOutletComponent;\n }\n return c;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n Compiler,\n EnvironmentInjector,\n inject,\n Injectable,\n InjectionToken,\n Injector,\n NgModuleFactory,\n Type,\n} from '@angular/core';\nimport {ConnectableObservable, from, Observable, of, Subject} from 'rxjs';\nimport {finalize, map, mergeMap, refCount, tap} from 'rxjs/operators';\n\nimport {DefaultExport, LoadedRouterConfig, Route, Routes} from './models';\nimport {wrapIntoObservable} from './utils/collection';\nimport {assertStandalone, validateConfig} from './utils/config';\nimport {standardizeConfig} from './components/empty_outlet';\n\n/**\n * The DI token for a router configuration.\n *\n * `ROUTES` is a low level API for router configuration via dependency injection.\n *\n * We recommend that in almost all cases to use higher level APIs such as `RouterModule.forRoot()`,\n * `provideRouter`, or `Router.resetConfig()`.\n *\n * @publicApi\n */\nexport const ROUTES = new InjectionToken(ngDevMode ? 'ROUTES' : '');\n\ntype ComponentLoader = Observable>;\n\n@Injectable({providedIn: 'root'})\nexport class RouterConfigLoader {\n private componentLoaders = new WeakMap();\n private childrenLoaders = new WeakMap>();\n onLoadStartListener?: (r: Route) => void;\n onLoadEndListener?: (r: Route) => void;\n private readonly compiler = inject(Compiler);\n\n loadComponent(route: Route): Observable> {\n if (this.componentLoaders.get(route)) {\n return this.componentLoaders.get(route)!;\n } else if (route._loadedComponent) {\n return of(route._loadedComponent);\n }\n\n if (this.onLoadStartListener) {\n this.onLoadStartListener(route);\n }\n const loadRunner = wrapIntoObservable(route.loadComponent!()).pipe(\n map(maybeUnwrapDefaultExport),\n tap((component) => {\n if (this.onLoadEndListener) {\n this.onLoadEndListener(route);\n }\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n assertStandalone(route.path ?? '', component);\n route._loadedComponent = component;\n }),\n finalize(() => {\n this.componentLoaders.delete(route);\n }),\n );\n // Use custom ConnectableObservable as share in runners pipe increasing the bundle size too much\n const loader = new ConnectableObservable(loadRunner, () => new Subject>()).pipe(\n refCount(),\n );\n this.componentLoaders.set(route, loader);\n return loader;\n }\n\n loadChildren(parentInjector: Injector, route: Route): Observable {\n if (this.childrenLoaders.get(route)) {\n return this.childrenLoaders.get(route)!;\n } else if (route._loadedRoutes) {\n return of({routes: route._loadedRoutes, injector: route._loadedInjector});\n }\n\n if (this.onLoadStartListener) {\n this.onLoadStartListener(route);\n }\n const moduleFactoryOrRoutes$ = loadChildren(\n route,\n this.compiler,\n parentInjector,\n this.onLoadEndListener,\n );\n const loadRunner = moduleFactoryOrRoutes$.pipe(\n finalize(() => {\n this.childrenLoaders.delete(route);\n }),\n );\n // Use custom ConnectableObservable as share in runners pipe increasing the bundle size too much\n const loader = new ConnectableObservable(\n loadRunner,\n () => new Subject(),\n ).pipe(refCount());\n this.childrenLoaders.set(route, loader);\n return loader;\n }\n}\n\n/**\n * Executes a `route.loadChildren` callback and converts the result to an array of child routes and\n * an injector if that callback returned a module.\n *\n * This function is used for the route discovery during prerendering\n * in @angular-devkit/build-angular. If there are any updates to the contract here, it will require\n * an update to the extractor.\n */\nexport function loadChildren(\n route: Route,\n compiler: Compiler,\n parentInjector: Injector,\n onLoadEndListener?: (r: Route) => void,\n): Observable {\n return wrapIntoObservable(route.loadChildren!()).pipe(\n map(maybeUnwrapDefaultExport),\n mergeMap((t) => {\n if (t instanceof NgModuleFactory || Array.isArray(t)) {\n return of(t);\n } else {\n return from(compiler.compileModuleAsync(t));\n }\n }),\n map((factoryOrRoutes: NgModuleFactory | Routes) => {\n if (onLoadEndListener) {\n onLoadEndListener(route);\n }\n // This injector comes from the `NgModuleRef` when lazy loading an `NgModule`. There is\n // no injector associated with lazy loading a `Route` array.\n let injector: EnvironmentInjector | undefined;\n let rawRoutes: Route[];\n let requireStandaloneComponents = false;\n if (Array.isArray(factoryOrRoutes)) {\n rawRoutes = factoryOrRoutes;\n requireStandaloneComponents = true;\n } else {\n injector = factoryOrRoutes.create(parentInjector).injector;\n // When loading a module that doesn't provide `RouterModule.forChild()` preloader\n // will get stuck in an infinite loop. The child module's Injector will look to\n // its parent `Injector` when it doesn't find any ROUTES so it will return routes\n // for it's parent module instead.\n rawRoutes = injector.get(ROUTES, [], {optional: true, self: true}).flat();\n }\n const routes = rawRoutes.map(standardizeConfig);\n (typeof ngDevMode === 'undefined' || ngDevMode) &&\n validateConfig(routes, route.path, requireStandaloneComponents);\n return {routes, injector};\n }),\n );\n}\n\nfunction isWrappedDefaultExport(value: T | DefaultExport): value is DefaultExport {\n // We use `in` here with a string key `'default'`, because we expect `DefaultExport` objects to be\n // dynamically imported ES modules with a spec-mandated `default` key. Thus we don't expect that\n // `default` will be a renamed property.\n return value && typeof value === 'object' && 'default' in value;\n}\n\nfunction maybeUnwrapDefaultExport