@angular/router 14.0.1 → 14.1.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,14 @@
1
1
  /**
2
- * @license Angular v14.0.1
2
+ * @license Angular v14.1.0-next.0
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
6
6
 
7
- import * as i3 from '@angular/common';
8
- import { Location, LocationStrategy, PlatformLocation, APP_BASE_HREF, ViewportScroller, HashLocationStrategy, PathLocationStrategy, LOCATION_INITIALIZED } from '@angular/common';
9
7
  import * as i0 from '@angular/core';
10
8
  import { ɵisObservable, ɵisPromise, EventEmitter, Directive, Attribute, Output, Component, createEnvironmentInjector, ɵisStandalone, ComponentFactoryResolver, InjectionToken, InjectFlags, NgModuleFactory, Injectable, NgModuleRef, ɵConsole, NgZone, ɵcoerceToBoolean, Input, HostListener, HostBinding, Optional, ContentChildren, Injector, Compiler, NgProbeToken, ANALYZE_FOR_ENTRY_COMPONENTS, SkipSelf, Inject, APP_INITIALIZER, APP_BOOTSTRAP_LISTENER, NgModule, ApplicationRef, Version } from '@angular/core';
11
9
  import { from, of, BehaviorSubject, combineLatest, throwError, EmptyError, concat, defer, Observable, EMPTY, ConnectableObservable, Subject } from 'rxjs';
10
+ import * as i3 from '@angular/common';
11
+ import { Location, LocationStrategy, PlatformLocation, APP_BASE_HREF, ViewportScroller, HashLocationStrategy, PathLocationStrategy, LOCATION_INITIALIZED } from '@angular/common';
12
12
  import { map, switchMap, take, startWith, scan, filter, catchError, concatMap, last as last$1, first, mergeMap, tap, takeLast, mapTo, finalize, refCount, defaultIfEmpty, mergeAll } from 'rxjs/operators';
13
13
  import * as i1 from '@angular/platform-browser';
14
14
 
@@ -20,627 +20,782 @@ import * as i1 from '@angular/platform-browser';
20
20
  * found in the LICENSE file at https://angular.io/license
21
21
  */
22
22
  /**
23
- * Base for events the router goes through, as opposed to events tied to a specific
24
- * route. Fired one time for any given navigation.
25
- *
26
- * The following code shows how a class subscribes to router events.
27
- *
28
- * ```ts
29
- * import {Event, RouterEvent, Router} from '@angular/router';
30
- *
31
- * class MyService {
32
- * constructor(public router: Router) {
33
- * router.events.pipe(
34
- * filter((e: Event): e is RouterEvent => e instanceof RouterEvent)
35
- * ).subscribe((e: RouterEvent) => {
36
- * // Do something
37
- * });
38
- * }
39
- * }
40
- * ```
23
+ * The primary routing outlet.
41
24
  *
42
- * @see `Event`
43
- * @see [Router events summary](guide/router-reference#router-events)
44
25
  * @publicApi
45
26
  */
46
- class RouterEvent {
47
- constructor(
48
- /** A unique ID that the router assigns to every router navigation. */
49
- id,
50
- /** The URL that is the destination for this navigation. */
51
- url) {
52
- this.id = id;
53
- this.url = url;
27
+ const PRIMARY_OUTLET = 'primary';
28
+ class ParamsAsMap {
29
+ constructor(params) {
30
+ this.params = params || {};
31
+ }
32
+ has(name) {
33
+ return Object.prototype.hasOwnProperty.call(this.params, name);
34
+ }
35
+ get(name) {
36
+ if (this.has(name)) {
37
+ const v = this.params[name];
38
+ return Array.isArray(v) ? v[0] : v;
39
+ }
40
+ return null;
41
+ }
42
+ getAll(name) {
43
+ if (this.has(name)) {
44
+ const v = this.params[name];
45
+ return Array.isArray(v) ? v : [v];
46
+ }
47
+ return [];
48
+ }
49
+ get keys() {
50
+ return Object.keys(this.params);
54
51
  }
55
52
  }
56
53
  /**
57
- * An event triggered when a navigation starts.
54
+ * Converts a `Params` instance to a `ParamMap`.
55
+ * @param params The instance to convert.
56
+ * @returns The new map instance.
58
57
  *
59
58
  * @publicApi
60
59
  */
61
- class NavigationStart extends RouterEvent {
62
- constructor(
63
- /** @docsNotRequired */
64
- id,
65
- /** @docsNotRequired */
66
- url,
67
- /** @docsNotRequired */
68
- navigationTrigger = 'imperative',
69
- /** @docsNotRequired */
70
- restoredState = null) {
71
- super(id, url);
72
- this.type = 0 /* EventType.NavigationStart */;
73
- this.navigationTrigger = navigationTrigger;
74
- this.restoredState = restoredState;
60
+ function convertToParamMap(params) {
61
+ return new ParamsAsMap(params);
62
+ }
63
+ const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
64
+ function navigationCancelingError(message) {
65
+ const error = Error('NavigationCancelingError: ' + message);
66
+ error[NAVIGATION_CANCELING_ERROR] = true;
67
+ return error;
68
+ }
69
+ function isNavigationCancelingError(error) {
70
+ return error && error[NAVIGATION_CANCELING_ERROR];
71
+ }
72
+ // Matches the route configuration (`route`) against the actual URL (`segments`).
73
+ function defaultUrlMatcher(segments, segmentGroup, route) {
74
+ const parts = route.path.split('/');
75
+ if (parts.length > segments.length) {
76
+ // The actual URL is shorter than the config, no match
77
+ return null;
75
78
  }
76
- /** @docsNotRequired */
77
- toString() {
78
- return `NavigationStart(id: ${this.id}, url: '${this.url}')`;
79
+ if (route.pathMatch === 'full' &&
80
+ (segmentGroup.hasChildren() || parts.length < segments.length)) {
81
+ // The config is longer than the actual URL but we are looking for a full match, return null
82
+ return null;
83
+ }
84
+ const posParams = {};
85
+ // Check each config part against the actual URL
86
+ for (let index = 0; index < parts.length; index++) {
87
+ const part = parts[index];
88
+ const segment = segments[index];
89
+ const isParameter = part.startsWith(':');
90
+ if (isParameter) {
91
+ posParams[part.substring(1)] = segment;
92
+ }
93
+ else if (part !== segment.path) {
94
+ // The actual URL part does not match the config, no match
95
+ return null;
96
+ }
79
97
  }
98
+ return { consumed: segments.slice(0, parts.length), posParams };
80
99
  }
100
+
81
101
  /**
82
- * An event triggered when a navigation ends successfully.
83
- *
84
- * @see `NavigationStart`
85
- * @see `NavigationCancel`
86
- * @see `NavigationError`
102
+ * @license
103
+ * Copyright Google LLC All Rights Reserved.
87
104
  *
88
- * @publicApi
105
+ * Use of this source code is governed by an MIT-style license that can be
106
+ * found in the LICENSE file at https://angular.io/license
89
107
  */
90
- class NavigationEnd extends RouterEvent {
91
- constructor(
92
- /** @docsNotRequired */
93
- id,
94
- /** @docsNotRequired */
95
- url,
96
- /** @docsNotRequired */
97
- urlAfterRedirects) {
98
- super(id, url);
99
- this.urlAfterRedirects = urlAfterRedirects;
100
- this.type = 1 /* EventType.NavigationEnd */;
108
+ function shallowEqualArrays(a, b) {
109
+ if (a.length !== b.length)
110
+ return false;
111
+ for (let i = 0; i < a.length; ++i) {
112
+ if (!shallowEqual(a[i], b[i]))
113
+ return false;
101
114
  }
102
- /** @docsNotRequired */
103
- toString() {
104
- return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`;
115
+ return true;
116
+ }
117
+ function shallowEqual(a, b) {
118
+ // While `undefined` should never be possible, it would sometimes be the case in IE 11
119
+ // and pre-chromium Edge. The check below accounts for this edge case.
120
+ const k1 = a ? Object.keys(a) : undefined;
121
+ const k2 = b ? Object.keys(b) : undefined;
122
+ if (!k1 || !k2 || k1.length != k2.length) {
123
+ return false;
124
+ }
125
+ let key;
126
+ for (let i = 0; i < k1.length; i++) {
127
+ key = k1[i];
128
+ if (!equalArraysOrString(a[key], b[key])) {
129
+ return false;
130
+ }
105
131
  }
132
+ return true;
106
133
  }
107
134
  /**
108
- * An event triggered when a navigation is canceled, directly or indirectly.
109
- * This can happen for several reasons including when a route guard
110
- * returns `false` or initiates a redirect by returning a `UrlTree`.
111
- *
112
- * @see `NavigationStart`
113
- * @see `NavigationEnd`
114
- * @see `NavigationError`
115
- *
116
- * @publicApi
135
+ * Test equality for arrays of strings or a string.
117
136
  */
118
- class NavigationCancel extends RouterEvent {
119
- constructor(
120
- /** @docsNotRequired */
121
- id,
122
- /** @docsNotRequired */
123
- url,
124
- /** @docsNotRequired */
125
- reason) {
126
- super(id, url);
127
- this.reason = reason;
128
- this.type = 2 /* EventType.NavigationCancel */;
137
+ function equalArraysOrString(a, b) {
138
+ if (Array.isArray(a) && Array.isArray(b)) {
139
+ if (a.length !== b.length)
140
+ return false;
141
+ const aSorted = [...a].sort();
142
+ const bSorted = [...b].sort();
143
+ return aSorted.every((val, index) => bSorted[index] === val);
129
144
  }
130
- /** @docsNotRequired */
131
- toString() {
132
- return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;
145
+ else {
146
+ return a === b;
133
147
  }
134
148
  }
135
149
  /**
136
- * An event triggered when a navigation fails due to an unexpected error.
137
- *
138
- * @see `NavigationStart`
139
- * @see `NavigationEnd`
140
- * @see `NavigationCancel`
141
- *
142
- * @publicApi
150
+ * Flattens single-level nested arrays.
143
151
  */
144
- class NavigationError extends RouterEvent {
145
- constructor(
146
- /** @docsNotRequired */
147
- id,
148
- /** @docsNotRequired */
149
- url,
150
- /** @docsNotRequired */
151
- error) {
152
- super(id, url);
153
- this.error = error;
154
- this.type = 3 /* EventType.NavigationError */;
155
- }
156
- /** @docsNotRequired */
157
- toString() {
158
- return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`;
159
- }
152
+ function flatten(arr) {
153
+ return Array.prototype.concat.apply([], arr);
160
154
  }
161
155
  /**
162
- * An event triggered when routes are recognized.
163
- *
164
- * @publicApi
156
+ * Return the last element of an array.
165
157
  */
166
- class RoutesRecognized extends RouterEvent {
167
- constructor(
168
- /** @docsNotRequired */
169
- id,
170
- /** @docsNotRequired */
171
- url,
172
- /** @docsNotRequired */
173
- urlAfterRedirects,
174
- /** @docsNotRequired */
175
- state) {
176
- super(id, url);
177
- this.urlAfterRedirects = urlAfterRedirects;
178
- this.state = state;
179
- this.type = 4 /* EventType.RoutesRecognized */;
180
- }
181
- /** @docsNotRequired */
182
- toString() {
183
- return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
184
- }
158
+ function last(a) {
159
+ return a.length > 0 ? a[a.length - 1] : null;
185
160
  }
186
161
  /**
187
- * An event triggered at the start of the Guard phase of routing.
188
- *
189
- * @see `GuardsCheckEnd`
190
- *
191
- * @publicApi
162
+ * Verifys all booleans in an array are `true`.
192
163
  */
193
- class GuardsCheckStart extends RouterEvent {
194
- constructor(
195
- /** @docsNotRequired */
196
- id,
197
- /** @docsNotRequired */
198
- url,
199
- /** @docsNotRequired */
200
- urlAfterRedirects,
201
- /** @docsNotRequired */
202
- state) {
203
- super(id, url);
204
- this.urlAfterRedirects = urlAfterRedirects;
205
- this.state = state;
206
- this.type = 7 /* EventType.GuardsCheckStart */;
164
+ function and(bools) {
165
+ return !bools.some(v => !v);
166
+ }
167
+ function forEach(map, callback) {
168
+ for (const prop in map) {
169
+ if (map.hasOwnProperty(prop)) {
170
+ callback(map[prop], prop);
171
+ }
207
172
  }
208
- toString() {
209
- return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
173
+ }
174
+ function wrapIntoObservable(value) {
175
+ if (ɵisObservable(value)) {
176
+ return value;
177
+ }
178
+ if (ɵisPromise(value)) {
179
+ // Use `Promise.resolve()` to wrap promise-like instances.
180
+ // Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the
181
+ // change detection.
182
+ return from(Promise.resolve(value));
210
183
  }
184
+ return of(value);
211
185
  }
186
+
212
187
  /**
213
- * An event triggered at the end of the Guard phase of routing.
214
- *
215
- * @see `GuardsCheckStart`
188
+ * @license
189
+ * Copyright Google LLC All Rights Reserved.
216
190
  *
217
- * @publicApi
191
+ * Use of this source code is governed by an MIT-style license that can be
192
+ * found in the LICENSE file at https://angular.io/license
218
193
  */
219
- class GuardsCheckEnd extends RouterEvent {
220
- constructor(
221
- /** @docsNotRequired */
222
- id,
223
- /** @docsNotRequired */
224
- url,
225
- /** @docsNotRequired */
226
- urlAfterRedirects,
227
- /** @docsNotRequired */
228
- state,
229
- /** @docsNotRequired */
230
- shouldActivate) {
231
- super(id, url);
232
- this.urlAfterRedirects = urlAfterRedirects;
233
- this.state = state;
234
- this.shouldActivate = shouldActivate;
235
- this.type = 8 /* EventType.GuardsCheckEnd */;
194
+ function createEmptyUrlTree() {
195
+ return new UrlTree(new UrlSegmentGroup([], {}), {}, null);
196
+ }
197
+ const pathCompareMap = {
198
+ 'exact': equalSegmentGroups,
199
+ 'subset': containsSegmentGroup,
200
+ };
201
+ const paramCompareMap = {
202
+ 'exact': equalParams,
203
+ 'subset': containsParams,
204
+ 'ignored': () => true,
205
+ };
206
+ function containsTree(container, containee, options) {
207
+ return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) &&
208
+ paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) &&
209
+ !(options.fragment === 'exact' && container.fragment !== containee.fragment);
210
+ }
211
+ function equalParams(container, containee) {
212
+ // TODO: This does not handle array params correctly.
213
+ return shallowEqual(container, containee);
214
+ }
215
+ function equalSegmentGroups(container, containee, matrixParams) {
216
+ if (!equalPath(container.segments, containee.segments))
217
+ return false;
218
+ if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) {
219
+ return false;
236
220
  }
237
- toString() {
238
- return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
221
+ if (container.numberOfChildren !== containee.numberOfChildren)
222
+ return false;
223
+ for (const c in containee.children) {
224
+ if (!container.children[c])
225
+ return false;
226
+ if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams))
227
+ return false;
228
+ }
229
+ return true;
230
+ }
231
+ function containsParams(container, containee) {
232
+ return Object.keys(containee).length <= Object.keys(container).length &&
233
+ Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key]));
234
+ }
235
+ function containsSegmentGroup(container, containee, matrixParams) {
236
+ return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams);
237
+ }
238
+ function containsSegmentGroupHelper(container, containee, containeePaths, matrixParams) {
239
+ if (container.segments.length > containeePaths.length) {
240
+ const current = container.segments.slice(0, containeePaths.length);
241
+ if (!equalPath(current, containeePaths))
242
+ return false;
243
+ if (containee.hasChildren())
244
+ return false;
245
+ if (!matrixParamsMatch(current, containeePaths, matrixParams))
246
+ return false;
247
+ return true;
248
+ }
249
+ else if (container.segments.length === containeePaths.length) {
250
+ if (!equalPath(container.segments, containeePaths))
251
+ return false;
252
+ if (!matrixParamsMatch(container.segments, containeePaths, matrixParams))
253
+ return false;
254
+ for (const c in containee.children) {
255
+ if (!container.children[c])
256
+ return false;
257
+ if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) {
258
+ return false;
259
+ }
260
+ }
261
+ return true;
262
+ }
263
+ else {
264
+ const current = containeePaths.slice(0, container.segments.length);
265
+ const next = containeePaths.slice(container.segments.length);
266
+ if (!equalPath(container.segments, current))
267
+ return false;
268
+ if (!matrixParamsMatch(container.segments, current, matrixParams))
269
+ return false;
270
+ if (!container.children[PRIMARY_OUTLET])
271
+ return false;
272
+ return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next, matrixParams);
239
273
  }
240
274
  }
275
+ function matrixParamsMatch(containerPaths, containeePaths, options) {
276
+ return containeePaths.every((containeeSegment, i) => {
277
+ return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters);
278
+ });
279
+ }
241
280
  /**
242
- * An event triggered at the start of the Resolve phase of routing.
281
+ * @description
243
282
  *
244
- * Runs in the "resolve" phase whether or not there is anything to resolve.
245
- * In future, may change to only run when there are things to be resolved.
283
+ * Represents the parsed URL.
246
284
  *
247
- * @see `ResolveEnd`
285
+ * Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a
286
+ * serialized tree.
287
+ * UrlTree is a data structure that provides a lot of affordances in dealing with URLs
288
+ *
289
+ * @usageNotes
290
+ * ### Example
291
+ *
292
+ * ```
293
+ * @Component({templateUrl:'template.html'})
294
+ * class MyComponent {
295
+ * constructor(router: Router) {
296
+ * const tree: UrlTree =
297
+ * router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
298
+ * const f = tree.fragment; // return 'fragment'
299
+ * const q = tree.queryParams; // returns {debug: 'true'}
300
+ * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
301
+ * const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'
302
+ * g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'
303
+ * g.children['support'].segments; // return 1 segment 'help'
304
+ * }
305
+ * }
306
+ * ```
248
307
  *
249
308
  * @publicApi
250
309
  */
251
- class ResolveStart extends RouterEvent {
310
+ class UrlTree {
311
+ /** @internal */
252
312
  constructor(
253
- /** @docsNotRequired */
254
- id,
255
- /** @docsNotRequired */
256
- url,
257
- /** @docsNotRequired */
258
- urlAfterRedirects,
259
- /** @docsNotRequired */
260
- state) {
261
- super(id, url);
262
- this.urlAfterRedirects = urlAfterRedirects;
263
- this.state = state;
264
- this.type = 5 /* EventType.ResolveStart */;
313
+ /** The root segment group of the URL tree */
314
+ root,
315
+ /** The query params of the URL */
316
+ queryParams,
317
+ /** The fragment of the URL */
318
+ fragment) {
319
+ this.root = root;
320
+ this.queryParams = queryParams;
321
+ this.fragment = fragment;
322
+ }
323
+ get queryParamMap() {
324
+ if (!this._queryParamMap) {
325
+ this._queryParamMap = convertToParamMap(this.queryParams);
326
+ }
327
+ return this._queryParamMap;
265
328
  }
329
+ /** @docsNotRequired */
266
330
  toString() {
267
- return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
331
+ return DEFAULT_SERIALIZER.serialize(this);
268
332
  }
269
333
  }
270
334
  /**
271
- * An event triggered at the end of the Resolve phase of routing.
272
- * @see `ResolveStart`.
335
+ * @description
336
+ *
337
+ * Represents the parsed URL segment group.
338
+ *
339
+ * See `UrlTree` for more information.
273
340
  *
274
341
  * @publicApi
275
342
  */
276
- class ResolveEnd extends RouterEvent {
343
+ class UrlSegmentGroup {
277
344
  constructor(
278
- /** @docsNotRequired */
279
- id,
280
- /** @docsNotRequired */
281
- url,
282
- /** @docsNotRequired */
283
- urlAfterRedirects,
284
- /** @docsNotRequired */
285
- state) {
286
- super(id, url);
287
- this.urlAfterRedirects = urlAfterRedirects;
288
- this.state = state;
289
- this.type = 6 /* EventType.ResolveEnd */;
345
+ /** The URL segments of this group. See `UrlSegment` for more information */
346
+ segments,
347
+ /** The list of children of this group */
348
+ children) {
349
+ this.segments = segments;
350
+ this.children = children;
351
+ /** The parent node in the url tree */
352
+ this.parent = null;
353
+ forEach(children, (v, k) => v.parent = this);
354
+ }
355
+ /** Whether the segment has child segments */
356
+ hasChildren() {
357
+ return this.numberOfChildren > 0;
358
+ }
359
+ /** Number of child segments */
360
+ get numberOfChildren() {
361
+ return Object.keys(this.children).length;
290
362
  }
363
+ /** @docsNotRequired */
291
364
  toString() {
292
- return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
365
+ return serializePaths(this);
293
366
  }
294
367
  }
295
368
  /**
296
- * An event triggered before lazy loading a route configuration.
369
+ * @description
297
370
  *
298
- * @see `RouteConfigLoadEnd`
371
+ * Represents a single URL segment.
299
372
  *
300
- * @publicApi
301
- */
302
- class RouteConfigLoadStart {
303
- constructor(
304
- /** @docsNotRequired */
305
- route) {
306
- this.route = route;
307
- this.type = 9 /* EventType.RouteConfigLoadStart */;
308
- }
309
- toString() {
310
- return `RouteConfigLoadStart(path: ${this.route.path})`;
311
- }
312
- }
313
- /**
314
- * An event triggered when a route has been lazy loaded.
373
+ * A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix
374
+ * parameters associated with the segment.
315
375
  *
316
- * @see `RouteConfigLoadStart`
376
+ * @usageNotes
377
+ * ### Example
378
+ *
379
+ * ```
380
+ * @Component({templateUrl:'template.html'})
381
+ * class MyComponent {
382
+ * constructor(router: Router) {
383
+ * const tree: UrlTree = router.parseUrl('/team;id=33');
384
+ * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
385
+ * const s: UrlSegment[] = g.segments;
386
+ * s[0].path; // returns 'team'
387
+ * s[0].parameters; // returns {id: 33}
388
+ * }
389
+ * }
390
+ * ```
317
391
  *
318
392
  * @publicApi
319
393
  */
320
- class RouteConfigLoadEnd {
394
+ class UrlSegment {
321
395
  constructor(
322
- /** @docsNotRequired */
323
- route) {
324
- this.route = route;
325
- this.type = 10 /* EventType.RouteConfigLoadEnd */;
396
+ /** The path part of a URL segment */
397
+ path,
398
+ /** The matrix parameters associated with a segment */
399
+ parameters) {
400
+ this.path = path;
401
+ this.parameters = parameters;
402
+ }
403
+ get parameterMap() {
404
+ if (!this._parameterMap) {
405
+ this._parameterMap = convertToParamMap(this.parameters);
406
+ }
407
+ return this._parameterMap;
326
408
  }
409
+ /** @docsNotRequired */
327
410
  toString() {
328
- return `RouteConfigLoadEnd(path: ${this.route.path})`;
411
+ return serializePath(this);
329
412
  }
330
413
  }
414
+ function equalSegments(as, bs) {
415
+ return equalPath(as, bs) && as.every((a, i) => shallowEqual(a.parameters, bs[i].parameters));
416
+ }
417
+ function equalPath(as, bs) {
418
+ if (as.length !== bs.length)
419
+ return false;
420
+ return as.every((a, i) => a.path === bs[i].path);
421
+ }
422
+ function mapChildrenIntoArray(segment, fn) {
423
+ let res = [];
424
+ forEach(segment.children, (child, childOutlet) => {
425
+ if (childOutlet === PRIMARY_OUTLET) {
426
+ res = res.concat(fn(child, childOutlet));
427
+ }
428
+ });
429
+ forEach(segment.children, (child, childOutlet) => {
430
+ if (childOutlet !== PRIMARY_OUTLET) {
431
+ res = res.concat(fn(child, childOutlet));
432
+ }
433
+ });
434
+ return res;
435
+ }
331
436
  /**
332
- * An event triggered at the start of the child-activation
333
- * part of the Resolve phase of routing.
334
- * @see `ChildActivationEnd`
335
- * @see `ResolveStart`
437
+ * @description
438
+ *
439
+ * Serializes and deserializes a URL string into a URL tree.
440
+ *
441
+ * The url serialization strategy is customizable. You can
442
+ * make all URLs case insensitive by providing a custom UrlSerializer.
443
+ *
444
+ * See `DefaultUrlSerializer` for an example of a URL serializer.
336
445
  *
337
446
  * @publicApi
338
447
  */
339
- class ChildActivationStart {
340
- constructor(
341
- /** @docsNotRequired */
342
- snapshot) {
343
- this.snapshot = snapshot;
344
- this.type = 11 /* EventType.ChildActivationStart */;
345
- }
346
- toString() {
347
- const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
348
- return `ChildActivationStart(path: '${path}')`;
349
- }
448
+ class UrlSerializer {
350
449
  }
351
450
  /**
352
- * An event triggered at the end of the child-activation part
353
- * of the Resolve phase of routing.
354
- * @see `ChildActivationStart`
355
- * @see `ResolveStart`
451
+ * @description
452
+ *
453
+ * A default implementation of the `UrlSerializer`.
454
+ *
455
+ * Example URLs:
456
+ *
457
+ * ```
458
+ * /inbox/33(popup:compose)
459
+ * /inbox/33;open=true/messages/44
460
+ * ```
461
+ *
462
+ * DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the
463
+ * colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to
464
+ * specify route specific parameters.
465
+ *
356
466
  * @publicApi
357
467
  */
358
- class ChildActivationEnd {
359
- constructor(
360
- /** @docsNotRequired */
361
- snapshot) {
362
- this.snapshot = snapshot;
363
- this.type = 12 /* EventType.ChildActivationEnd */;
468
+ class DefaultUrlSerializer {
469
+ /** Parses a url into a `UrlTree` */
470
+ parse(url) {
471
+ const p = new UrlParser(url);
472
+ return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
364
473
  }
365
- toString() {
366
- const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
367
- return `ChildActivationEnd(path: '${path}')`;
474
+ /** Converts a `UrlTree` into a url */
475
+ serialize(tree) {
476
+ const segment = `/${serializeSegment(tree.root, true)}`;
477
+ const query = serializeQueryParams(tree.queryParams);
478
+ const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';
479
+ return `${segment}${query}${fragment}`;
480
+ }
481
+ }
482
+ const DEFAULT_SERIALIZER = new DefaultUrlSerializer();
483
+ function serializePaths(segment) {
484
+ return segment.segments.map(p => serializePath(p)).join('/');
485
+ }
486
+ function serializeSegment(segment, root) {
487
+ if (!segment.hasChildren()) {
488
+ return serializePaths(segment);
489
+ }
490
+ if (root) {
491
+ const primary = segment.children[PRIMARY_OUTLET] ?
492
+ serializeSegment(segment.children[PRIMARY_OUTLET], false) :
493
+ '';
494
+ const children = [];
495
+ forEach(segment.children, (v, k) => {
496
+ if (k !== PRIMARY_OUTLET) {
497
+ children.push(`${k}:${serializeSegment(v, false)}`);
498
+ }
499
+ });
500
+ return children.length > 0 ? `${primary}(${children.join('//')})` : primary;
501
+ }
502
+ else {
503
+ const children = mapChildrenIntoArray(segment, (v, k) => {
504
+ if (k === PRIMARY_OUTLET) {
505
+ return [serializeSegment(segment.children[PRIMARY_OUTLET], false)];
506
+ }
507
+ return [`${k}:${serializeSegment(v, false)}`];
508
+ });
509
+ // use no parenthesis if the only child is a primary outlet route
510
+ if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) {
511
+ return `${serializePaths(segment)}/${children[0]}`;
512
+ }
513
+ return `${serializePaths(segment)}/(${children.join('//')})`;
368
514
  }
369
515
  }
370
516
  /**
371
- * An event triggered at the start of the activation part
372
- * of the Resolve phase of routing.
373
- * @see `ActivationEnd`
374
- * @see `ResolveStart`
517
+ * Encodes a URI string with the default encoding. This function will only ever be called from
518
+ * `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need
519
+ * a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't
520
+ * have to be encoded per https://url.spec.whatwg.org.
521
+ */
522
+ function encodeUriString(s) {
523
+ return encodeURIComponent(s)
524
+ .replace(/%40/g, '@')
525
+ .replace(/%3A/gi, ':')
526
+ .replace(/%24/g, '$')
527
+ .replace(/%2C/gi, ',');
528
+ }
529
+ /**
530
+ * This function should be used to encode both keys and values in a query string key/value. In
531
+ * the following URL, you need to call encodeUriQuery on "k" and "v":
375
532
  *
376
- * @publicApi
533
+ * http://www.site.org/html;mk=mv?k=v#f
377
534
  */
378
- class ActivationStart {
379
- constructor(
380
- /** @docsNotRequired */
381
- snapshot) {
382
- this.snapshot = snapshot;
383
- this.type = 13 /* EventType.ActivationStart */;
384
- }
385
- toString() {
386
- const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
387
- return `ActivationStart(path: '${path}')`;
388
- }
535
+ function encodeUriQuery(s) {
536
+ return encodeUriString(s).replace(/%3B/gi, ';');
389
537
  }
390
538
  /**
391
- * An event triggered at the end of the activation part
392
- * of the Resolve phase of routing.
393
- * @see `ActivationStart`
394
- * @see `ResolveStart`
539
+ * This function should be used to encode a URL fragment. In the following URL, you need to call
540
+ * encodeUriFragment on "f":
395
541
  *
396
- * @publicApi
542
+ * http://www.site.org/html;mk=mv?k=v#f
397
543
  */
398
- class ActivationEnd {
399
- constructor(
400
- /** @docsNotRequired */
401
- snapshot) {
402
- this.snapshot = snapshot;
403
- this.type = 14 /* EventType.ActivationEnd */;
404
- }
405
- toString() {
406
- const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
407
- return `ActivationEnd(path: '${path}')`;
408
- }
544
+ function encodeUriFragment(s) {
545
+ return encodeURI(s);
409
546
  }
410
547
  /**
411
- * An event triggered by scrolling.
548
+ * This function should be run on any URI segment as well as the key and value in a key/value
549
+ * pair for matrix params. In the following URL, you need to call encodeUriSegment on "html",
550
+ * "mk", and "mv":
412
551
  *
413
- * @publicApi
552
+ * http://www.site.org/html;mk=mv?k=v#f
414
553
  */
415
- class Scroll {
416
- constructor(
417
- /** @docsNotRequired */
418
- routerEvent,
419
- /** @docsNotRequired */
420
- position,
421
- /** @docsNotRequired */
422
- anchor) {
423
- this.routerEvent = routerEvent;
424
- this.position = position;
425
- this.anchor = anchor;
426
- this.type = 15 /* EventType.Scroll */;
427
- }
428
- toString() {
429
- const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;
430
- return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;
431
- }
554
+ function encodeUriSegment(s) {
555
+ return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&');
432
556
  }
433
- function stringifyEvent(routerEvent) {
434
- if (!('type' in routerEvent)) {
435
- return `Unknown Router Event: ${routerEvent.constructor.name}`;
436
- }
437
- switch (routerEvent.type) {
438
- case 14 /* EventType.ActivationEnd */:
439
- return `ActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
440
- case 13 /* EventType.ActivationStart */:
441
- return `ActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
442
- case 12 /* EventType.ChildActivationEnd */:
443
- return `ChildActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
444
- case 11 /* EventType.ChildActivationStart */:
445
- return `ChildActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
446
- case 8 /* EventType.GuardsCheckEnd */:
447
- return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;
448
- case 7 /* EventType.GuardsCheckStart */:
449
- return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
450
- case 2 /* EventType.NavigationCancel */:
451
- return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
452
- case 1 /* EventType.NavigationEnd */:
453
- return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;
454
- case 3 /* EventType.NavigationError */:
455
- return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;
456
- case 0 /* EventType.NavigationStart */:
457
- return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
458
- case 6 /* EventType.ResolveEnd */:
459
- return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
460
- case 5 /* EventType.ResolveStart */:
461
- return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
462
- case 10 /* EventType.RouteConfigLoadEnd */:
463
- return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;
464
- case 9 /* EventType.RouteConfigLoadStart */:
465
- return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;
466
- case 4 /* EventType.RoutesRecognized */:
467
- return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
468
- case 15 /* EventType.Scroll */:
469
- const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null;
470
- return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;
471
- }
557
+ function decode(s) {
558
+ return decodeURIComponent(s);
472
559
  }
473
-
474
- /**
475
- * @license
476
- * Copyright Google LLC All Rights Reserved.
477
- *
478
- * Use of this source code is governed by an MIT-style license that can be
479
- * found in the LICENSE file at https://angular.io/license
480
- */
481
- /**
482
- * The primary routing outlet.
483
- *
484
- * @publicApi
485
- */
486
- const PRIMARY_OUTLET = 'primary';
487
- class ParamsAsMap {
488
- constructor(params) {
489
- this.params = params || {};
490
- }
491
- has(name) {
492
- return Object.prototype.hasOwnProperty.call(this.params, name);
493
- }
494
- get(name) {
495
- if (this.has(name)) {
496
- const v = this.params[name];
497
- return Array.isArray(v) ? v[0] : v;
498
- }
499
- return null;
500
- }
501
- getAll(name) {
502
- if (this.has(name)) {
503
- const v = this.params[name];
504
- return Array.isArray(v) ? v : [v];
505
- }
506
- return [];
507
- }
508
- get keys() {
509
- return Object.keys(this.params);
510
- }
560
+ // Query keys/values should have the "+" replaced first, as "+" in a query string is " ".
561
+ // decodeURIComponent function will not decode "+" as a space.
562
+ function decodeQuery(s) {
563
+ return decode(s.replace(/\+/g, '%20'));
511
564
  }
512
- /**
513
- * Converts a `Params` instance to a `ParamMap`.
514
- * @param params The instance to convert.
515
- * @returns The new map instance.
516
- *
517
- * @publicApi
518
- */
519
- function convertToParamMap(params) {
520
- return new ParamsAsMap(params);
565
+ function serializePath(path) {
566
+ return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`;
521
567
  }
522
- const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
523
- function navigationCancelingError(message) {
524
- const error = Error('NavigationCancelingError: ' + message);
525
- error[NAVIGATION_CANCELING_ERROR] = true;
526
- return error;
568
+ function serializeMatrixParams(params) {
569
+ return Object.keys(params)
570
+ .map(key => `;${encodeUriSegment(key)}=${encodeUriSegment(params[key])}`)
571
+ .join('');
527
572
  }
528
- function isNavigationCancelingError(error) {
529
- return error && error[NAVIGATION_CANCELING_ERROR];
573
+ function serializeQueryParams(params) {
574
+ const strParams = Object.keys(params)
575
+ .map((name) => {
576
+ const value = params[name];
577
+ return Array.isArray(value) ?
578
+ value.map(v => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&') :
579
+ `${encodeUriQuery(name)}=${encodeUriQuery(value)}`;
580
+ })
581
+ .filter(s => !!s);
582
+ return strParams.length ? `?${strParams.join('&')}` : '';
530
583
  }
531
- // Matches the route configuration (`route`) against the actual URL (`segments`).
532
- function defaultUrlMatcher(segments, segmentGroup, route) {
533
- const parts = route.path.split('/');
534
- if (parts.length > segments.length) {
535
- // The actual URL is shorter than the config, no match
536
- return null;
537
- }
538
- if (route.pathMatch === 'full' &&
539
- (segmentGroup.hasChildren() || parts.length < segments.length)) {
540
- // The config is longer than the actual URL but we are looking for a full match, return null
541
- return null;
584
+ const SEGMENT_RE = /^[^\/()?;=#]+/;
585
+ function matchSegments(str) {
586
+ const match = str.match(SEGMENT_RE);
587
+ return match ? match[0] : '';
588
+ }
589
+ const QUERY_PARAM_RE = /^[^=?&#]+/;
590
+ // Return the name of the query param at the start of the string or an empty string
591
+ function matchQueryParams(str) {
592
+ const match = str.match(QUERY_PARAM_RE);
593
+ return match ? match[0] : '';
594
+ }
595
+ const QUERY_PARAM_VALUE_RE = /^[^&#]+/;
596
+ // Return the value of the query param at the start of the string or an empty string
597
+ function matchUrlQueryParamValue(str) {
598
+ const match = str.match(QUERY_PARAM_VALUE_RE);
599
+ return match ? match[0] : '';
600
+ }
601
+ class UrlParser {
602
+ constructor(url) {
603
+ this.url = url;
604
+ this.remaining = url;
542
605
  }
543
- const posParams = {};
544
- // Check each config part against the actual URL
545
- for (let index = 0; index < parts.length; index++) {
546
- const part = parts[index];
547
- const segment = segments[index];
548
- const isParameter = part.startsWith(':');
549
- if (isParameter) {
550
- posParams[part.substring(1)] = segment;
551
- }
552
- else if (part !== segment.path) {
553
- // The actual URL part does not match the config, no match
554
- return null;
606
+ parseRootSegment() {
607
+ this.consumeOptional('/');
608
+ if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {
609
+ return new UrlSegmentGroup([], {});
555
610
  }
611
+ // The root segment group never has segments
612
+ return new UrlSegmentGroup([], this.parseChildren());
556
613
  }
557
- return { consumed: segments.slice(0, parts.length), posParams };
558
- }
559
-
560
- /**
561
- * @license
562
- * Copyright Google LLC All Rights Reserved.
563
- *
564
- * Use of this source code is governed by an MIT-style license that can be
565
- * found in the LICENSE file at https://angular.io/license
566
- */
567
- function shallowEqualArrays(a, b) {
568
- if (a.length !== b.length)
569
- return false;
570
- for (let i = 0; i < a.length; ++i) {
571
- if (!shallowEqual(a[i], b[i]))
572
- return false;
614
+ parseQueryParams() {
615
+ const params = {};
616
+ if (this.consumeOptional('?')) {
617
+ do {
618
+ this.parseQueryParam(params);
619
+ } while (this.consumeOptional('&'));
620
+ }
621
+ return params;
573
622
  }
574
- return true;
575
- }
576
- function shallowEqual(a, b) {
577
- // While `undefined` should never be possible, it would sometimes be the case in IE 11
578
- // and pre-chromium Edge. The check below accounts for this edge case.
579
- const k1 = a ? Object.keys(a) : undefined;
580
- const k2 = b ? Object.keys(b) : undefined;
581
- if (!k1 || !k2 || k1.length != k2.length) {
582
- return false;
623
+ parseFragment() {
624
+ return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null;
583
625
  }
584
- let key;
585
- for (let i = 0; i < k1.length; i++) {
586
- key = k1[i];
587
- if (!equalArraysOrString(a[key], b[key])) {
588
- return false;
626
+ parseChildren() {
627
+ if (this.remaining === '') {
628
+ return {};
629
+ }
630
+ this.consumeOptional('/');
631
+ const segments = [];
632
+ if (!this.peekStartsWith('(')) {
633
+ segments.push(this.parseSegment());
634
+ }
635
+ while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) {
636
+ this.capture('/');
637
+ segments.push(this.parseSegment());
638
+ }
639
+ let children = {};
640
+ if (this.peekStartsWith('/(')) {
641
+ this.capture('/');
642
+ children = this.parseParens(true);
643
+ }
644
+ let res = {};
645
+ if (this.peekStartsWith('(')) {
646
+ res = this.parseParens(false);
589
647
  }
648
+ if (segments.length > 0 || Object.keys(children).length > 0) {
649
+ res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);
650
+ }
651
+ return res;
590
652
  }
591
- return true;
592
- }
593
- /**
594
- * Test equality for arrays of strings or a string.
595
- */
596
- function equalArraysOrString(a, b) {
597
- if (Array.isArray(a) && Array.isArray(b)) {
598
- if (a.length !== b.length)
599
- return false;
600
- const aSorted = [...a].sort();
601
- const bSorted = [...b].sort();
602
- return aSorted.every((val, index) => bSorted[index] === val);
653
+ // parse a segment with its matrix parameters
654
+ // ie `name;k1=v1;k2`
655
+ parseSegment() {
656
+ const path = matchSegments(this.remaining);
657
+ if (path === '' && this.peekStartsWith(';')) {
658
+ throw new Error(`Empty path url segment cannot have parameters: '${this.remaining}'.`);
659
+ }
660
+ this.capture(path);
661
+ return new UrlSegment(decode(path), this.parseMatrixParams());
603
662
  }
604
- else {
605
- return a === b;
663
+ parseMatrixParams() {
664
+ const params = {};
665
+ while (this.consumeOptional(';')) {
666
+ this.parseParam(params);
667
+ }
668
+ return params;
669
+ }
670
+ parseParam(params) {
671
+ const key = matchSegments(this.remaining);
672
+ if (!key) {
673
+ return;
674
+ }
675
+ this.capture(key);
676
+ let value = '';
677
+ if (this.consumeOptional('=')) {
678
+ const valueMatch = matchSegments(this.remaining);
679
+ if (valueMatch) {
680
+ value = valueMatch;
681
+ this.capture(value);
682
+ }
683
+ }
684
+ params[decode(key)] = decode(value);
685
+ }
686
+ // Parse a single query parameter `name[=value]`
687
+ parseQueryParam(params) {
688
+ const key = matchQueryParams(this.remaining);
689
+ if (!key) {
690
+ return;
691
+ }
692
+ this.capture(key);
693
+ let value = '';
694
+ if (this.consumeOptional('=')) {
695
+ const valueMatch = matchUrlQueryParamValue(this.remaining);
696
+ if (valueMatch) {
697
+ value = valueMatch;
698
+ this.capture(value);
699
+ }
700
+ }
701
+ const decodedKey = decodeQuery(key);
702
+ const decodedVal = decodeQuery(value);
703
+ if (params.hasOwnProperty(decodedKey)) {
704
+ // Append to existing values
705
+ let currentVal = params[decodedKey];
706
+ if (!Array.isArray(currentVal)) {
707
+ currentVal = [currentVal];
708
+ params[decodedKey] = currentVal;
709
+ }
710
+ currentVal.push(decodedVal);
711
+ }
712
+ else {
713
+ // Create a new value
714
+ params[decodedKey] = decodedVal;
715
+ }
716
+ }
717
+ // parse `(a/b//outlet_name:c/d)`
718
+ parseParens(allowPrimary) {
719
+ const segments = {};
720
+ this.capture('(');
721
+ while (!this.consumeOptional(')') && this.remaining.length > 0) {
722
+ const path = matchSegments(this.remaining);
723
+ const next = this.remaining[path.length];
724
+ // if is is not one of these characters, then the segment was unescaped
725
+ // or the group was not closed
726
+ if (next !== '/' && next !== ')' && next !== ';') {
727
+ throw new Error(`Cannot parse url '${this.url}'`);
728
+ }
729
+ let outletName = undefined;
730
+ if (path.indexOf(':') > -1) {
731
+ outletName = path.slice(0, path.indexOf(':'));
732
+ this.capture(outletName);
733
+ this.capture(':');
734
+ }
735
+ else if (allowPrimary) {
736
+ outletName = PRIMARY_OUTLET;
737
+ }
738
+ const children = this.parseChildren();
739
+ segments[outletName] = Object.keys(children).length === 1 ? children[PRIMARY_OUTLET] :
740
+ new UrlSegmentGroup([], children);
741
+ this.consumeOptional('//');
742
+ }
743
+ return segments;
744
+ }
745
+ peekStartsWith(str) {
746
+ return this.remaining.startsWith(str);
747
+ }
748
+ // Consumes the prefix when it is present and returns whether it has been consumed
749
+ consumeOptional(str) {
750
+ if (this.peekStartsWith(str)) {
751
+ this.remaining = this.remaining.substring(str.length);
752
+ return true;
753
+ }
754
+ return false;
755
+ }
756
+ capture(str) {
757
+ if (!this.consumeOptional(str)) {
758
+ throw new Error(`Expected "${str}".`);
759
+ }
606
760
  }
607
761
  }
762
+ function createRoot(rootCandidate) {
763
+ return rootCandidate.segments.length > 0 ?
764
+ new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) :
765
+ rootCandidate;
766
+ }
608
767
  /**
609
- * Flattens single-level nested arrays.
610
- */
611
- function flatten(arr) {
612
- return Array.prototype.concat.apply([], arr);
613
- }
614
- /**
615
- * Return the last element of an array.
616
- */
617
- function last(a) {
618
- return a.length > 0 ? a[a.length - 1] : null;
619
- }
620
- /**
621
- * Verifys all booleans in an array are `true`.
768
+ * Recursively merges primary segment children into their parents and also drops empty children
769
+ * (those which have no segments and no children themselves). The latter prevents serializing a
770
+ * group into something like `/a(aux:)`, where `aux` is an empty child segment.
622
771
  */
623
- function and(bools) {
624
- return !bools.some(v => !v);
625
- }
626
- function forEach(map, callback) {
627
- for (const prop in map) {
628
- if (map.hasOwnProperty(prop)) {
629
- callback(map[prop], prop);
772
+ function squashSegmentGroup(segmentGroup) {
773
+ const newChildren = {};
774
+ for (const childOutlet of Object.keys(segmentGroup.children)) {
775
+ const child = segmentGroup.children[childOutlet];
776
+ const childCandidate = squashSegmentGroup(child);
777
+ // don't add empty children
778
+ if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {
779
+ newChildren[childOutlet] = childCandidate;
630
780
  }
631
781
  }
782
+ const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
783
+ return mergeTrivialChildren(s);
632
784
  }
633
- function wrapIntoObservable(value) {
634
- if (ɵisObservable(value)) {
635
- return value;
636
- }
637
- if (ɵisPromise(value)) {
638
- // Use `Promise.resolve()` to wrap promise-like instances.
639
- // Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the
640
- // change detection.
641
- return from(Promise.resolve(value));
785
+ /**
786
+ * When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.
787
+ *
788
+ * When a segment group has only one child which is a primary outlet, merges that child into the
789
+ * parent. That is, the child segment group's segments are merged into the `s` and the child's
790
+ * children become the children of `s`. Think of this like a 'squash', merging the child segment
791
+ * group into the parent.
792
+ */
793
+ function mergeTrivialChildren(s) {
794
+ if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
795
+ const c = s.children[PRIMARY_OUTLET];
796
+ return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);
642
797
  }
643
- return of(value);
798
+ return s;
644
799
  }
645
800
 
646
801
  /**
@@ -650,998 +805,1009 @@ function wrapIntoObservable(value) {
650
805
  * Use of this source code is governed by an MIT-style license that can be
651
806
  * found in the LICENSE file at https://angular.io/license
652
807
  */
653
- function createEmptyUrlTree() {
654
- return new UrlTree(new UrlSegmentGroup([], {}), {}, null);
655
- }
656
- const pathCompareMap = {
657
- 'exact': equalSegmentGroups,
658
- 'subset': containsSegmentGroup,
659
- };
660
- const paramCompareMap = {
661
- 'exact': equalParams,
662
- 'subset': containsParams,
663
- 'ignored': () => true,
664
- };
665
- function containsTree(container, containee, options) {
666
- return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) &&
667
- paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) &&
668
- !(options.fragment === 'exact' && container.fragment !== containee.fragment);
669
- }
670
- function equalParams(container, containee) {
671
- // TODO: This does not handle array params correctly.
672
- return shallowEqual(container, containee);
673
- }
674
- function equalSegmentGroups(container, containee, matrixParams) {
675
- if (!equalPath(container.segments, containee.segments))
676
- return false;
677
- if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) {
678
- return false;
679
- }
680
- if (container.numberOfChildren !== containee.numberOfChildren)
681
- return false;
682
- for (const c in containee.children) {
683
- if (!container.children[c])
684
- return false;
685
- if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams))
686
- return false;
687
- }
688
- return true;
689
- }
690
- function containsParams(container, containee) {
691
- return Object.keys(containee).length <= Object.keys(container).length &&
692
- Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key]));
693
- }
694
- function containsSegmentGroup(container, containee, matrixParams) {
695
- return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams);
696
- }
697
- function containsSegmentGroupHelper(container, containee, containeePaths, matrixParams) {
698
- if (container.segments.length > containeePaths.length) {
699
- const current = container.segments.slice(0, containeePaths.length);
700
- if (!equalPath(current, containeePaths))
701
- return false;
702
- if (containee.hasChildren())
703
- return false;
704
- if (!matrixParamsMatch(current, containeePaths, matrixParams))
705
- return false;
706
- return true;
707
- }
708
- else if (container.segments.length === containeePaths.length) {
709
- if (!equalPath(container.segments, containeePaths))
710
- return false;
711
- if (!matrixParamsMatch(container.segments, containeePaths, matrixParams))
712
- return false;
713
- for (const c in containee.children) {
714
- if (!container.children[c])
715
- return false;
716
- if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) {
717
- return false;
718
- }
719
- }
720
- return true;
721
- }
722
- else {
723
- const current = containeePaths.slice(0, container.segments.length);
724
- const next = containeePaths.slice(container.segments.length);
725
- if (!equalPath(container.segments, current))
726
- return false;
727
- if (!matrixParamsMatch(container.segments, current, matrixParams))
728
- return false;
729
- if (!container.children[PRIMARY_OUTLET])
730
- return false;
731
- return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next, matrixParams);
732
- }
733
- }
734
- function matrixParamsMatch(containerPaths, containeePaths, options) {
735
- return containeePaths.every((containeeSegment, i) => {
736
- return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters);
737
- });
738
- }
739
808
  /**
740
- * @description
809
+ * Creates a `UrlTree` relative to an `ActivatedRouteSnapshot`.
741
810
  *
742
- * Represents the parsed URL.
811
+ * @publicApi
743
812
  *
744
- * Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a
745
- * serialized tree.
746
- * UrlTree is a data structure that provides a lot of affordances in dealing with URLs
813
+ *
814
+ * @param relativeTo The `ActivatedRouteSnapshot` to apply the commands to
815
+ * @param commands An array of URL fragments with which to construct the new URL tree.
816
+ * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path
817
+ * segments, followed by the parameters for each segment.
818
+ * The fragments are applied to the one provided in the `relativeTo` parameter.
819
+ * @param queryParams The query parameters for the `UrlTree`. `null` if the `UrlTree` does not have
820
+ * any query parameters.
821
+ * @param fragment The fragment for the `UrlTree`. `null` if the `UrlTree` does not have a fragment.
747
822
  *
748
823
  * @usageNotes
749
- * ### Example
750
824
  *
751
825
  * ```
752
- * @Component({templateUrl:'template.html'})
753
- * class MyComponent {
754
- * constructor(router: Router) {
755
- * const tree: UrlTree =
756
- * router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
757
- * const f = tree.fragment; // return 'fragment'
758
- * const q = tree.queryParams; // returns {debug: 'true'}
759
- * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
760
- * const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'
761
- * g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'
762
- * g.children['support'].segments; // return 1 segment 'help'
763
- * }
764
- * }
765
- * ```
826
+ * // create /team/33/user/11
827
+ * createUrlTreeFromSnapshot(snapshot, ['/team', 33, 'user', 11]);
766
828
  *
767
- * @publicApi
768
- */
769
- class UrlTree {
770
- /** @internal */
771
- constructor(
772
- /** The root segment group of the URL tree */
773
- root,
774
- /** The query params of the URL */
775
- queryParams,
776
- /** The fragment of the URL */
777
- fragment) {
778
- this.root = root;
779
- this.queryParams = queryParams;
780
- this.fragment = fragment;
781
- }
782
- get queryParamMap() {
783
- if (!this._queryParamMap) {
784
- this._queryParamMap = convertToParamMap(this.queryParams);
785
- }
786
- return this._queryParamMap;
787
- }
788
- /** @docsNotRequired */
789
- toString() {
790
- return DEFAULT_SERIALIZER.serialize(this);
791
- }
792
- }
793
- /**
794
- * @description
829
+ * // create /team/33;expand=true/user/11
830
+ * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {expand: true}, 'user', 11]);
795
831
  *
796
- * Represents the parsed URL segment group.
832
+ * // you can collapse static segments like this (this works only with the first passed-in value):
833
+ * createUrlTreeFromSnapshot(snapshot, ['/team/33/user', userId]);
797
834
  *
798
- * See `UrlTree` for more information.
835
+ * // If the first segment can contain slashes, and you do not want the router to split it,
836
+ * // you can do the following:
837
+ * createUrlTreeFromSnapshot(snapshot, [{segmentPath: '/one/two'}]);
799
838
  *
800
- * @publicApi
801
- */
802
- class UrlSegmentGroup {
803
- constructor(
804
- /** The URL segments of this group. See `UrlSegment` for more information */
805
- segments,
806
- /** The list of children of this group */
807
- children) {
808
- this.segments = segments;
809
- this.children = children;
810
- /** The parent node in the url tree */
811
- this.parent = null;
812
- forEach(children, (v, k) => v.parent = this);
813
- }
814
- /** Whether the segment has child segments */
815
- hasChildren() {
816
- return this.numberOfChildren > 0;
817
- }
818
- /** Number of child segments */
819
- get numberOfChildren() {
820
- return Object.keys(this.children).length;
821
- }
822
- /** @docsNotRequired */
823
- toString() {
824
- return serializePaths(this);
825
- }
826
- }
827
- /**
828
- * @description
839
+ * // create /team/33/(user/11//right:chat)
840
+ * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right:
841
+ * 'chat'}}], null, null);
829
842
  *
830
- * Represents a single URL segment.
843
+ * // remove the right secondary node
844
+ * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right: null}}]);
831
845
  *
832
- * A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix
833
- * parameters associated with the segment.
846
+ * // For the examples below, assume the current URL is for the `/team/33/user/11` and the
847
+ * `ActivatedRouteSnapshot` points to `user/11`:
834
848
  *
835
- * @usageNotes
836
- * ### Example
849
+ * // navigate to /team/33/user/11/details
850
+ * createUrlTreeFromSnapshot(snapshot, ['details']);
837
851
  *
838
- * ```
839
- * @Component({templateUrl:'template.html'})
840
- * class MyComponent {
841
- * constructor(router: Router) {
842
- * const tree: UrlTree = router.parseUrl('/team;id=33');
843
- * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
844
- * const s: UrlSegment[] = g.segments;
845
- * s[0].path; // returns 'team'
846
- * s[0].parameters; // returns {id: 33}
847
- * }
848
- * }
849
- * ```
852
+ * // navigate to /team/33/user/22
853
+ * createUrlTreeFromSnapshot(snapshot, ['../22']);
850
854
  *
851
- * @publicApi
855
+ * // navigate to /team/44/user/22
856
+ * createUrlTreeFromSnapshot(snapshot, ['../../team/44/user/22']);
857
+ * ```
852
858
  */
853
- class UrlSegment {
854
- constructor(
855
- /** The path part of a URL segment */
856
- path,
857
- /** The matrix parameters associated with a segment */
858
- parameters) {
859
- this.path = path;
860
- this.parameters = parameters;
861
- }
862
- get parameterMap() {
863
- if (!this._parameterMap) {
864
- this._parameterMap = convertToParamMap(this.parameters);
865
- }
866
- return this._parameterMap;
859
+ function createUrlTreeFromSnapshot(relativeTo, commands, queryParams = null, fragment = null) {
860
+ const relativeToUrlSegmentGroup = createSegmentGroupFromRoute(relativeTo);
861
+ return createUrlTreeFromSegmentGroup(relativeToUrlSegmentGroup, commands, queryParams, fragment);
862
+ }
863
+ function createSegmentGroupFromRoute(route) {
864
+ let targetGroup;
865
+ function createSegmentGroupFromRouteRecursive(currentRoute) {
866
+ const childOutlets = {};
867
+ for (const childSnapshot of currentRoute.children) {
868
+ const root = createSegmentGroupFromRouteRecursive(childSnapshot);
869
+ childOutlets[childSnapshot.outlet] = root;
870
+ }
871
+ const segmentGroup = new UrlSegmentGroup(currentRoute.url, childOutlets);
872
+ if (currentRoute === route) {
873
+ targetGroup = segmentGroup;
874
+ }
875
+ return segmentGroup;
876
+ }
877
+ const rootCandidate = createSegmentGroupFromRouteRecursive(route.root);
878
+ const rootSegmentGroup = createRoot(rootCandidate);
879
+ return targetGroup ?? rootSegmentGroup;
880
+ }
881
+ function createUrlTreeFromSegmentGroup(relativeTo, commands, queryParams, fragment) {
882
+ let root = relativeTo;
883
+ while (root.parent) {
884
+ root = root.parent;
885
+ }
886
+ // There are no commands so the `UrlTree` goes to the same path as the one created from the
887
+ // `UrlSegmentGroup`. All we need to do is update the `queryParams` and `fragment` without
888
+ // applying any other logic.
889
+ if (commands.length === 0) {
890
+ return tree(root, root, root, queryParams, fragment);
867
891
  }
868
- /** @docsNotRequired */
869
- toString() {
870
- return serializePath(this);
892
+ const nav = computeNavigation(commands);
893
+ if (nav.toRoot()) {
894
+ return tree(root, root, new UrlSegmentGroup([], {}), queryParams, fragment);
871
895
  }
896
+ const position = findStartingPositionForTargetGroup(nav, root, relativeTo);
897
+ const newSegmentGroup = position.processChildren ?
898
+ updateSegmentGroupChildren(position.segmentGroup, position.index, nav.commands) :
899
+ updateSegmentGroup(position.segmentGroup, position.index, nav.commands);
900
+ return tree(root, position.segmentGroup, newSegmentGroup, queryParams, fragment);
872
901
  }
873
- function equalSegments(as, bs) {
874
- return equalPath(as, bs) && as.every((a, i) => shallowEqual(a.parameters, bs[i].parameters));
875
- }
876
- function equalPath(as, bs) {
877
- if (as.length !== bs.length)
878
- return false;
879
- return as.every((a, i) => a.path === bs[i].path);
880
- }
881
- function mapChildrenIntoArray(segment, fn) {
882
- let res = [];
883
- forEach(segment.children, (child, childOutlet) => {
884
- if (childOutlet === PRIMARY_OUTLET) {
885
- res = res.concat(fn(child, childOutlet));
886
- }
887
- });
888
- forEach(segment.children, (child, childOutlet) => {
889
- if (childOutlet !== PRIMARY_OUTLET) {
890
- res = res.concat(fn(child, childOutlet));
902
+ function createUrlTree(route, urlTree, commands, queryParams, fragment) {
903
+ if (commands.length === 0) {
904
+ return tree(urlTree.root, urlTree.root, urlTree.root, queryParams, fragment);
905
+ }
906
+ const nav = computeNavigation(commands);
907
+ if (nav.toRoot()) {
908
+ return tree(urlTree.root, urlTree.root, new UrlSegmentGroup([], {}), queryParams, fragment);
909
+ }
910
+ function createTreeUsingPathIndex(lastPathIndex) {
911
+ const startingPosition = findStartingPosition(nav, urlTree, route.snapshot?._urlSegment, lastPathIndex);
912
+ const segmentGroup = startingPosition.processChildren ?
913
+ updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) :
914
+ updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
915
+ return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment);
916
+ }
917
+ // Note: The types should disallow `snapshot` from being `undefined` but due to test mocks, this
918
+ // may be the case. Since we try to access it at an earlier point before the refactor to add the
919
+ // warning for `relativeLinkResolution: 'legacy'`, this may cause failures in tests where it
920
+ // didn't before.
921
+ const result = createTreeUsingPathIndex(route.snapshot?._lastPathIndex);
922
+ // Check if application is relying on `relativeLinkResolution: 'legacy'`
923
+ if (typeof ngDevMode === 'undefined' || !!ngDevMode) {
924
+ const correctedResult = createTreeUsingPathIndex(route.snapshot?._correctedLastPathIndex);
925
+ if (correctedResult.toString() !== result.toString()) {
926
+ console.warn(`relativeLinkResolution: 'legacy' is deprecated and will be removed in a future version of Angular. The link to ${result.toString()} will change to ${correctedResult.toString()} if the code is not updated before then.`);
891
927
  }
892
- });
893
- return res;
928
+ }
929
+ return result;
894
930
  }
895
- /**
896
- * @description
897
- *
898
- * Serializes and deserializes a URL string into a URL tree.
899
- *
900
- * The url serialization strategy is customizable. You can
901
- * make all URLs case insensitive by providing a custom UrlSerializer.
902
- *
903
- * See `DefaultUrlSerializer` for an example of a URL serializer.
904
- *
905
- * @publicApi
906
- */
907
- class UrlSerializer {
931
+ function isMatrixParams(command) {
932
+ return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
908
933
  }
909
934
  /**
910
- * @description
911
- *
912
- * A default implementation of the `UrlSerializer`.
913
- *
914
- * Example URLs:
915
- *
916
- * ```
917
- * /inbox/33(popup:compose)
918
- * /inbox/33;open=true/messages/44
919
- * ```
920
- *
921
- * DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the
922
- * colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to
923
- * specify route specific parameters.
924
- *
925
- * @publicApi
935
+ * Determines if a given command has an `outlets` map. When we encounter a command
936
+ * with an outlets k/v map, we need to apply each outlet individually to the existing segment.
926
937
  */
927
- class DefaultUrlSerializer {
928
- /** Parses a url into a `UrlTree` */
929
- parse(url) {
930
- const p = new UrlParser(url);
931
- return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
932
- }
933
- /** Converts a `UrlTree` into a url */
934
- serialize(tree) {
935
- const segment = `/${serializeSegment(tree.root, true)}`;
936
- const query = serializeQueryParams(tree.queryParams);
937
- const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';
938
- return `${segment}${query}${fragment}`;
939
- }
940
- }
941
- const DEFAULT_SERIALIZER = new DefaultUrlSerializer();
942
- function serializePaths(segment) {
943
- return segment.segments.map(p => serializePath(p)).join('/');
938
+ function isCommandWithOutlets(command) {
939
+ return typeof command === 'object' && command != null && command.outlets;
944
940
  }
945
- function serializeSegment(segment, root) {
946
- if (!segment.hasChildren()) {
947
- return serializePaths(segment);
948
- }
949
- if (root) {
950
- const primary = segment.children[PRIMARY_OUTLET] ?
951
- serializeSegment(segment.children[PRIMARY_OUTLET], false) :
952
- '';
953
- const children = [];
954
- forEach(segment.children, (v, k) => {
955
- if (k !== PRIMARY_OUTLET) {
956
- children.push(`${k}:${serializeSegment(v, false)}`);
957
- }
941
+ function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment) {
942
+ let qp = {};
943
+ if (queryParams) {
944
+ forEach(queryParams, (value, name) => {
945
+ qp[name] = Array.isArray(value) ? value.map((v) => `${v}`) : `${value}`;
958
946
  });
959
- return children.length > 0 ? `${primary}(${children.join('//')})` : primary;
947
+ }
948
+ let rootCandidate;
949
+ if (oldRoot === oldSegmentGroup) {
950
+ rootCandidate = newSegmentGroup;
960
951
  }
961
952
  else {
962
- const children = mapChildrenIntoArray(segment, (v, k) => {
963
- if (k === PRIMARY_OUTLET) {
964
- return [serializeSegment(segment.children[PRIMARY_OUTLET], false)];
965
- }
966
- return [`${k}:${serializeSegment(v, false)}`];
967
- });
968
- // use no parenthesis if the only child is a primary outlet route
969
- if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) {
970
- return `${serializePaths(segment)}/${children[0]}`;
971
- }
972
- return `${serializePaths(segment)}/(${children.join('//')})`;
953
+ rootCandidate = replaceSegment(oldRoot, oldSegmentGroup, newSegmentGroup);
973
954
  }
955
+ const newRoot = createRoot(squashSegmentGroup(rootCandidate));
956
+ return new UrlTree(newRoot, qp, fragment);
974
957
  }
975
958
  /**
976
- * Encodes a URI string with the default encoding. This function will only ever be called from
977
- * `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need
978
- * a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't
979
- * have to be encoded per https://url.spec.whatwg.org.
980
- */
981
- function encodeUriString(s) {
982
- return encodeURIComponent(s)
983
- .replace(/%40/g, '@')
984
- .replace(/%3A/gi, ':')
985
- .replace(/%24/g, '$')
986
- .replace(/%2C/gi, ',');
987
- }
988
- /**
989
- * This function should be used to encode both keys and values in a query string key/value. In
990
- * the following URL, you need to call encodeUriQuery on "k" and "v":
991
- *
992
- * http://www.site.org/html;mk=mv?k=v#f
959
+ * Replaces the `oldSegment` which is located in some child of the `current` with the `newSegment`.
960
+ * This also has the effect of creating new `UrlSegmentGroup` copies to update references. This
961
+ * shouldn't be necessary but the fallback logic for an invalid ActivatedRoute in the creation uses
962
+ * the Router's current url tree. If we don't create new segment groups, we end up modifying that
963
+ * value.
993
964
  */
994
- function encodeUriQuery(s) {
995
- return encodeUriString(s).replace(/%3B/gi, ';');
965
+ function replaceSegment(current, oldSegment, newSegment) {
966
+ const children = {};
967
+ forEach(current.children, (c, outletName) => {
968
+ if (c === oldSegment) {
969
+ children[outletName] = newSegment;
970
+ }
971
+ else {
972
+ children[outletName] = replaceSegment(c, oldSegment, newSegment);
973
+ }
974
+ });
975
+ return new UrlSegmentGroup(current.segments, children);
996
976
  }
997
- /**
998
- * This function should be used to encode a URL fragment. In the following URL, you need to call
999
- * encodeUriFragment on "f":
1000
- *
1001
- * http://www.site.org/html;mk=mv?k=v#f
1002
- */
1003
- function encodeUriFragment(s) {
1004
- return encodeURI(s);
1005
- }
1006
- /**
1007
- * This function should be run on any URI segment as well as the key and value in a key/value
1008
- * pair for matrix params. In the following URL, you need to call encodeUriSegment on "html",
1009
- * "mk", and "mv":
1010
- *
1011
- * http://www.site.org/html;mk=mv?k=v#f
1012
- */
1013
- function encodeUriSegment(s) {
1014
- return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&');
1015
- }
1016
- function decode(s) {
1017
- return decodeURIComponent(s);
1018
- }
1019
- // Query keys/values should have the "+" replaced first, as "+" in a query string is " ".
1020
- // decodeURIComponent function will not decode "+" as a space.
1021
- function decodeQuery(s) {
1022
- return decode(s.replace(/\+/g, '%20'));
977
+ class Navigation {
978
+ constructor(isAbsolute, numberOfDoubleDots, commands) {
979
+ this.isAbsolute = isAbsolute;
980
+ this.numberOfDoubleDots = numberOfDoubleDots;
981
+ this.commands = commands;
982
+ if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {
983
+ throw new Error('Root segment cannot have matrix parameters');
984
+ }
985
+ const cmdWithOutlet = commands.find(isCommandWithOutlets);
986
+ if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {
987
+ throw new Error('{outlets:{}} has to be the last command');
988
+ }
989
+ }
990
+ toRoot() {
991
+ return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';
992
+ }
1023
993
  }
1024
- function serializePath(path) {
1025
- return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`;
994
+ /** Transforms commands to a normalized `Navigation` */
995
+ function computeNavigation(commands) {
996
+ if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] === '/') {
997
+ return new Navigation(true, 0, commands);
998
+ }
999
+ let numberOfDoubleDots = 0;
1000
+ let isAbsolute = false;
1001
+ const res = commands.reduce((res, cmd, cmdIdx) => {
1002
+ if (typeof cmd === 'object' && cmd != null) {
1003
+ if (cmd.outlets) {
1004
+ const outlets = {};
1005
+ forEach(cmd.outlets, (commands, name) => {
1006
+ outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;
1007
+ });
1008
+ return [...res, { outlets }];
1009
+ }
1010
+ if (cmd.segmentPath) {
1011
+ return [...res, cmd.segmentPath];
1012
+ }
1013
+ }
1014
+ if (!(typeof cmd === 'string')) {
1015
+ return [...res, cmd];
1016
+ }
1017
+ if (cmdIdx === 0) {
1018
+ cmd.split('/').forEach((urlPart, partIndex) => {
1019
+ if (partIndex == 0 && urlPart === '.') {
1020
+ // skip './a'
1021
+ }
1022
+ else if (partIndex == 0 && urlPart === '') { // '/a'
1023
+ isAbsolute = true;
1024
+ }
1025
+ else if (urlPart === '..') { // '../a'
1026
+ numberOfDoubleDots++;
1027
+ }
1028
+ else if (urlPart != '') {
1029
+ res.push(urlPart);
1030
+ }
1031
+ });
1032
+ return res;
1033
+ }
1034
+ return [...res, cmd];
1035
+ }, []);
1036
+ return new Navigation(isAbsolute, numberOfDoubleDots, res);
1026
1037
  }
1027
- function serializeMatrixParams(params) {
1028
- return Object.keys(params)
1029
- .map(key => `;${encodeUriSegment(key)}=${encodeUriSegment(params[key])}`)
1030
- .join('');
1038
+ class Position {
1039
+ constructor(segmentGroup, processChildren, index) {
1040
+ this.segmentGroup = segmentGroup;
1041
+ this.processChildren = processChildren;
1042
+ this.index = index;
1043
+ }
1031
1044
  }
1032
- function serializeQueryParams(params) {
1033
- const strParams = Object.keys(params)
1034
- .map((name) => {
1035
- const value = params[name];
1036
- return Array.isArray(value) ?
1037
- value.map(v => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&') :
1038
- `${encodeUriQuery(name)}=${encodeUriQuery(value)}`;
1039
- })
1040
- .filter(s => !!s);
1041
- return strParams.length ? `?${strParams.join('&')}` : '';
1045
+ function findStartingPositionForTargetGroup(nav, root, target) {
1046
+ if (nav.isAbsolute) {
1047
+ return new Position(root, true, 0);
1048
+ }
1049
+ if (!target) {
1050
+ // `NaN` is used only to maintain backwards compatibility with incorrectly mocked
1051
+ // `ActivatedRouteSnapshot` in tests. In prior versions of this code, the position here was
1052
+ // determined based on an internal property that was rarely mocked, resulting in `NaN`. In
1053
+ // reality, this code path should _never_ be touched since `target` is not allowed to be falsey.
1054
+ return new Position(root, false, NaN);
1055
+ }
1056
+ if (target.parent === null) {
1057
+ return new Position(target, true, 0);
1058
+ }
1059
+ const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
1060
+ const index = target.segments.length - 1 + modifier;
1061
+ return createPositionApplyingDoubleDots(target, index, nav.numberOfDoubleDots);
1042
1062
  }
1043
- const SEGMENT_RE = /^[^\/()?;=#]+/;
1044
- function matchSegments(str) {
1045
- const match = str.match(SEGMENT_RE);
1046
- return match ? match[0] : '';
1063
+ function findStartingPosition(nav, tree, segmentGroup, lastPathIndex) {
1064
+ if (nav.isAbsolute) {
1065
+ return new Position(tree.root, true, 0);
1066
+ }
1067
+ if (lastPathIndex === -1) {
1068
+ // Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
1069
+ // see issue #26224, #13011, #35687
1070
+ // However, if the ActivatedRoute is the root we should process children like above.
1071
+ const processChildren = segmentGroup === tree.root;
1072
+ return new Position(segmentGroup, processChildren, 0);
1073
+ }
1074
+ const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
1075
+ const index = lastPathIndex + modifier;
1076
+ return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
1047
1077
  }
1048
- const QUERY_PARAM_RE = /^[^=?&#]+/;
1049
- // Return the name of the query param at the start of the string or an empty string
1050
- function matchQueryParams(str) {
1051
- const match = str.match(QUERY_PARAM_RE);
1052
- return match ? match[0] : '';
1078
+ function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) {
1079
+ let g = group;
1080
+ let ci = index;
1081
+ let dd = numberOfDoubleDots;
1082
+ while (dd > ci) {
1083
+ dd -= ci;
1084
+ g = g.parent;
1085
+ if (!g) {
1086
+ throw new Error('Invalid number of \'../\'');
1087
+ }
1088
+ ci = g.segments.length;
1089
+ }
1090
+ return new Position(g, false, ci - dd);
1053
1091
  }
1054
- const QUERY_PARAM_VALUE_RE = /^[^&#]+/;
1055
- // Return the value of the query param at the start of the string or an empty string
1056
- function matchUrlQueryParamValue(str) {
1057
- const match = str.match(QUERY_PARAM_VALUE_RE);
1058
- return match ? match[0] : '';
1092
+ function getOutlets(commands) {
1093
+ if (isCommandWithOutlets(commands[0])) {
1094
+ return commands[0].outlets;
1095
+ }
1096
+ return { [PRIMARY_OUTLET]: commands };
1059
1097
  }
1060
- class UrlParser {
1061
- constructor(url) {
1062
- this.url = url;
1063
- this.remaining = url;
1098
+ function updateSegmentGroup(segmentGroup, startIndex, commands) {
1099
+ if (!segmentGroup) {
1100
+ segmentGroup = new UrlSegmentGroup([], {});
1064
1101
  }
1065
- parseRootSegment() {
1066
- this.consumeOptional('/');
1067
- if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {
1068
- return new UrlSegmentGroup([], {});
1069
- }
1070
- // The root segment group never has segments
1071
- return new UrlSegmentGroup([], this.parseChildren());
1102
+ if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
1103
+ return updateSegmentGroupChildren(segmentGroup, startIndex, commands);
1072
1104
  }
1073
- parseQueryParams() {
1074
- const params = {};
1075
- if (this.consumeOptional('?')) {
1076
- do {
1077
- this.parseQueryParam(params);
1078
- } while (this.consumeOptional('&'));
1079
- }
1080
- return params;
1105
+ const m = prefixedWith(segmentGroup, startIndex, commands);
1106
+ const slicedCommands = commands.slice(m.commandIndex);
1107
+ if (m.match && m.pathIndex < segmentGroup.segments.length) {
1108
+ const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});
1109
+ g.children[PRIMARY_OUTLET] =
1110
+ new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children);
1111
+ return updateSegmentGroupChildren(g, 0, slicedCommands);
1081
1112
  }
1082
- parseFragment() {
1083
- return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null;
1113
+ else if (m.match && slicedCommands.length === 0) {
1114
+ return new UrlSegmentGroup(segmentGroup.segments, {});
1084
1115
  }
1085
- parseChildren() {
1086
- if (this.remaining === '') {
1087
- return {};
1088
- }
1089
- this.consumeOptional('/');
1090
- const segments = [];
1091
- if (!this.peekStartsWith('(')) {
1092
- segments.push(this.parseSegment());
1093
- }
1094
- while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) {
1095
- this.capture('/');
1096
- segments.push(this.parseSegment());
1097
- }
1098
- let children = {};
1099
- if (this.peekStartsWith('/(')) {
1100
- this.capture('/');
1101
- children = this.parseParens(true);
1102
- }
1103
- let res = {};
1104
- if (this.peekStartsWith('(')) {
1105
- res = this.parseParens(false);
1106
- }
1107
- if (segments.length > 0 || Object.keys(children).length > 0) {
1108
- res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);
1109
- }
1110
- return res;
1116
+ else if (m.match && !segmentGroup.hasChildren()) {
1117
+ return createNewSegmentGroup(segmentGroup, startIndex, commands);
1111
1118
  }
1112
- // parse a segment with its matrix parameters
1113
- // ie `name;k1=v1;k2`
1114
- parseSegment() {
1115
- const path = matchSegments(this.remaining);
1116
- if (path === '' && this.peekStartsWith(';')) {
1117
- throw new Error(`Empty path url segment cannot have parameters: '${this.remaining}'.`);
1118
- }
1119
- this.capture(path);
1120
- return new UrlSegment(decode(path), this.parseMatrixParams());
1119
+ else if (m.match) {
1120
+ return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands);
1121
1121
  }
1122
- parseMatrixParams() {
1123
- const params = {};
1124
- while (this.consumeOptional(';')) {
1125
- this.parseParam(params);
1126
- }
1127
- return params;
1122
+ else {
1123
+ return createNewSegmentGroup(segmentGroup, startIndex, commands);
1128
1124
  }
1129
- parseParam(params) {
1130
- const key = matchSegments(this.remaining);
1131
- if (!key) {
1132
- return;
1133
- }
1134
- this.capture(key);
1135
- let value = '';
1136
- if (this.consumeOptional('=')) {
1137
- const valueMatch = matchSegments(this.remaining);
1138
- if (valueMatch) {
1139
- value = valueMatch;
1140
- this.capture(value);
1125
+ }
1126
+ function updateSegmentGroupChildren(segmentGroup, startIndex, commands) {
1127
+ if (commands.length === 0) {
1128
+ return new UrlSegmentGroup(segmentGroup.segments, {});
1129
+ }
1130
+ else {
1131
+ const outlets = getOutlets(commands);
1132
+ const children = {};
1133
+ forEach(outlets, (commands, outlet) => {
1134
+ if (typeof commands === 'string') {
1135
+ commands = [commands];
1136
+ }
1137
+ if (commands !== null) {
1138
+ children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);
1139
+ }
1140
+ });
1141
+ forEach(segmentGroup.children, (child, childOutlet) => {
1142
+ if (outlets[childOutlet] === undefined) {
1143
+ children[childOutlet] = child;
1141
1144
  }
1145
+ });
1146
+ return new UrlSegmentGroup(segmentGroup.segments, children);
1147
+ }
1148
+ }
1149
+ function prefixedWith(segmentGroup, startIndex, commands) {
1150
+ let currentCommandIndex = 0;
1151
+ let currentPathIndex = startIndex;
1152
+ const noMatch = { match: false, pathIndex: 0, commandIndex: 0 };
1153
+ while (currentPathIndex < segmentGroup.segments.length) {
1154
+ if (currentCommandIndex >= commands.length)
1155
+ return noMatch;
1156
+ const path = segmentGroup.segments[currentPathIndex];
1157
+ const command = commands[currentCommandIndex];
1158
+ // Do not try to consume command as part of the prefixing if it has outlets because it can
1159
+ // contain outlets other than the one being processed. Consuming the outlets command would
1160
+ // result in other outlets being ignored.
1161
+ if (isCommandWithOutlets(command)) {
1162
+ break;
1142
1163
  }
1143
- params[decode(key)] = decode(value);
1164
+ const curr = `${command}`;
1165
+ const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;
1166
+ if (currentPathIndex > 0 && curr === undefined)
1167
+ break;
1168
+ if (curr && next && (typeof next === 'object') && next.outlets === undefined) {
1169
+ if (!compare(curr, next, path))
1170
+ return noMatch;
1171
+ currentCommandIndex += 2;
1172
+ }
1173
+ else {
1174
+ if (!compare(curr, {}, path))
1175
+ return noMatch;
1176
+ currentCommandIndex++;
1177
+ }
1178
+ currentPathIndex++;
1144
1179
  }
1145
- // Parse a single query parameter `name[=value]`
1146
- parseQueryParam(params) {
1147
- const key = matchQueryParams(this.remaining);
1148
- if (!key) {
1149
- return;
1180
+ return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex };
1181
+ }
1182
+ function createNewSegmentGroup(segmentGroup, startIndex, commands) {
1183
+ const paths = segmentGroup.segments.slice(0, startIndex);
1184
+ let i = 0;
1185
+ while (i < commands.length) {
1186
+ const command = commands[i];
1187
+ if (isCommandWithOutlets(command)) {
1188
+ const children = createNewSegmentChildren(command.outlets);
1189
+ return new UrlSegmentGroup(paths, children);
1150
1190
  }
1151
- this.capture(key);
1152
- let value = '';
1153
- if (this.consumeOptional('=')) {
1154
- const valueMatch = matchUrlQueryParamValue(this.remaining);
1155
- if (valueMatch) {
1156
- value = valueMatch;
1157
- this.capture(value);
1158
- }
1191
+ // if we start with an object literal, we need to reuse the path part from the segment
1192
+ if (i === 0 && isMatrixParams(commands[0])) {
1193
+ const p = segmentGroup.segments[startIndex];
1194
+ paths.push(new UrlSegment(p.path, stringify(commands[0])));
1195
+ i++;
1196
+ continue;
1159
1197
  }
1160
- const decodedKey = decodeQuery(key);
1161
- const decodedVal = decodeQuery(value);
1162
- if (params.hasOwnProperty(decodedKey)) {
1163
- // Append to existing values
1164
- let currentVal = params[decodedKey];
1165
- if (!Array.isArray(currentVal)) {
1166
- currentVal = [currentVal];
1167
- params[decodedKey] = currentVal;
1168
- }
1169
- currentVal.push(decodedVal);
1198
+ const curr = isCommandWithOutlets(command) ? command.outlets[PRIMARY_OUTLET] : `${command}`;
1199
+ const next = (i < commands.length - 1) ? commands[i + 1] : null;
1200
+ if (curr && next && isMatrixParams(next)) {
1201
+ paths.push(new UrlSegment(curr, stringify(next)));
1202
+ i += 2;
1170
1203
  }
1171
1204
  else {
1172
- // Create a new value
1173
- params[decodedKey] = decodedVal;
1205
+ paths.push(new UrlSegment(curr, {}));
1206
+ i++;
1174
1207
  }
1175
1208
  }
1176
- // parse `(a/b//outlet_name:c/d)`
1177
- parseParens(allowPrimary) {
1178
- const segments = {};
1179
- this.capture('(');
1180
- while (!this.consumeOptional(')') && this.remaining.length > 0) {
1181
- const path = matchSegments(this.remaining);
1182
- const next = this.remaining[path.length];
1183
- // if is is not one of these characters, then the segment was unescaped
1184
- // or the group was not closed
1185
- if (next !== '/' && next !== ')' && next !== ';') {
1186
- throw new Error(`Cannot parse url '${this.url}'`);
1187
- }
1188
- let outletName = undefined;
1189
- if (path.indexOf(':') > -1) {
1190
- outletName = path.slice(0, path.indexOf(':'));
1191
- this.capture(outletName);
1192
- this.capture(':');
1193
- }
1194
- else if (allowPrimary) {
1195
- outletName = PRIMARY_OUTLET;
1196
- }
1197
- const children = this.parseChildren();
1198
- segments[outletName] = Object.keys(children).length === 1 ? children[PRIMARY_OUTLET] :
1199
- new UrlSegmentGroup([], children);
1200
- this.consumeOptional('//');
1209
+ return new UrlSegmentGroup(paths, {});
1210
+ }
1211
+ function createNewSegmentChildren(outlets) {
1212
+ const children = {};
1213
+ forEach(outlets, (commands, outlet) => {
1214
+ if (typeof commands === 'string') {
1215
+ commands = [commands];
1201
1216
  }
1202
- return segments;
1217
+ if (commands !== null) {
1218
+ children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands);
1219
+ }
1220
+ });
1221
+ return children;
1222
+ }
1223
+ function stringify(params) {
1224
+ const res = {};
1225
+ forEach(params, (v, k) => res[k] = `${v}`);
1226
+ return res;
1227
+ }
1228
+ function compare(path, params, segment) {
1229
+ return path == segment.path && shallowEqual(params, segment.parameters);
1230
+ }
1231
+
1232
+ /**
1233
+ * @license
1234
+ * Copyright Google LLC All Rights Reserved.
1235
+ *
1236
+ * Use of this source code is governed by an MIT-style license that can be
1237
+ * found in the LICENSE file at https://angular.io/license
1238
+ */
1239
+ /**
1240
+ * Base for events the router goes through, as opposed to events tied to a specific
1241
+ * route. Fired one time for any given navigation.
1242
+ *
1243
+ * The following code shows how a class subscribes to router events.
1244
+ *
1245
+ * ```ts
1246
+ * import {Event, RouterEvent, Router} from '@angular/router';
1247
+ *
1248
+ * class MyService {
1249
+ * constructor(public router: Router) {
1250
+ * router.events.pipe(
1251
+ * filter((e: Event): e is RouterEvent => e instanceof RouterEvent)
1252
+ * ).subscribe((e: RouterEvent) => {
1253
+ * // Do something
1254
+ * });
1255
+ * }
1256
+ * }
1257
+ * ```
1258
+ *
1259
+ * @see `Event`
1260
+ * @see [Router events summary](guide/router-reference#router-events)
1261
+ * @publicApi
1262
+ */
1263
+ class RouterEvent {
1264
+ constructor(
1265
+ /** A unique ID that the router assigns to every router navigation. */
1266
+ id,
1267
+ /** The URL that is the destination for this navigation. */
1268
+ url) {
1269
+ this.id = id;
1270
+ this.url = url;
1271
+ }
1272
+ }
1273
+ /**
1274
+ * An event triggered when a navigation starts.
1275
+ *
1276
+ * @publicApi
1277
+ */
1278
+ class NavigationStart extends RouterEvent {
1279
+ constructor(
1280
+ /** @docsNotRequired */
1281
+ id,
1282
+ /** @docsNotRequired */
1283
+ url,
1284
+ /** @docsNotRequired */
1285
+ navigationTrigger = 'imperative',
1286
+ /** @docsNotRequired */
1287
+ restoredState = null) {
1288
+ super(id, url);
1289
+ this.type = 0 /* EventType.NavigationStart */;
1290
+ this.navigationTrigger = navigationTrigger;
1291
+ this.restoredState = restoredState;
1292
+ }
1293
+ /** @docsNotRequired */
1294
+ toString() {
1295
+ return `NavigationStart(id: ${this.id}, url: '${this.url}')`;
1296
+ }
1297
+ }
1298
+ /**
1299
+ * An event triggered when a navigation ends successfully.
1300
+ *
1301
+ * @see `NavigationStart`
1302
+ * @see `NavigationCancel`
1303
+ * @see `NavigationError`
1304
+ *
1305
+ * @publicApi
1306
+ */
1307
+ class NavigationEnd extends RouterEvent {
1308
+ constructor(
1309
+ /** @docsNotRequired */
1310
+ id,
1311
+ /** @docsNotRequired */
1312
+ url,
1313
+ /** @docsNotRequired */
1314
+ urlAfterRedirects) {
1315
+ super(id, url);
1316
+ this.urlAfterRedirects = urlAfterRedirects;
1317
+ this.type = 1 /* EventType.NavigationEnd */;
1203
1318
  }
1204
- peekStartsWith(str) {
1205
- return this.remaining.startsWith(str);
1319
+ /** @docsNotRequired */
1320
+ toString() {
1321
+ return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`;
1206
1322
  }
1207
- // Consumes the prefix when it is present and returns whether it has been consumed
1208
- consumeOptional(str) {
1209
- if (this.peekStartsWith(str)) {
1210
- this.remaining = this.remaining.substring(str.length);
1211
- return true;
1212
- }
1213
- return false;
1323
+ }
1324
+ /**
1325
+ * An event triggered when a navigation is canceled, directly or indirectly.
1326
+ * This can happen for several reasons including when a route guard
1327
+ * returns `false` or initiates a redirect by returning a `UrlTree`.
1328
+ *
1329
+ * @see `NavigationStart`
1330
+ * @see `NavigationEnd`
1331
+ * @see `NavigationError`
1332
+ *
1333
+ * @publicApi
1334
+ */
1335
+ class NavigationCancel extends RouterEvent {
1336
+ constructor(
1337
+ /** @docsNotRequired */
1338
+ id,
1339
+ /** @docsNotRequired */
1340
+ url,
1341
+ /** @docsNotRequired */
1342
+ reason) {
1343
+ super(id, url);
1344
+ this.reason = reason;
1345
+ this.type = 2 /* EventType.NavigationCancel */;
1214
1346
  }
1215
- capture(str) {
1216
- if (!this.consumeOptional(str)) {
1217
- throw new Error(`Expected "${str}".`);
1218
- }
1347
+ /** @docsNotRequired */
1348
+ toString() {
1349
+ return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;
1219
1350
  }
1220
1351
  }
1221
-
1222
1352
  /**
1223
- * @license
1224
- * Copyright Google LLC All Rights Reserved.
1353
+ * An event triggered when a navigation fails due to an unexpected error.
1225
1354
  *
1226
- * Use of this source code is governed by an MIT-style license that can be
1227
- * found in the LICENSE file at https://angular.io/license
1355
+ * @see `NavigationStart`
1356
+ * @see `NavigationEnd`
1357
+ * @see `NavigationCancel`
1358
+ *
1359
+ * @publicApi
1228
1360
  */
1229
- class Tree {
1230
- constructor(root) {
1231
- this._root = root;
1232
- }
1233
- get root() {
1234
- return this._root.value;
1361
+ class NavigationError extends RouterEvent {
1362
+ constructor(
1363
+ /** @docsNotRequired */
1364
+ id,
1365
+ /** @docsNotRequired */
1366
+ url,
1367
+ /** @docsNotRequired */
1368
+ error) {
1369
+ super(id, url);
1370
+ this.error = error;
1371
+ this.type = 3 /* EventType.NavigationError */;
1235
1372
  }
1236
- /**
1237
- * @internal
1238
- */
1239
- parent(t) {
1240
- const p = this.pathFromRoot(t);
1241
- return p.length > 1 ? p[p.length - 2] : null;
1373
+ /** @docsNotRequired */
1374
+ toString() {
1375
+ return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`;
1242
1376
  }
1243
- /**
1244
- * @internal
1245
- */
1246
- children(t) {
1247
- const n = findNode(t, this._root);
1248
- return n ? n.children.map(t => t.value) : [];
1377
+ }
1378
+ /**
1379
+ * An event triggered when routes are recognized.
1380
+ *
1381
+ * @publicApi
1382
+ */
1383
+ class RoutesRecognized extends RouterEvent {
1384
+ constructor(
1385
+ /** @docsNotRequired */
1386
+ id,
1387
+ /** @docsNotRequired */
1388
+ url,
1389
+ /** @docsNotRequired */
1390
+ urlAfterRedirects,
1391
+ /** @docsNotRequired */
1392
+ state) {
1393
+ super(id, url);
1394
+ this.urlAfterRedirects = urlAfterRedirects;
1395
+ this.state = state;
1396
+ this.type = 4 /* EventType.RoutesRecognized */;
1249
1397
  }
1250
- /**
1251
- * @internal
1252
- */
1253
- firstChild(t) {
1254
- const n = findNode(t, this._root);
1255
- return n && n.children.length > 0 ? n.children[0].value : null;
1398
+ /** @docsNotRequired */
1399
+ toString() {
1400
+ return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
1256
1401
  }
1257
- /**
1258
- * @internal
1259
- */
1260
- siblings(t) {
1261
- const p = findPath(t, this._root);
1262
- if (p.length < 2)
1263
- return [];
1264
- const c = p[p.length - 2].children.map(c => c.value);
1265
- return c.filter(cc => cc !== t);
1402
+ }
1403
+ /**
1404
+ * An event triggered at the start of the Guard phase of routing.
1405
+ *
1406
+ * @see `GuardsCheckEnd`
1407
+ *
1408
+ * @publicApi
1409
+ */
1410
+ class GuardsCheckStart extends RouterEvent {
1411
+ constructor(
1412
+ /** @docsNotRequired */
1413
+ id,
1414
+ /** @docsNotRequired */
1415
+ url,
1416
+ /** @docsNotRequired */
1417
+ urlAfterRedirects,
1418
+ /** @docsNotRequired */
1419
+ state) {
1420
+ super(id, url);
1421
+ this.urlAfterRedirects = urlAfterRedirects;
1422
+ this.state = state;
1423
+ this.type = 7 /* EventType.GuardsCheckStart */;
1266
1424
  }
1267
- /**
1268
- * @internal
1269
- */
1270
- pathFromRoot(t) {
1271
- return findPath(t, this._root).map(s => s.value);
1425
+ toString() {
1426
+ return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
1272
1427
  }
1273
1428
  }
1274
- // DFS for the node matching the value
1275
- function findNode(value, node) {
1276
- if (value === node.value)
1277
- return node;
1278
- for (const child of node.children) {
1279
- const node = findNode(value, child);
1280
- if (node)
1281
- return node;
1429
+ /**
1430
+ * An event triggered at the end of the Guard phase of routing.
1431
+ *
1432
+ * @see `GuardsCheckStart`
1433
+ *
1434
+ * @publicApi
1435
+ */
1436
+ class GuardsCheckEnd extends RouterEvent {
1437
+ constructor(
1438
+ /** @docsNotRequired */
1439
+ id,
1440
+ /** @docsNotRequired */
1441
+ url,
1442
+ /** @docsNotRequired */
1443
+ urlAfterRedirects,
1444
+ /** @docsNotRequired */
1445
+ state,
1446
+ /** @docsNotRequired */
1447
+ shouldActivate) {
1448
+ super(id, url);
1449
+ this.urlAfterRedirects = urlAfterRedirects;
1450
+ this.state = state;
1451
+ this.shouldActivate = shouldActivate;
1452
+ this.type = 8 /* EventType.GuardsCheckEnd */;
1453
+ }
1454
+ toString() {
1455
+ return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
1282
1456
  }
1283
- return null;
1284
1457
  }
1285
- // Return the path to the node with the given value using DFS
1286
- function findPath(value, node) {
1287
- if (value === node.value)
1288
- return [node];
1289
- for (const child of node.children) {
1290
- const path = findPath(value, child);
1291
- if (path.length) {
1292
- path.unshift(node);
1293
- return path;
1294
- }
1458
+ /**
1459
+ * An event triggered at the start of the Resolve phase of routing.
1460
+ *
1461
+ * Runs in the "resolve" phase whether or not there is anything to resolve.
1462
+ * In future, may change to only run when there are things to be resolved.
1463
+ *
1464
+ * @see `ResolveEnd`
1465
+ *
1466
+ * @publicApi
1467
+ */
1468
+ class ResolveStart extends RouterEvent {
1469
+ constructor(
1470
+ /** @docsNotRequired */
1471
+ id,
1472
+ /** @docsNotRequired */
1473
+ url,
1474
+ /** @docsNotRequired */
1475
+ urlAfterRedirects,
1476
+ /** @docsNotRequired */
1477
+ state) {
1478
+ super(id, url);
1479
+ this.urlAfterRedirects = urlAfterRedirects;
1480
+ this.state = state;
1481
+ this.type = 5 /* EventType.ResolveStart */;
1482
+ }
1483
+ toString() {
1484
+ return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
1295
1485
  }
1296
- return [];
1297
1486
  }
1298
- class TreeNode {
1299
- constructor(value, children) {
1300
- this.value = value;
1301
- this.children = children;
1487
+ /**
1488
+ * An event triggered at the end of the Resolve phase of routing.
1489
+ * @see `ResolveStart`.
1490
+ *
1491
+ * @publicApi
1492
+ */
1493
+ class ResolveEnd extends RouterEvent {
1494
+ constructor(
1495
+ /** @docsNotRequired */
1496
+ id,
1497
+ /** @docsNotRequired */
1498
+ url,
1499
+ /** @docsNotRequired */
1500
+ urlAfterRedirects,
1501
+ /** @docsNotRequired */
1502
+ state) {
1503
+ super(id, url);
1504
+ this.urlAfterRedirects = urlAfterRedirects;
1505
+ this.state = state;
1506
+ this.type = 6 /* EventType.ResolveEnd */;
1302
1507
  }
1303
1508
  toString() {
1304
- return `TreeNode(${this.value})`;
1305
- }
1306
- }
1307
- // Return the list of T indexed by outlet name
1308
- function nodeChildrenAsMap(node) {
1309
- const map = {};
1310
- if (node) {
1311
- node.children.forEach(child => map[child.value.outlet] = child);
1509
+ return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
1312
1510
  }
1313
- return map;
1314
1511
  }
1315
-
1316
1512
  /**
1317
- * @license
1318
- * Copyright Google LLC All Rights Reserved.
1513
+ * An event triggered before lazy loading a route configuration.
1319
1514
  *
1320
- * Use of this source code is governed by an MIT-style license that can be
1321
- * found in the LICENSE file at https://angular.io/license
1515
+ * @see `RouteConfigLoadEnd`
1516
+ *
1517
+ * @publicApi
1322
1518
  */
1519
+ class RouteConfigLoadStart {
1520
+ constructor(
1521
+ /** @docsNotRequired */
1522
+ route) {
1523
+ this.route = route;
1524
+ this.type = 9 /* EventType.RouteConfigLoadStart */;
1525
+ }
1526
+ toString() {
1527
+ return `RouteConfigLoadStart(path: ${this.route.path})`;
1528
+ }
1529
+ }
1323
1530
  /**
1324
- * Represents the state of the router as a tree of activated routes.
1325
- *
1326
- * @usageNotes
1327
- *
1328
- * Every node in the route tree is an `ActivatedRoute` instance
1329
- * that knows about the "consumed" URL segments, the extracted parameters,
1330
- * and the resolved data.
1331
- * Use the `ActivatedRoute` properties to traverse the tree from any node.
1332
- *
1333
- * The following fragment shows how a component gets the root node
1334
- * of the current state to establish its own route tree:
1335
- *
1336
- * ```
1337
- * @Component({templateUrl:'template.html'})
1338
- * class MyComponent {
1339
- * constructor(router: Router) {
1340
- * const state: RouterState = router.routerState;
1341
- * const root: ActivatedRoute = state.root;
1342
- * const child = root.firstChild;
1343
- * const id: Observable<string> = child.params.map(p => p.id);
1344
- * //...
1345
- * }
1346
- * }
1347
- * ```
1531
+ * An event triggered when a route has been lazy loaded.
1348
1532
  *
1349
- * @see `ActivatedRoute`
1350
- * @see [Getting route information](guide/router#getting-route-information)
1533
+ * @see `RouteConfigLoadStart`
1351
1534
  *
1352
1535
  * @publicApi
1353
1536
  */
1354
- class RouterState extends Tree {
1355
- /** @internal */
1356
- constructor(root,
1357
- /** The current snapshot of the router state */
1358
- snapshot) {
1359
- super(root);
1360
- this.snapshot = snapshot;
1361
- setRouterState(this, root);
1537
+ class RouteConfigLoadEnd {
1538
+ constructor(
1539
+ /** @docsNotRequired */
1540
+ route) {
1541
+ this.route = route;
1542
+ this.type = 10 /* EventType.RouteConfigLoadEnd */;
1362
1543
  }
1363
1544
  toString() {
1364
- return this.snapshot.toString();
1545
+ return `RouteConfigLoadEnd(path: ${this.route.path})`;
1365
1546
  }
1366
1547
  }
1367
- function createEmptyState(urlTree, rootComponent) {
1368
- const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
1369
- const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
1370
- const emptyParams = new BehaviorSubject({});
1371
- const emptyData = new BehaviorSubject({});
1372
- const emptyQueryParams = new BehaviorSubject({});
1373
- const fragment = new BehaviorSubject('');
1374
- const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root);
1375
- activated.snapshot = snapshot.root;
1376
- return new RouterState(new TreeNode(activated, []), snapshot);
1377
- }
1378
- function createEmptyStateSnapshot(urlTree, rootComponent) {
1379
- const emptyParams = {};
1380
- const emptyData = {};
1381
- const emptyQueryParams = {};
1382
- const fragment = '';
1383
- const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {});
1384
- return new RouterStateSnapshot('', new TreeNode(activated, []));
1385
- }
1386
1548
  /**
1387
- * Provides access to information about a route associated with a component
1388
- * that is loaded in an outlet.
1389
- * Use to traverse the `RouterState` tree and extract information from nodes.
1390
- *
1391
- * The following example shows how to construct a component using information from a
1392
- * currently activated route.
1393
- *
1394
- * Note: the observables in this class only emit when the current and previous values differ based
1395
- * on shallow equality. For example, changing deeply nested properties in resolved `data` will not
1396
- * cause the `ActivatedRoute.data` `Observable` to emit a new value.
1397
- *
1398
- * {@example router/activated-route/module.ts region="activated-route"
1399
- * header="activated-route.component.ts"}
1400
- *
1401
- * @see [Getting route information](guide/router#getting-route-information)
1549
+ * An event triggered at the start of the child-activation
1550
+ * part of the Resolve phase of routing.
1551
+ * @see `ChildActivationEnd`
1552
+ * @see `ResolveStart`
1402
1553
  *
1403
1554
  * @publicApi
1404
1555
  */
1405
- class ActivatedRoute {
1406
- /** @internal */
1556
+ class ChildActivationStart {
1407
1557
  constructor(
1408
- /** An observable of the URL segments matched by this route. */
1409
- url,
1410
- /** An observable of the matrix parameters scoped to this route. */
1411
- params,
1412
- /** An observable of the query parameters shared by all the routes. */
1413
- queryParams,
1414
- /** An observable of the URL fragment shared by all the routes. */
1415
- fragment,
1416
- /** An observable of the static and resolved data of this route. */
1417
- data,
1418
- /** The outlet name of the route, a constant. */
1419
- outlet,
1420
- /** The component of the route, a constant. */
1421
- component, futureSnapshot) {
1422
- this.url = url;
1423
- this.params = params;
1424
- this.queryParams = queryParams;
1425
- this.fragment = fragment;
1426
- this.data = data;
1427
- this.outlet = outlet;
1428
- this.component = component;
1429
- this._futureSnapshot = futureSnapshot;
1558
+ /** @docsNotRequired */
1559
+ snapshot) {
1560
+ this.snapshot = snapshot;
1561
+ this.type = 11 /* EventType.ChildActivationStart */;
1430
1562
  }
1431
- /** The configuration used to match this route. */
1432
- get routeConfig() {
1433
- return this._futureSnapshot.routeConfig;
1563
+ toString() {
1564
+ const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
1565
+ return `ChildActivationStart(path: '${path}')`;
1434
1566
  }
1435
- /** The root of the router state. */
1436
- get root() {
1437
- return this._routerState.root;
1567
+ }
1568
+ /**
1569
+ * An event triggered at the end of the child-activation part
1570
+ * of the Resolve phase of routing.
1571
+ * @see `ChildActivationStart`
1572
+ * @see `ResolveStart`
1573
+ * @publicApi
1574
+ */
1575
+ class ChildActivationEnd {
1576
+ constructor(
1577
+ /** @docsNotRequired */
1578
+ snapshot) {
1579
+ this.snapshot = snapshot;
1580
+ this.type = 12 /* EventType.ChildActivationEnd */;
1438
1581
  }
1439
- /** The parent of this route in the router state tree. */
1440
- get parent() {
1441
- return this._routerState.parent(this);
1582
+ toString() {
1583
+ const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
1584
+ return `ChildActivationEnd(path: '${path}')`;
1442
1585
  }
1443
- /** The first child of this route in the router state tree. */
1444
- get firstChild() {
1445
- return this._routerState.firstChild(this);
1586
+ }
1587
+ /**
1588
+ * An event triggered at the start of the activation part
1589
+ * of the Resolve phase of routing.
1590
+ * @see `ActivationEnd`
1591
+ * @see `ResolveStart`
1592
+ *
1593
+ * @publicApi
1594
+ */
1595
+ class ActivationStart {
1596
+ constructor(
1597
+ /** @docsNotRequired */
1598
+ snapshot) {
1599
+ this.snapshot = snapshot;
1600
+ this.type = 13 /* EventType.ActivationStart */;
1446
1601
  }
1447
- /** The children of this route in the router state tree. */
1448
- get children() {
1449
- return this._routerState.children(this);
1602
+ toString() {
1603
+ const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
1604
+ return `ActivationStart(path: '${path}')`;
1450
1605
  }
1451
- /** The path from the root of the router state tree to this route. */
1452
- get pathFromRoot() {
1453
- return this._routerState.pathFromRoot(this);
1606
+ }
1607
+ /**
1608
+ * An event triggered at the end of the activation part
1609
+ * of the Resolve phase of routing.
1610
+ * @see `ActivationStart`
1611
+ * @see `ResolveStart`
1612
+ *
1613
+ * @publicApi
1614
+ */
1615
+ class ActivationEnd {
1616
+ constructor(
1617
+ /** @docsNotRequired */
1618
+ snapshot) {
1619
+ this.snapshot = snapshot;
1620
+ this.type = 14 /* EventType.ActivationEnd */;
1454
1621
  }
1455
- /**
1456
- * An Observable that contains a map of the required and optional parameters
1457
- * specific to the route.
1458
- * The map supports retrieving single and multiple values from the same parameter.
1459
- */
1460
- get paramMap() {
1461
- if (!this._paramMap) {
1462
- this._paramMap = this.params.pipe(map((p) => convertToParamMap(p)));
1463
- }
1464
- return this._paramMap;
1622
+ toString() {
1623
+ const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
1624
+ return `ActivationEnd(path: '${path}')`;
1465
1625
  }
1466
- /**
1467
- * An Observable that contains a map of the query parameters available to all routes.
1468
- * The map supports retrieving single and multiple values from the query parameter.
1469
- */
1470
- get queryParamMap() {
1471
- if (!this._queryParamMap) {
1472
- this._queryParamMap =
1473
- this.queryParams.pipe(map((p) => convertToParamMap(p)));
1474
- }
1475
- return this._queryParamMap;
1626
+ }
1627
+ /**
1628
+ * An event triggered by scrolling.
1629
+ *
1630
+ * @publicApi
1631
+ */
1632
+ class Scroll {
1633
+ constructor(
1634
+ /** @docsNotRequired */
1635
+ routerEvent,
1636
+ /** @docsNotRequired */
1637
+ position,
1638
+ /** @docsNotRequired */
1639
+ anchor) {
1640
+ this.routerEvent = routerEvent;
1641
+ this.position = position;
1642
+ this.anchor = anchor;
1643
+ this.type = 15 /* EventType.Scroll */;
1476
1644
  }
1477
1645
  toString() {
1478
- return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
1646
+ const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;
1647
+ return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;
1479
1648
  }
1480
1649
  }
1481
- /**
1482
- * Returns the inherited params, data, and resolve for a given route.
1483
- * By default, this only inherits values up to the nearest path-less or component-less route.
1484
- * @internal
1485
- */
1486
- function inheritedParamsDataResolve(route, paramsInheritanceStrategy = 'emptyOnly') {
1487
- const pathFromRoot = route.pathFromRoot;
1488
- let inheritingStartingFrom = 0;
1489
- if (paramsInheritanceStrategy !== 'always') {
1490
- inheritingStartingFrom = pathFromRoot.length - 1;
1491
- while (inheritingStartingFrom >= 1) {
1492
- const current = pathFromRoot[inheritingStartingFrom];
1493
- const parent = pathFromRoot[inheritingStartingFrom - 1];
1494
- // current route is an empty path => inherits its parent's params and data
1495
- if (current.routeConfig && current.routeConfig.path === '') {
1496
- inheritingStartingFrom--;
1497
- // parent is componentless => current route should inherit its params and data
1498
- }
1499
- else if (!parent.component) {
1500
- inheritingStartingFrom--;
1501
- }
1502
- else {
1503
- break;
1504
- }
1505
- }
1650
+ function stringifyEvent(routerEvent) {
1651
+ if (!('type' in routerEvent)) {
1652
+ return `Unknown Router Event: ${routerEvent.constructor.name}`;
1653
+ }
1654
+ switch (routerEvent.type) {
1655
+ case 14 /* EventType.ActivationEnd */:
1656
+ return `ActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
1657
+ case 13 /* EventType.ActivationStart */:
1658
+ return `ActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
1659
+ case 12 /* EventType.ChildActivationEnd */:
1660
+ return `ChildActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
1661
+ case 11 /* EventType.ChildActivationStart */:
1662
+ return `ChildActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
1663
+ case 8 /* EventType.GuardsCheckEnd */:
1664
+ return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;
1665
+ case 7 /* EventType.GuardsCheckStart */:
1666
+ return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
1667
+ case 2 /* EventType.NavigationCancel */:
1668
+ return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
1669
+ case 1 /* EventType.NavigationEnd */:
1670
+ return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;
1671
+ case 3 /* EventType.NavigationError */:
1672
+ return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;
1673
+ case 0 /* EventType.NavigationStart */:
1674
+ return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
1675
+ case 6 /* EventType.ResolveEnd */:
1676
+ return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
1677
+ case 5 /* EventType.ResolveStart */:
1678
+ return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
1679
+ case 10 /* EventType.RouteConfigLoadEnd */:
1680
+ return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;
1681
+ case 9 /* EventType.RouteConfigLoadStart */:
1682
+ return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;
1683
+ case 4 /* EventType.RoutesRecognized */:
1684
+ return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
1685
+ case 15 /* EventType.Scroll */:
1686
+ const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null;
1687
+ return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;
1506
1688
  }
1507
- return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
1508
- }
1509
- /** @internal */
1510
- function flattenInherited(pathFromRoot) {
1511
- return pathFromRoot.reduce((res, curr) => {
1512
- const params = { ...res.params, ...curr.params };
1513
- const data = { ...res.data, ...curr.data };
1514
- const resolve = { ...curr.data, ...res.resolve, ...curr.routeConfig?.data, ...curr._resolvedData };
1515
- return { params, data, resolve };
1516
- }, { params: {}, data: {}, resolve: {} });
1517
1689
  }
1690
+
1518
1691
  /**
1519
- * @description
1520
- *
1521
- * Contains the information about a route associated with a component loaded in an
1522
- * outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to
1523
- * traverse the router state tree.
1524
- *
1525
- * The following example initializes a component with route information extracted
1526
- * from the snapshot of the root node at the time of creation.
1527
- *
1528
- * ```
1529
- * @Component({templateUrl:'./my-component.html'})
1530
- * class MyComponent {
1531
- * constructor(route: ActivatedRoute) {
1532
- * const id: string = route.snapshot.params.id;
1533
- * const url: string = route.snapshot.url.join('');
1534
- * const user = route.snapshot.data.user;
1535
- * }
1536
- * }
1537
- * ```
1692
+ * @license
1693
+ * Copyright Google LLC All Rights Reserved.
1538
1694
  *
1539
- * @publicApi
1695
+ * Use of this source code is governed by an MIT-style license that can be
1696
+ * found in the LICENSE file at https://angular.io/license
1540
1697
  */
1541
- class ActivatedRouteSnapshot {
1542
- /** @internal */
1543
- constructor(
1544
- /** The URL segments matched by this route */
1545
- url,
1698
+ class Tree {
1699
+ constructor(root) {
1700
+ this._root = root;
1701
+ }
1702
+ get root() {
1703
+ return this._root.value;
1704
+ }
1546
1705
  /**
1547
- * The matrix parameters scoped to this route.
1548
- *
1549
- * You can compute all params (or data) in the router state or to get params outside
1550
- * of an activated component by traversing the `RouterState` tree as in the following
1551
- * example:
1552
- * ```
1553
- * collectRouteParams(router: Router) {
1554
- * let params = {};
1555
- * let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
1556
- * while (stack.length > 0) {
1557
- * const route = stack.pop()!;
1558
- * params = {...params, ...route.params};
1559
- * stack.push(...route.children);
1560
- * }
1561
- * return params;
1562
- * }
1563
- * ```
1706
+ * @internal
1564
1707
  */
1565
- params,
1566
- /** The query parameters shared by all the routes */
1567
- queryParams,
1568
- /** The URL fragment shared by all the routes */
1569
- fragment,
1570
- /** The static and resolved data of this route */
1571
- data,
1572
- /** The outlet name of the route */
1573
- outlet,
1574
- /** The component of the route */
1575
- component, routeConfig, urlSegment, lastPathIndex, resolve, correctedLastPathIndex) {
1576
- this.url = url;
1577
- this.params = params;
1578
- this.queryParams = queryParams;
1579
- this.fragment = fragment;
1580
- this.data = data;
1581
- this.outlet = outlet;
1582
- this.component = component;
1583
- this.routeConfig = routeConfig;
1584
- this._urlSegment = urlSegment;
1585
- this._lastPathIndex = lastPathIndex;
1586
- this._correctedLastPathIndex = correctedLastPathIndex ?? lastPathIndex;
1587
- this._resolve = resolve;
1708
+ parent(t) {
1709
+ const p = this.pathFromRoot(t);
1710
+ return p.length > 1 ? p[p.length - 2] : null;
1588
1711
  }
1589
- /** The root of the router state */
1590
- get root() {
1591
- return this._routerState.root;
1712
+ /**
1713
+ * @internal
1714
+ */
1715
+ children(t) {
1716
+ const n = findNode(t, this._root);
1717
+ return n ? n.children.map(t => t.value) : [];
1592
1718
  }
1593
- /** The parent of this route in the router state tree */
1594
- get parent() {
1595
- return this._routerState.parent(this);
1719
+ /**
1720
+ * @internal
1721
+ */
1722
+ firstChild(t) {
1723
+ const n = findNode(t, this._root);
1724
+ return n && n.children.length > 0 ? n.children[0].value : null;
1596
1725
  }
1597
- /** The first child of this route in the router state tree */
1598
- get firstChild() {
1599
- return this._routerState.firstChild(this);
1726
+ /**
1727
+ * @internal
1728
+ */
1729
+ siblings(t) {
1730
+ const p = findPath(t, this._root);
1731
+ if (p.length < 2)
1732
+ return [];
1733
+ const c = p[p.length - 2].children.map(c => c.value);
1734
+ return c.filter(cc => cc !== t);
1600
1735
  }
1601
- /** The children of this route in the router state tree */
1602
- get children() {
1603
- return this._routerState.children(this);
1736
+ /**
1737
+ * @internal
1738
+ */
1739
+ pathFromRoot(t) {
1740
+ return findPath(t, this._root).map(s => s.value);
1604
1741
  }
1605
- /** The path from the root of the router state tree to this route */
1606
- get pathFromRoot() {
1607
- return this._routerState.pathFromRoot(this);
1742
+ }
1743
+ // DFS for the node matching the value
1744
+ function findNode(value, node) {
1745
+ if (value === node.value)
1746
+ return node;
1747
+ for (const child of node.children) {
1748
+ const node = findNode(value, child);
1749
+ if (node)
1750
+ return node;
1608
1751
  }
1609
- get paramMap() {
1610
- if (!this._paramMap) {
1611
- this._paramMap = convertToParamMap(this.params);
1752
+ return null;
1753
+ }
1754
+ // Return the path to the node with the given value using DFS
1755
+ function findPath(value, node) {
1756
+ if (value === node.value)
1757
+ return [node];
1758
+ for (const child of node.children) {
1759
+ const path = findPath(value, child);
1760
+ if (path.length) {
1761
+ path.unshift(node);
1762
+ return path;
1612
1763
  }
1613
- return this._paramMap;
1614
1764
  }
1615
- get queryParamMap() {
1616
- if (!this._queryParamMap) {
1617
- this._queryParamMap = convertToParamMap(this.queryParams);
1618
- }
1619
- return this._queryParamMap;
1765
+ return [];
1766
+ }
1767
+ class TreeNode {
1768
+ constructor(value, children) {
1769
+ this.value = value;
1770
+ this.children = children;
1620
1771
  }
1621
1772
  toString() {
1622
- const url = this.url.map(segment => segment.toString()).join('/');
1623
- const matched = this.routeConfig ? this.routeConfig.path : '';
1624
- return `Route(url:'${url}', path:'${matched}')`;
1773
+ return `TreeNode(${this.value})`;
1774
+ }
1775
+ }
1776
+ // Return the list of T indexed by outlet name
1777
+ function nodeChildrenAsMap(node) {
1778
+ const map = {};
1779
+ if (node) {
1780
+ node.children.forEach(child => map[child.value.outlet] = child);
1625
1781
  }
1782
+ return map;
1626
1783
  }
1784
+
1627
1785
  /**
1628
- * @description
1786
+ * @license
1787
+ * Copyright Google LLC All Rights Reserved.
1629
1788
  *
1630
- * Represents the state of the router at a moment in time.
1789
+ * Use of this source code is governed by an MIT-style license that can be
1790
+ * found in the LICENSE file at https://angular.io/license
1791
+ */
1792
+ /**
1793
+ * Represents the state of the router as a tree of activated routes.
1794
+ *
1795
+ * @usageNotes
1631
1796
  *
1632
- * This is a tree of activated route snapshots. Every node in this tree knows about
1633
- * the "consumed" URL segments, the extracted parameters, and the resolved data.
1797
+ * Every node in the route tree is an `ActivatedRoute` instance
1798
+ * that knows about the "consumed" URL segments, the extracted parameters,
1799
+ * and the resolved data.
1800
+ * Use the `ActivatedRoute` properties to traverse the tree from any node.
1634
1801
  *
1635
- * The following example shows how a component is initialized with information
1636
- * from the snapshot of the root node's state at the time of creation.
1802
+ * The following fragment shows how a component gets the root node
1803
+ * of the current state to establish its own route tree:
1637
1804
  *
1638
1805
  * ```
1639
1806
  * @Component({templateUrl:'template.html'})
1640
1807
  * class MyComponent {
1641
1808
  * constructor(router: Router) {
1642
1809
  * const state: RouterState = router.routerState;
1643
- * const snapshot: RouterStateSnapshot = state.snapshot;
1644
- * const root: ActivatedRouteSnapshot = snapshot.root;
1810
+ * const root: ActivatedRoute = state.root;
1645
1811
  * const child = root.firstChild;
1646
1812
  * const id: Observable<string> = child.params.map(p => p.id);
1647
1813
  * //...
@@ -1649,423 +1815,418 @@ class ActivatedRouteSnapshot {
1649
1815
  * }
1650
1816
  * ```
1651
1817
  *
1818
+ * @see `ActivatedRoute`
1819
+ * @see [Getting route information](guide/router#getting-route-information)
1820
+ *
1652
1821
  * @publicApi
1653
1822
  */
1654
- class RouterStateSnapshot extends Tree {
1823
+ class RouterState extends Tree {
1655
1824
  /** @internal */
1656
- constructor(
1657
- /** The url from which this snapshot was created */
1658
- url, root) {
1825
+ constructor(root,
1826
+ /** The current snapshot of the router state */
1827
+ snapshot) {
1659
1828
  super(root);
1660
- this.url = url;
1829
+ this.snapshot = snapshot;
1661
1830
  setRouterState(this, root);
1662
1831
  }
1663
1832
  toString() {
1664
- return serializeNode(this._root);
1833
+ return this.snapshot.toString();
1665
1834
  }
1666
1835
  }
1667
- function setRouterState(state, node) {
1668
- node.value._routerState = state;
1669
- node.children.forEach(c => setRouterState(state, c));
1670
- }
1671
- function serializeNode(node) {
1672
- const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : '';
1673
- return `${node.value}${c}`;
1674
- }
1675
- /**
1676
- * The expectation is that the activate route is created with the right set of parameters.
1677
- * So we push new values into the observables only when they are not the initial values.
1678
- * And we detect that by checking if the snapshot field is set.
1679
- */
1680
- function advanceActivatedRoute(route) {
1681
- if (route.snapshot) {
1682
- const currentSnapshot = route.snapshot;
1683
- const nextSnapshot = route._futureSnapshot;
1684
- route.snapshot = nextSnapshot;
1685
- if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {
1686
- route.queryParams.next(nextSnapshot.queryParams);
1687
- }
1688
- if (currentSnapshot.fragment !== nextSnapshot.fragment) {
1689
- route.fragment.next(nextSnapshot.fragment);
1690
- }
1691
- if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {
1692
- route.params.next(nextSnapshot.params);
1693
- }
1694
- if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {
1695
- route.url.next(nextSnapshot.url);
1696
- }
1697
- if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {
1698
- route.data.next(nextSnapshot.data);
1699
- }
1700
- }
1701
- else {
1702
- route.snapshot = route._futureSnapshot;
1703
- // this is for resolved data
1704
- route.data.next(route._futureSnapshot.data);
1705
- }
1836
+ function createEmptyState(urlTree, rootComponent) {
1837
+ const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
1838
+ const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
1839
+ const emptyParams = new BehaviorSubject({});
1840
+ const emptyData = new BehaviorSubject({});
1841
+ const emptyQueryParams = new BehaviorSubject({});
1842
+ const fragment = new BehaviorSubject('');
1843
+ const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root);
1844
+ activated.snapshot = snapshot.root;
1845
+ return new RouterState(new TreeNode(activated, []), snapshot);
1706
1846
  }
1707
- function equalParamsAndUrlSegments(a, b) {
1708
- const equalUrlParams = shallowEqual(a.params, b.params) && equalSegments(a.url, b.url);
1709
- const parentsMismatch = !a.parent !== !b.parent;
1710
- return equalUrlParams && !parentsMismatch &&
1711
- (!a.parent || equalParamsAndUrlSegments(a.parent, b.parent));
1847
+ function createEmptyStateSnapshot(urlTree, rootComponent) {
1848
+ const emptyParams = {};
1849
+ const emptyData = {};
1850
+ const emptyQueryParams = {};
1851
+ const fragment = '';
1852
+ const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {});
1853
+ return new RouterStateSnapshot('', new TreeNode(activated, []));
1712
1854
  }
1713
-
1714
1855
  /**
1715
- * @license
1716
- * Copyright Google LLC All Rights Reserved.
1856
+ * Provides access to information about a route associated with a component
1857
+ * that is loaded in an outlet.
1858
+ * Use to traverse the `RouterState` tree and extract information from nodes.
1717
1859
  *
1718
- * Use of this source code is governed by an MIT-style license that can be
1719
- * found in the LICENSE file at https://angular.io/license
1720
- */
1721
- function createRouterState(routeReuseStrategy, curr, prevState) {
1722
- const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);
1723
- return new RouterState(root, curr);
1724
- }
1725
- function createNode(routeReuseStrategy, curr, prevState) {
1726
- // reuse an activated route that is currently displayed on the screen
1727
- if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {
1728
- const value = prevState.value;
1729
- value._futureSnapshot = curr.value;
1730
- const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);
1731
- return new TreeNode(value, children);
1732
- }
1733
- else {
1734
- if (routeReuseStrategy.shouldAttach(curr.value)) {
1735
- // retrieve an activated route that is used to be displayed, but is not currently displayed
1736
- const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);
1737
- if (detachedRouteHandle !== null) {
1738
- const tree = detachedRouteHandle.route;
1739
- tree.value._futureSnapshot = curr.value;
1740
- tree.children = curr.children.map(c => createNode(routeReuseStrategy, c));
1741
- return tree;
1742
- }
1743
- }
1744
- const value = createActivatedRoute(curr.value);
1745
- const children = curr.children.map(c => createNode(routeReuseStrategy, c));
1746
- return new TreeNode(value, children);
1747
- }
1748
- }
1749
- function createOrReuseChildren(routeReuseStrategy, curr, prevState) {
1750
- return curr.children.map(child => {
1751
- for (const p of prevState.children) {
1752
- if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) {
1753
- return createNode(routeReuseStrategy, child, p);
1754
- }
1755
- }
1756
- return createNode(routeReuseStrategy, child);
1757
- });
1758
- }
1759
- function createActivatedRoute(c) {
1760
- return new ActivatedRoute(new BehaviorSubject(c.url), new BehaviorSubject(c.params), new BehaviorSubject(c.queryParams), new BehaviorSubject(c.fragment), new BehaviorSubject(c.data), c.outlet, c.component, c);
1761
- }
1762
-
1763
- /**
1764
- * @license
1765
- * Copyright Google LLC All Rights Reserved.
1860
+ * The following example shows how to construct a component using information from a
1861
+ * currently activated route.
1766
1862
  *
1767
- * Use of this source code is governed by an MIT-style license that can be
1768
- * found in the LICENSE file at https://angular.io/license
1863
+ * Note: the observables in this class only emit when the current and previous values differ based
1864
+ * on shallow equality. For example, changing deeply nested properties in resolved `data` will not
1865
+ * cause the `ActivatedRoute.data` `Observable` to emit a new value.
1866
+ *
1867
+ * {@example router/activated-route/module.ts region="activated-route"
1868
+ * header="activated-route.component.ts"}
1869
+ *
1870
+ * @see [Getting route information](guide/router#getting-route-information)
1871
+ *
1872
+ * @publicApi
1769
1873
  */
1770
- function createUrlTree(route, urlTree, commands, queryParams, fragment) {
1771
- if (commands.length === 0) {
1772
- return tree(urlTree.root, urlTree.root, urlTree.root, queryParams, fragment);
1773
- }
1774
- const nav = computeNavigation(commands);
1775
- if (nav.toRoot()) {
1776
- return tree(urlTree.root, urlTree.root, new UrlSegmentGroup([], {}), queryParams, fragment);
1777
- }
1778
- function createTreeUsingPathIndex(lastPathIndex) {
1779
- const startingPosition = findStartingPosition(nav, urlTree, route.snapshot?._urlSegment, lastPathIndex);
1780
- const segmentGroup = startingPosition.processChildren ?
1781
- updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) :
1782
- updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
1783
- return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment);
1784
- }
1785
- // Note: The types should disallow `snapshot` from being `undefined` but due to test mocks, this
1786
- // may be the case. Since we try to access it at an earlier point before the refactor to add the
1787
- // warning for `relativeLinkResolution: 'legacy'`, this may cause failures in tests where it
1788
- // didn't before.
1789
- const result = createTreeUsingPathIndex(route.snapshot?._lastPathIndex);
1790
- // Check if application is relying on `relativeLinkResolution: 'legacy'`
1791
- if (typeof ngDevMode === 'undefined' || !!ngDevMode) {
1792
- const correctedResult = createTreeUsingPathIndex(route.snapshot?._correctedLastPathIndex);
1793
- if (correctedResult.toString() !== result.toString()) {
1794
- console.warn(`relativeLinkResolution: 'legacy' is deprecated and will be removed in a future version of Angular. The link to ${result.toString()} will change to ${correctedResult.toString()} if the code is not updated before then.`);
1795
- }
1874
+ class ActivatedRoute {
1875
+ /** @internal */
1876
+ constructor(
1877
+ /** An observable of the URL segments matched by this route. */
1878
+ url,
1879
+ /** An observable of the matrix parameters scoped to this route. */
1880
+ params,
1881
+ /** An observable of the query parameters shared by all the routes. */
1882
+ queryParams,
1883
+ /** An observable of the URL fragment shared by all the routes. */
1884
+ fragment,
1885
+ /** An observable of the static and resolved data of this route. */
1886
+ data,
1887
+ /** The outlet name of the route, a constant. */
1888
+ outlet,
1889
+ /** The component of the route, a constant. */
1890
+ component, futureSnapshot) {
1891
+ this.url = url;
1892
+ this.params = params;
1893
+ this.queryParams = queryParams;
1894
+ this.fragment = fragment;
1895
+ this.data = data;
1896
+ this.outlet = outlet;
1897
+ this.component = component;
1898
+ this._futureSnapshot = futureSnapshot;
1796
1899
  }
1797
- return result;
1798
- }
1799
- function isMatrixParams(command) {
1800
- return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
1801
- }
1802
- /**
1803
- * Determines if a given command has an `outlets` map. When we encounter a command
1804
- * with an outlets k/v map, we need to apply each outlet individually to the existing segment.
1805
- */
1806
- function isCommandWithOutlets(command) {
1807
- return typeof command === 'object' && command != null && command.outlets;
1808
- }
1809
- function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment) {
1810
- let qp = {};
1811
- if (queryParams) {
1812
- forEach(queryParams, (value, name) => {
1813
- qp[name] = Array.isArray(value) ? value.map((v) => `${v}`) : `${value}`;
1814
- });
1900
+ /** The configuration used to match this route. */
1901
+ get routeConfig() {
1902
+ return this._futureSnapshot.routeConfig;
1815
1903
  }
1816
- if (oldRoot === oldSegmentGroup) {
1817
- return new UrlTree(newSegmentGroup, qp, fragment);
1904
+ /** The root of the router state. */
1905
+ get root() {
1906
+ return this._routerState.root;
1818
1907
  }
1819
- const newRoot = replaceSegment(oldRoot, oldSegmentGroup, newSegmentGroup);
1820
- return new UrlTree(newRoot, qp, fragment);
1821
- }
1822
- function replaceSegment(current, oldSegment, newSegment) {
1823
- const children = {};
1824
- forEach(current.children, (c, outletName) => {
1825
- if (c === oldSegment) {
1826
- children[outletName] = newSegment;
1827
- }
1828
- else {
1829
- children[outletName] = replaceSegment(c, oldSegment, newSegment);
1830
- }
1831
- });
1832
- return new UrlSegmentGroup(current.segments, children);
1833
- }
1834
- class Navigation {
1835
- constructor(isAbsolute, numberOfDoubleDots, commands) {
1836
- this.isAbsolute = isAbsolute;
1837
- this.numberOfDoubleDots = numberOfDoubleDots;
1838
- this.commands = commands;
1839
- if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {
1840
- throw new Error('Root segment cannot have matrix parameters');
1908
+ /** The parent of this route in the router state tree. */
1909
+ get parent() {
1910
+ return this._routerState.parent(this);
1911
+ }
1912
+ /** The first child of this route in the router state tree. */
1913
+ get firstChild() {
1914
+ return this._routerState.firstChild(this);
1915
+ }
1916
+ /** The children of this route in the router state tree. */
1917
+ get children() {
1918
+ return this._routerState.children(this);
1919
+ }
1920
+ /** The path from the root of the router state tree to this route. */
1921
+ get pathFromRoot() {
1922
+ return this._routerState.pathFromRoot(this);
1923
+ }
1924
+ /**
1925
+ * An Observable that contains a map of the required and optional parameters
1926
+ * specific to the route.
1927
+ * The map supports retrieving single and multiple values from the same parameter.
1928
+ */
1929
+ get paramMap() {
1930
+ if (!this._paramMap) {
1931
+ this._paramMap = this.params.pipe(map((p) => convertToParamMap(p)));
1841
1932
  }
1842
- const cmdWithOutlet = commands.find(isCommandWithOutlets);
1843
- if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {
1844
- throw new Error('{outlets:{}} has to be the last command');
1933
+ return this._paramMap;
1934
+ }
1935
+ /**
1936
+ * An Observable that contains a map of the query parameters available to all routes.
1937
+ * The map supports retrieving single and multiple values from the query parameter.
1938
+ */
1939
+ get queryParamMap() {
1940
+ if (!this._queryParamMap) {
1941
+ this._queryParamMap =
1942
+ this.queryParams.pipe(map((p) => convertToParamMap(p)));
1845
1943
  }
1944
+ return this._queryParamMap;
1846
1945
  }
1847
- toRoot() {
1848
- return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';
1946
+ toString() {
1947
+ return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
1849
1948
  }
1850
1949
  }
1851
- /** Transforms commands to a normalized `Navigation` */
1852
- function computeNavigation(commands) {
1853
- if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] === '/') {
1854
- return new Navigation(true, 0, commands);
1855
- }
1856
- let numberOfDoubleDots = 0;
1857
- let isAbsolute = false;
1858
- const res = commands.reduce((res, cmd, cmdIdx) => {
1859
- if (typeof cmd === 'object' && cmd != null) {
1860
- if (cmd.outlets) {
1861
- const outlets = {};
1862
- forEach(cmd.outlets, (commands, name) => {
1863
- outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;
1864
- });
1865
- return [...res, { outlets }];
1950
+ /**
1951
+ * Returns the inherited params, data, and resolve for a given route.
1952
+ * By default, this only inherits values up to the nearest path-less or component-less route.
1953
+ * @internal
1954
+ */
1955
+ function inheritedParamsDataResolve(route, paramsInheritanceStrategy = 'emptyOnly') {
1956
+ const pathFromRoot = route.pathFromRoot;
1957
+ let inheritingStartingFrom = 0;
1958
+ if (paramsInheritanceStrategy !== 'always') {
1959
+ inheritingStartingFrom = pathFromRoot.length - 1;
1960
+ while (inheritingStartingFrom >= 1) {
1961
+ const current = pathFromRoot[inheritingStartingFrom];
1962
+ const parent = pathFromRoot[inheritingStartingFrom - 1];
1963
+ // current route is an empty path => inherits its parent's params and data
1964
+ if (current.routeConfig && current.routeConfig.path === '') {
1965
+ inheritingStartingFrom--;
1966
+ // parent is componentless => current route should inherit its params and data
1866
1967
  }
1867
- if (cmd.segmentPath) {
1868
- return [...res, cmd.segmentPath];
1968
+ else if (!parent.component) {
1969
+ inheritingStartingFrom--;
1970
+ }
1971
+ else {
1972
+ break;
1869
1973
  }
1870
1974
  }
1871
- if (!(typeof cmd === 'string')) {
1872
- return [...res, cmd];
1873
- }
1874
- if (cmdIdx === 0) {
1875
- cmd.split('/').forEach((urlPart, partIndex) => {
1876
- if (partIndex == 0 && urlPart === '.') {
1877
- // skip './a'
1878
- }
1879
- else if (partIndex == 0 && urlPart === '') { // '/a'
1880
- isAbsolute = true;
1881
- }
1882
- else if (urlPart === '..') { // '../a'
1883
- numberOfDoubleDots++;
1884
- }
1885
- else if (urlPart != '') {
1886
- res.push(urlPart);
1887
- }
1888
- });
1889
- return res;
1890
- }
1891
- return [...res, cmd];
1892
- }, []);
1893
- return new Navigation(isAbsolute, numberOfDoubleDots, res);
1894
- }
1895
- class Position {
1896
- constructor(segmentGroup, processChildren, index) {
1897
- this.segmentGroup = segmentGroup;
1898
- this.processChildren = processChildren;
1899
- this.index = index;
1900
1975
  }
1976
+ return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
1901
1977
  }
1902
- function findStartingPosition(nav, tree, segmentGroup, lastPathIndex) {
1903
- if (nav.isAbsolute) {
1904
- return new Position(tree.root, true, 0);
1905
- }
1906
- if (lastPathIndex === -1) {
1907
- // Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
1908
- // see issue #26224, #13011, #35687
1909
- // However, if the ActivatedRoute is the root we should process children like above.
1910
- const processChildren = segmentGroup === tree.root;
1911
- return new Position(segmentGroup, processChildren, 0);
1912
- }
1913
- const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
1914
- const index = lastPathIndex + modifier;
1915
- return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
1978
+ /** @internal */
1979
+ function flattenInherited(pathFromRoot) {
1980
+ return pathFromRoot.reduce((res, curr) => {
1981
+ const params = { ...res.params, ...curr.params };
1982
+ const data = { ...res.data, ...curr.data };
1983
+ const resolve = { ...curr.data, ...res.resolve, ...curr.routeConfig?.data, ...curr._resolvedData };
1984
+ return { params, data, resolve };
1985
+ }, { params: {}, data: {}, resolve: {} });
1916
1986
  }
1917
- function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) {
1918
- let g = group;
1919
- let ci = index;
1920
- let dd = numberOfDoubleDots;
1921
- while (dd > ci) {
1922
- dd -= ci;
1923
- g = g.parent;
1924
- if (!g) {
1925
- throw new Error('Invalid number of \'../\'');
1926
- }
1927
- ci = g.segments.length;
1987
+ /**
1988
+ * @description
1989
+ *
1990
+ * Contains the information about a route associated with a component loaded in an
1991
+ * outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to
1992
+ * traverse the router state tree.
1993
+ *
1994
+ * The following example initializes a component with route information extracted
1995
+ * from the snapshot of the root node at the time of creation.
1996
+ *
1997
+ * ```
1998
+ * @Component({templateUrl:'./my-component.html'})
1999
+ * class MyComponent {
2000
+ * constructor(route: ActivatedRoute) {
2001
+ * const id: string = route.snapshot.params.id;
2002
+ * const url: string = route.snapshot.url.join('');
2003
+ * const user = route.snapshot.data.user;
2004
+ * }
2005
+ * }
2006
+ * ```
2007
+ *
2008
+ * @publicApi
2009
+ */
2010
+ class ActivatedRouteSnapshot {
2011
+ /** @internal */
2012
+ constructor(
2013
+ /** The URL segments matched by this route */
2014
+ url,
2015
+ /**
2016
+ * The matrix parameters scoped to this route.
2017
+ *
2018
+ * You can compute all params (or data) in the router state or to get params outside
2019
+ * of an activated component by traversing the `RouterState` tree as in the following
2020
+ * example:
2021
+ * ```
2022
+ * collectRouteParams(router: Router) {
2023
+ * let params = {};
2024
+ * let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
2025
+ * while (stack.length > 0) {
2026
+ * const route = stack.pop()!;
2027
+ * params = {...params, ...route.params};
2028
+ * stack.push(...route.children);
2029
+ * }
2030
+ * return params;
2031
+ * }
2032
+ * ```
2033
+ */
2034
+ params,
2035
+ /** The query parameters shared by all the routes */
2036
+ queryParams,
2037
+ /** The URL fragment shared by all the routes */
2038
+ fragment,
2039
+ /** The static and resolved data of this route */
2040
+ data,
2041
+ /** The outlet name of the route */
2042
+ outlet,
2043
+ /** The component of the route */
2044
+ component, routeConfig, urlSegment, lastPathIndex, resolve, correctedLastPathIndex) {
2045
+ this.url = url;
2046
+ this.params = params;
2047
+ this.queryParams = queryParams;
2048
+ this.fragment = fragment;
2049
+ this.data = data;
2050
+ this.outlet = outlet;
2051
+ this.component = component;
2052
+ this.routeConfig = routeConfig;
2053
+ this._urlSegment = urlSegment;
2054
+ this._lastPathIndex = lastPathIndex;
2055
+ this._correctedLastPathIndex = correctedLastPathIndex ?? lastPathIndex;
2056
+ this._resolve = resolve;
1928
2057
  }
1929
- return new Position(g, false, ci - dd);
1930
- }
1931
- function getOutlets(commands) {
1932
- if (isCommandWithOutlets(commands[0])) {
1933
- return commands[0].outlets;
2058
+ /** The root of the router state */
2059
+ get root() {
2060
+ return this._routerState.root;
1934
2061
  }
1935
- return { [PRIMARY_OUTLET]: commands };
1936
- }
1937
- function updateSegmentGroup(segmentGroup, startIndex, commands) {
1938
- if (!segmentGroup) {
1939
- segmentGroup = new UrlSegmentGroup([], {});
2062
+ /** The parent of this route in the router state tree */
2063
+ get parent() {
2064
+ return this._routerState.parent(this);
1940
2065
  }
1941
- if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
1942
- return updateSegmentGroupChildren(segmentGroup, startIndex, commands);
2066
+ /** The first child of this route in the router state tree */
2067
+ get firstChild() {
2068
+ return this._routerState.firstChild(this);
1943
2069
  }
1944
- const m = prefixedWith(segmentGroup, startIndex, commands);
1945
- const slicedCommands = commands.slice(m.commandIndex);
1946
- if (m.match && m.pathIndex < segmentGroup.segments.length) {
1947
- const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});
1948
- g.children[PRIMARY_OUTLET] =
1949
- new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children);
1950
- return updateSegmentGroupChildren(g, 0, slicedCommands);
2070
+ /** The children of this route in the router state tree */
2071
+ get children() {
2072
+ return this._routerState.children(this);
1951
2073
  }
1952
- else if (m.match && slicedCommands.length === 0) {
1953
- return new UrlSegmentGroup(segmentGroup.segments, {});
2074
+ /** The path from the root of the router state tree to this route */
2075
+ get pathFromRoot() {
2076
+ return this._routerState.pathFromRoot(this);
1954
2077
  }
1955
- else if (m.match && !segmentGroup.hasChildren()) {
1956
- return createNewSegmentGroup(segmentGroup, startIndex, commands);
2078
+ get paramMap() {
2079
+ if (!this._paramMap) {
2080
+ this._paramMap = convertToParamMap(this.params);
2081
+ }
2082
+ return this._paramMap;
1957
2083
  }
1958
- else if (m.match) {
1959
- return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands);
2084
+ get queryParamMap() {
2085
+ if (!this._queryParamMap) {
2086
+ this._queryParamMap = convertToParamMap(this.queryParams);
2087
+ }
2088
+ return this._queryParamMap;
1960
2089
  }
1961
- else {
1962
- return createNewSegmentGroup(segmentGroup, startIndex, commands);
2090
+ toString() {
2091
+ const url = this.url.map(segment => segment.toString()).join('/');
2092
+ const matched = this.routeConfig ? this.routeConfig.path : '';
2093
+ return `Route(url:'${url}', path:'${matched}')`;
1963
2094
  }
1964
2095
  }
1965
- function updateSegmentGroupChildren(segmentGroup, startIndex, commands) {
1966
- if (commands.length === 0) {
1967
- return new UrlSegmentGroup(segmentGroup.segments, {});
2096
+ /**
2097
+ * @description
2098
+ *
2099
+ * Represents the state of the router at a moment in time.
2100
+ *
2101
+ * This is a tree of activated route snapshots. Every node in this tree knows about
2102
+ * the "consumed" URL segments, the extracted parameters, and the resolved data.
2103
+ *
2104
+ * The following example shows how a component is initialized with information
2105
+ * from the snapshot of the root node's state at the time of creation.
2106
+ *
2107
+ * ```
2108
+ * @Component({templateUrl:'template.html'})
2109
+ * class MyComponent {
2110
+ * constructor(router: Router) {
2111
+ * const state: RouterState = router.routerState;
2112
+ * const snapshot: RouterStateSnapshot = state.snapshot;
2113
+ * const root: ActivatedRouteSnapshot = snapshot.root;
2114
+ * const child = root.firstChild;
2115
+ * const id: Observable<string> = child.params.map(p => p.id);
2116
+ * //...
2117
+ * }
2118
+ * }
2119
+ * ```
2120
+ *
2121
+ * @publicApi
2122
+ */
2123
+ class RouterStateSnapshot extends Tree {
2124
+ /** @internal */
2125
+ constructor(
2126
+ /** The url from which this snapshot was created */
2127
+ url, root) {
2128
+ super(root);
2129
+ this.url = url;
2130
+ setRouterState(this, root);
1968
2131
  }
1969
- else {
1970
- const outlets = getOutlets(commands);
1971
- const children = {};
1972
- forEach(outlets, (commands, outlet) => {
1973
- if (typeof commands === 'string') {
1974
- commands = [commands];
1975
- }
1976
- if (commands !== null) {
1977
- children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);
1978
- }
1979
- });
1980
- forEach(segmentGroup.children, (child, childOutlet) => {
1981
- if (outlets[childOutlet] === undefined) {
1982
- children[childOutlet] = child;
1983
- }
1984
- });
1985
- return new UrlSegmentGroup(segmentGroup.segments, children);
2132
+ toString() {
2133
+ return serializeNode(this._root);
1986
2134
  }
1987
2135
  }
1988
- function prefixedWith(segmentGroup, startIndex, commands) {
1989
- let currentCommandIndex = 0;
1990
- let currentPathIndex = startIndex;
1991
- const noMatch = { match: false, pathIndex: 0, commandIndex: 0 };
1992
- while (currentPathIndex < segmentGroup.segments.length) {
1993
- if (currentCommandIndex >= commands.length)
1994
- return noMatch;
1995
- const path = segmentGroup.segments[currentPathIndex];
1996
- const command = commands[currentCommandIndex];
1997
- // Do not try to consume command as part of the prefixing if it has outlets because it can
1998
- // contain outlets other than the one being processed. Consuming the outlets command would
1999
- // result in other outlets being ignored.
2000
- if (isCommandWithOutlets(command)) {
2001
- break;
2002
- }
2003
- const curr = `${command}`;
2004
- const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;
2005
- if (currentPathIndex > 0 && curr === undefined)
2006
- break;
2007
- if (curr && next && (typeof next === 'object') && next.outlets === undefined) {
2008
- if (!compare(curr, next, path))
2009
- return noMatch;
2010
- currentCommandIndex += 2;
2011
- }
2012
- else {
2013
- if (!compare(curr, {}, path))
2014
- return noMatch;
2015
- currentCommandIndex++;
2016
- }
2017
- currentPathIndex++;
2018
- }
2019
- return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex };
2136
+ function setRouterState(state, node) {
2137
+ node.value._routerState = state;
2138
+ node.children.forEach(c => setRouterState(state, c));
2020
2139
  }
2021
- function createNewSegmentGroup(segmentGroup, startIndex, commands) {
2022
- const paths = segmentGroup.segments.slice(0, startIndex);
2023
- let i = 0;
2024
- while (i < commands.length) {
2025
- const command = commands[i];
2026
- if (isCommandWithOutlets(command)) {
2027
- const children = createNewSegmentChildren(command.outlets);
2028
- return new UrlSegmentGroup(paths, children);
2140
+ function serializeNode(node) {
2141
+ const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : '';
2142
+ return `${node.value}${c}`;
2143
+ }
2144
+ /**
2145
+ * The expectation is that the activate route is created with the right set of parameters.
2146
+ * So we push new values into the observables only when they are not the initial values.
2147
+ * And we detect that by checking if the snapshot field is set.
2148
+ */
2149
+ function advanceActivatedRoute(route) {
2150
+ if (route.snapshot) {
2151
+ const currentSnapshot = route.snapshot;
2152
+ const nextSnapshot = route._futureSnapshot;
2153
+ route.snapshot = nextSnapshot;
2154
+ if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {
2155
+ route.queryParams.next(nextSnapshot.queryParams);
2029
2156
  }
2030
- // if we start with an object literal, we need to reuse the path part from the segment
2031
- if (i === 0 && isMatrixParams(commands[0])) {
2032
- const p = segmentGroup.segments[startIndex];
2033
- paths.push(new UrlSegment(p.path, stringify(commands[0])));
2034
- i++;
2035
- continue;
2157
+ if (currentSnapshot.fragment !== nextSnapshot.fragment) {
2158
+ route.fragment.next(nextSnapshot.fragment);
2036
2159
  }
2037
- const curr = isCommandWithOutlets(command) ? command.outlets[PRIMARY_OUTLET] : `${command}`;
2038
- const next = (i < commands.length - 1) ? commands[i + 1] : null;
2039
- if (curr && next && isMatrixParams(next)) {
2040
- paths.push(new UrlSegment(curr, stringify(next)));
2041
- i += 2;
2160
+ if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {
2161
+ route.params.next(nextSnapshot.params);
2042
2162
  }
2043
- else {
2044
- paths.push(new UrlSegment(curr, {}));
2045
- i++;
2163
+ if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {
2164
+ route.url.next(nextSnapshot.url);
2165
+ }
2166
+ if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {
2167
+ route.data.next(nextSnapshot.data);
2046
2168
  }
2047
2169
  }
2048
- return new UrlSegmentGroup(paths, {});
2170
+ else {
2171
+ route.snapshot = route._futureSnapshot;
2172
+ // this is for resolved data
2173
+ route.data.next(route._futureSnapshot.data);
2174
+ }
2049
2175
  }
2050
- function createNewSegmentChildren(outlets) {
2051
- const children = {};
2052
- forEach(outlets, (commands, outlet) => {
2053
- if (typeof commands === 'string') {
2054
- commands = [commands];
2176
+ function equalParamsAndUrlSegments(a, b) {
2177
+ const equalUrlParams = shallowEqual(a.params, b.params) && equalSegments(a.url, b.url);
2178
+ const parentsMismatch = !a.parent !== !b.parent;
2179
+ return equalUrlParams && !parentsMismatch &&
2180
+ (!a.parent || equalParamsAndUrlSegments(a.parent, b.parent));
2181
+ }
2182
+
2183
+ /**
2184
+ * @license
2185
+ * Copyright Google LLC All Rights Reserved.
2186
+ *
2187
+ * Use of this source code is governed by an MIT-style license that can be
2188
+ * found in the LICENSE file at https://angular.io/license
2189
+ */
2190
+ function createRouterState(routeReuseStrategy, curr, prevState) {
2191
+ const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);
2192
+ return new RouterState(root, curr);
2193
+ }
2194
+ function createNode(routeReuseStrategy, curr, prevState) {
2195
+ // reuse an activated route that is currently displayed on the screen
2196
+ if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {
2197
+ const value = prevState.value;
2198
+ value._futureSnapshot = curr.value;
2199
+ const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);
2200
+ return new TreeNode(value, children);
2201
+ }
2202
+ else {
2203
+ if (routeReuseStrategy.shouldAttach(curr.value)) {
2204
+ // retrieve an activated route that is used to be displayed, but is not currently displayed
2205
+ const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);
2206
+ if (detachedRouteHandle !== null) {
2207
+ const tree = detachedRouteHandle.route;
2208
+ tree.value._futureSnapshot = curr.value;
2209
+ tree.children = curr.children.map(c => createNode(routeReuseStrategy, c));
2210
+ return tree;
2211
+ }
2055
2212
  }
2056
- if (commands !== null) {
2057
- children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands);
2213
+ const value = createActivatedRoute(curr.value);
2214
+ const children = curr.children.map(c => createNode(routeReuseStrategy, c));
2215
+ return new TreeNode(value, children);
2216
+ }
2217
+ }
2218
+ function createOrReuseChildren(routeReuseStrategy, curr, prevState) {
2219
+ return curr.children.map(child => {
2220
+ for (const p of prevState.children) {
2221
+ if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) {
2222
+ return createNode(routeReuseStrategy, child, p);
2223
+ }
2058
2224
  }
2225
+ return createNode(routeReuseStrategy, child);
2059
2226
  });
2060
- return children;
2061
- }
2062
- function stringify(params) {
2063
- const res = {};
2064
- forEach(params, (v, k) => res[k] = `${v}`);
2065
- return res;
2066
2227
  }
2067
- function compare(path, params, segment) {
2068
- return path == segment.path && shallowEqual(params, segment.parameters);
2228
+ function createActivatedRoute(c) {
2229
+ return new ActivatedRoute(new BehaviorSubject(c.url), new BehaviorSubject(c.params), new BehaviorSubject(c.queryParams), new BehaviorSubject(c.fragment), new BehaviorSubject(c.data), c.outlet, c.component, c);
2069
2230
  }
2070
2231
 
2071
2232
  /**
@@ -2328,9 +2489,9 @@ class RouterOutlet {
2328
2489
  this.activateEvents.emit(this.activated.instance);
2329
2490
  }
2330
2491
  }
2331
- RouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterOutlet, deps: [{ token: ChildrenOutletContexts }, { token: i0.ViewContainerRef }, { token: 'name', attribute: true }, { token: i0.ChangeDetectorRef }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Directive });
2332
- RouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: RouterOutlet, selector: "router-outlet", outputs: { activateEvents: "activate", deactivateEvents: "deactivate", attachEvents: "attach", detachEvents: "detach" }, exportAs: ["outlet"], ngImport: i0 });
2333
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterOutlet, decorators: [{
2492
+ RouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterOutlet, deps: [{ token: ChildrenOutletContexts }, { token: i0.ViewContainerRef }, { token: 'name', attribute: true }, { token: i0.ChangeDetectorRef }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Directive });
2493
+ RouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterOutlet, selector: "router-outlet", outputs: { activateEvents: "activate", deactivateEvents: "deactivate", attachEvents: "attach", detachEvents: "detach" }, exportAs: ["outlet"], ngImport: i0 });
2494
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterOutlet, decorators: [{
2334
2495
  type: Directive,
2335
2496
  args: [{ selector: 'router-outlet', exportAs: 'outlet' }]
2336
2497
  }], ctorParameters: function () { return [{ type: ChildrenOutletContexts }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
@@ -2387,9 +2548,9 @@ function isComponentFactoryResolver(item) {
2387
2548
  */
2388
2549
  class ɵEmptyOutletComponent {
2389
2550
  }
2390
- ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ɵEmptyOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2391
- ɵEmptyOutletComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: ɵEmptyOutletComponent, selector: "ng-component", ngImport: i0, template: `<router-outlet></router-outlet>`, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
2392
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ɵEmptyOutletComponent, decorators: [{
2551
+ ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: ɵEmptyOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2552
+ ɵEmptyOutletComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.1.0-next.0", type: ɵEmptyOutletComponent, selector: "ng-component", ngImport: i0, template: `<router-outlet></router-outlet>`, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
2553
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: ɵEmptyOutletComponent, decorators: [{
2393
2554
  type: Component,
2394
2555
  args: [{ template: `<router-outlet></router-outlet>` }]
2395
2556
  }] });
@@ -3073,9 +3234,7 @@ class ApplyRedirects {
3073
3234
  return new Error(`Cannot match any routes. URL Segment: '${e.segmentGroup}'`);
3074
3235
  }
3075
3236
  createUrlTree(rootCandidate, queryParams, fragment) {
3076
- const root = rootCandidate.segments.length > 0 ?
3077
- new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) :
3078
- rootCandidate;
3237
+ const root = createRoot(rootCandidate);
3079
3238
  return new UrlTree(root, queryParams, fragment);
3080
3239
  }
3081
3240
  expandSegmentGroup(injector, routes, segmentGroup, outlet) {
@@ -3327,39 +3486,6 @@ class ApplyRedirects {
3327
3486
  return redirectToUrlSegment;
3328
3487
  }
3329
3488
  }
3330
- /**
3331
- * When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.
3332
- *
3333
- * When a segment group has only one child which is a primary outlet, merges that child into the
3334
- * parent. That is, the child segment group's segments are merged into the `s` and the child's
3335
- * children become the children of `s`. Think of this like a 'squash', merging the child segment
3336
- * group into the parent.
3337
- */
3338
- function mergeTrivialChildren(s) {
3339
- if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
3340
- const c = s.children[PRIMARY_OUTLET];
3341
- return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);
3342
- }
3343
- return s;
3344
- }
3345
- /**
3346
- * Recursively merges primary segment children into their parents and also drops empty children
3347
- * (those which have no segments and no children themselves). The latter prevents serializing a
3348
- * group into something like `/a(aux:)`, where `aux` is an empty child segment.
3349
- */
3350
- function squashSegmentGroup(segmentGroup) {
3351
- const newChildren = {};
3352
- for (const childOutlet of Object.keys(segmentGroup.children)) {
3353
- const child = segmentGroup.children[childOutlet];
3354
- const childCandidate = squashSegmentGroup(child);
3355
- // don't add empty children
3356
- if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {
3357
- newChildren[childOutlet] = childCandidate;
3358
- }
3359
- }
3360
- const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
3361
- return mergeTrivialChildren(s);
3362
- }
3363
3489
 
3364
3490
  /**
3365
3491
  * @license
@@ -4186,9 +4312,9 @@ class RouterConfigLoader {
4186
4312
  }));
4187
4313
  }
4188
4314
  }
4189
- RouterConfigLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterConfigLoader, deps: [{ token: i0.Injector }, { token: i0.Compiler }], target: i0.ɵɵFactoryTarget.Injectable });
4190
- RouterConfigLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterConfigLoader });
4191
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterConfigLoader, decorators: [{
4315
+ RouterConfigLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader, deps: [{ token: i0.Injector }, { token: i0.Compiler }], target: i0.ɵɵFactoryTarget.Injectable });
4316
+ RouterConfigLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader });
4317
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader, decorators: [{
4192
4318
  type: Injectable
4193
4319
  }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.Compiler }]; } });
4194
4320
 
@@ -5193,9 +5319,9 @@ class Router {
5193
5319
  return { navigationId };
5194
5320
  }
5195
5321
  }
5196
- Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: Router, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
5197
- Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: Router });
5198
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: Router, decorators: [{
5322
+ Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
5323
+ Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router });
5324
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router, decorators: [{
5199
5325
  type: Injectable
5200
5326
  }], ctorParameters: function () { return [{ type: i0.Type }, { type: UrlSerializer }, { type: ChildrenOutletContexts }, { type: i3.Location }, { type: i0.Injector }, { type: i0.Compiler }, { type: undefined }]; } });
5201
5327
  function validateCommands(commands) {
@@ -5394,9 +5520,9 @@ class RouterLink {
5394
5520
  });
5395
5521
  }
5396
5522
  }
5397
- RouterLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLink, deps: [{ token: Router }, { token: ActivatedRoute }, { token: 'tabindex', attribute: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
5398
- RouterLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: RouterLink, selector: ":not(a):not(area)[routerLink]", inputs: { queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick()" } }, usesOnChanges: true, ngImport: i0 });
5399
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLink, decorators: [{
5523
+ RouterLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLink, deps: [{ token: Router }, { token: ActivatedRoute }, { token: 'tabindex', attribute: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
5524
+ RouterLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterLink, selector: ":not(a):not(area)[routerLink]", inputs: { queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick()" } }, usesOnChanges: true, ngImport: i0 });
5525
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLink, decorators: [{
5400
5526
  type: Directive,
5401
5527
  args: [{ selector: ':not(a):not(area)[routerLink]' }]
5402
5528
  }], ctorParameters: function () { return [{ type: Router }, { type: ActivatedRoute }, { type: undefined, decorators: [{
@@ -5513,9 +5639,9 @@ class RouterLinkWithHref {
5513
5639
  });
5514
5640
  }
5515
5641
  }
5516
- RouterLinkWithHref.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLinkWithHref, deps: [{ token: Router }, { token: ActivatedRoute }, { token: i3.LocationStrategy }], target: i0.ɵɵFactoryTarget.Directive });
5517
- RouterLinkWithHref.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: { target: "target", queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick($event.button,$event.ctrlKey,$event.shiftKey,$event.altKey,$event.metaKey)" }, properties: { "attr.target": "this.target", "attr.href": "this.href" } }, usesOnChanges: true, ngImport: i0 });
5518
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLinkWithHref, decorators: [{
5642
+ RouterLinkWithHref.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkWithHref, deps: [{ token: Router }, { token: ActivatedRoute }, { token: i3.LocationStrategy }], target: i0.ɵɵFactoryTarget.Directive });
5643
+ RouterLinkWithHref.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: { target: "target", queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick($event.button,$event.ctrlKey,$event.shiftKey,$event.altKey,$event.metaKey)" }, properties: { "attr.target": "this.target", "attr.href": "this.href" } }, usesOnChanges: true, ngImport: i0 });
5644
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkWithHref, decorators: [{
5519
5645
  type: Directive,
5520
5646
  args: [{ selector: 'a[routerLink],area[routerLink]' }]
5521
5647
  }], ctorParameters: function () { return [{ type: Router }, { type: ActivatedRoute }, { type: i3.LocationStrategy }]; }, propDecorators: { target: [{
@@ -5740,9 +5866,9 @@ class RouterLinkActive {
5740
5866
  this.links.some(isActiveCheckFn) || this.linksWithHrefs.some(isActiveCheckFn);
5741
5867
  }
5742
5868
  }
5743
- RouterLinkActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLinkActive, deps: [{ token: Router }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: RouterLink, optional: true }, { token: RouterLinkWithHref, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
5744
- RouterLinkActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: { routerLinkActiveOptions: "routerLinkActiveOptions", ariaCurrentWhenActive: "ariaCurrentWhenActive", routerLinkActive: "routerLinkActive" }, outputs: { isActiveChange: "isActiveChange" }, queries: [{ propertyName: "links", predicate: RouterLink, descendants: true }, { propertyName: "linksWithHrefs", predicate: RouterLinkWithHref, descendants: true }], exportAs: ["routerLinkActive"], usesOnChanges: true, ngImport: i0 });
5745
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLinkActive, decorators: [{
5869
+ RouterLinkActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkActive, deps: [{ token: Router }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: RouterLink, optional: true }, { token: RouterLinkWithHref, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
5870
+ RouterLinkActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: { routerLinkActiveOptions: "routerLinkActiveOptions", ariaCurrentWhenActive: "ariaCurrentWhenActive", routerLinkActive: "routerLinkActive" }, outputs: { isActiveChange: "isActiveChange" }, queries: [{ propertyName: "links", predicate: RouterLink, descendants: true }, { propertyName: "linksWithHrefs", predicate: RouterLinkWithHref, descendants: true }], exportAs: ["routerLinkActive"], usesOnChanges: true, ngImport: i0 });
5871
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkActive, decorators: [{
5746
5872
  type: Directive,
5747
5873
  args: [{
5748
5874
  selector: '[routerLinkActive]',
@@ -5845,9 +5971,9 @@ class DefaultTitleStrategy extends TitleStrategy {
5845
5971
  }
5846
5972
  }
5847
5973
  }
5848
- DefaultTitleStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DefaultTitleStrategy, deps: [{ token: i1.Title }], target: i0.ɵɵFactoryTarget.Injectable });
5849
- DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DefaultTitleStrategy, providedIn: 'root' });
5850
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DefaultTitleStrategy, decorators: [{
5974
+ DefaultTitleStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, deps: [{ token: i1.Title }], target: i0.ɵɵFactoryTarget.Injectable });
5975
+ DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, providedIn: 'root' });
5976
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, decorators: [{
5851
5977
  type: Injectable,
5852
5978
  args: [{ providedIn: 'root' }]
5853
5979
  }], ctorParameters: function () { return [{ type: i1.Title }]; } });
@@ -5980,9 +6106,9 @@ class RouterPreloader {
5980
6106
  });
5981
6107
  }
5982
6108
  }
5983
- RouterPreloader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterPreloader, deps: [{ token: Router }, { token: i0.Compiler }, { token: i0.EnvironmentInjector }, { token: PreloadingStrategy }, { token: RouterConfigLoader }], target: i0.ɵɵFactoryTarget.Injectable });
5984
- RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterPreloader });
5985
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterPreloader, decorators: [{
6109
+ RouterPreloader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader, deps: [{ token: Router }, { token: i0.Compiler }, { token: i0.EnvironmentInjector }, { token: PreloadingStrategy }, { token: RouterConfigLoader }], target: i0.ɵɵFactoryTarget.Injectable });
6110
+ RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader });
6111
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader, decorators: [{
5986
6112
  type: Injectable
5987
6113
  }], ctorParameters: function () { return [{ type: Router }, { type: i0.Compiler }, { type: i0.EnvironmentInjector }, { type: PreloadingStrategy }, { type: RouterConfigLoader }]; } });
5988
6114
 
@@ -6068,9 +6194,9 @@ class RouterScroller {
6068
6194
  }
6069
6195
  }
6070
6196
  }
6071
- RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterScroller, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
6072
- RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterScroller });
6073
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterScroller, decorators: [{
6197
+ RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
6198
+ RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller });
6199
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller, decorators: [{
6074
6200
  type: Injectable
6075
6201
  }], ctorParameters: function () { return [{ type: Router }, { type: i3.ViewportScroller }, { type: undefined }]; } });
6076
6202
 
@@ -6212,10 +6338,10 @@ class RouterModule {
6212
6338
  return { ngModule: RouterModule, providers: [provideRoutes(routes)] };
6213
6339
  }
6214
6340
  }
6215
- RouterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterModule, deps: [{ token: ROUTER_FORROOT_GUARD, optional: true }, { token: Router, optional: true }], target: i0.ɵɵFactoryTarget.NgModule });
6216
- RouterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.1", ngImport: i0, type: RouterModule, declarations: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent], exports: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent] });
6217
- RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterModule });
6218
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterModule, decorators: [{
6341
+ RouterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, deps: [{ token: ROUTER_FORROOT_GUARD, optional: true }, { token: Router, optional: true }], target: i0.ɵɵFactoryTarget.NgModule });
6342
+ RouterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, declarations: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent], exports: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent] });
6343
+ RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule });
6344
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, decorators: [{
6219
6345
  type: NgModule,
6220
6346
  args: [{
6221
6347
  declarations: ROUTER_DIRECTIVES,
@@ -6392,9 +6518,9 @@ class RouterInitializer {
6392
6518
  this.destroyed = true;
6393
6519
  }
6394
6520
  }
6395
- RouterInitializer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterInitializer, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
6396
- RouterInitializer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterInitializer });
6397
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterInitializer, decorators: [{
6521
+ RouterInitializer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterInitializer, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
6522
+ RouterInitializer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterInitializer });
6523
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterInitializer, decorators: [{
6398
6524
  type: Injectable
6399
6525
  }], ctorParameters: function () { return [{ type: i0.Injector }]; } });
6400
6526
  function getAppInitializer(r) {
@@ -6434,7 +6560,7 @@ function provideRouterInitializer() {
6434
6560
  /**
6435
6561
  * @publicApi
6436
6562
  */
6437
- const VERSION = new Version('14.0.1');
6563
+ const VERSION = new Version('14.1.0-next.0');
6438
6564
 
6439
6565
  /**
6440
6566
  * @license
@@ -6473,5 +6599,5 @@ const VERSION = new Version('14.0.1');
6473
6599
  * Generated bundle index. Do not edit.
6474
6600
  */
6475
6601
 
6476
- export { ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, BaseRouteReuseStrategy, ChildActivationEnd, ChildActivationStart, ChildrenOutletContexts, DefaultTitleStrategy, DefaultUrlSerializer, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, NoPreloading, OutletContext, PRIMARY_OUTLET, PreloadAllModules, PreloadingStrategy, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, ROUTES, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterEvent, RouterLink, RouterLinkActive, RouterLinkWithHref, RouterModule, RouterOutlet, RouterPreloader, RouterState, RouterStateSnapshot, RoutesRecognized, Scroll, TitleStrategy, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree, VERSION, convertToParamMap, provideRoutes, ɵEmptyOutletComponent, ROUTER_PROVIDERS as ɵROUTER_PROVIDERS, assignExtraOptionsToRouter as ɵassignExtraOptionsToRouter, flatten as ɵflatten };
6602
+ export { ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, BaseRouteReuseStrategy, ChildActivationEnd, ChildActivationStart, ChildrenOutletContexts, DefaultTitleStrategy, DefaultUrlSerializer, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, NoPreloading, OutletContext, PRIMARY_OUTLET, PreloadAllModules, PreloadingStrategy, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, ROUTES, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterEvent, RouterLink, RouterLinkActive, RouterLinkWithHref, RouterModule, RouterOutlet, RouterPreloader, RouterState, RouterStateSnapshot, RoutesRecognized, Scroll, TitleStrategy, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree, VERSION, convertToParamMap, createUrlTreeFromSnapshot, provideRoutes, ɵEmptyOutletComponent, ROUTER_PROVIDERS as ɵROUTER_PROVIDERS, assignExtraOptionsToRouter as ɵassignExtraOptionsToRouter, flatten as ɵflatten };
6477
6603
  //# sourceMappingURL=router.mjs.map