kdu-router 4.0.16 → 4.1.6

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,70 +1,33 @@
1
1
  /*!
2
- * kdu-router v4.0.16
3
- * (c) 2022 NKDuy
2
+ * kdu-router v4.1.6
3
+ * (c) 2023 NKDuy
4
4
  * @license MIT
5
5
  */
6
6
  var KduRouter = (function (exports, kdu) {
7
7
  'use strict';
8
8
 
9
- const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
10
- const PolySymbol = (name) =>
11
- // kr = kdu router
12
- hasSymbol
13
- ? Symbol('[kdu-router]: ' + name )
14
- : ('[kdu-router]: ' ) + name;
15
- // rvlm = Router View Location Matched
16
- /**
17
- * RouteRecord being rendered by the closest ancestor Router View. Used for
18
- * `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View
19
- * Location Matched
20
- *
21
- * @internal
22
- */
23
- const matchedRouteKey = /*#__PURE__*/ PolySymbol('router view location matched' );
24
- /**
25
- * Allows overriding the router view depth to control which component in
26
- * `matched` is rendered. rvd stands for Router View Depth
27
- *
28
- * @internal
29
- */
30
- const viewDepthKey = /*#__PURE__*/ PolySymbol('router view depth' );
31
- /**
32
- * Allows overriding the router instance returned by `useRouter` in tests. r
33
- * stands for router
34
- *
35
- * @internal
36
- */
37
- const routerKey = /*#__PURE__*/ PolySymbol('router' );
38
- /**
39
- * Allows overriding the current route returned by `useRoute` in tests. rl
40
- * stands for route location
41
- *
42
- * @internal
43
- */
44
- const routeLocationKey = /*#__PURE__*/ PolySymbol('route location' );
45
- /**
46
- * Allows overriding the current route used by router-view. Internally this is
47
- * used when the `route` prop is passed.
48
- *
49
- * @internal
50
- */
51
- const routerViewLocationKey = /*#__PURE__*/ PolySymbol('router view location' );
52
-
53
9
  const isBrowser = typeof window !== 'undefined';
54
10
 
55
11
  function isESModule(obj) {
56
- return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module');
12
+ return obj.__esModule || obj[Symbol.toStringTag] === 'Module';
57
13
  }
58
14
  const assign = Object.assign;
59
15
  function applyToParams(fn, params) {
60
16
  const newParams = {};
61
17
  for (const key in params) {
62
18
  const value = params[key];
63
- newParams[key] = Array.isArray(value) ? value.map(fn) : fn(value);
19
+ newParams[key] = isArray(value)
20
+ ? value.map(fn)
21
+ : fn(value);
64
22
  }
65
23
  return newParams;
66
24
  }
67
- const noop = () => { };
25
+ const noop = () => { };
26
+ /**
27
+ * Typesafe alternative to Array.isArray
28
+ * https://github.com/microsoft/TypeScript/pull/48228
29
+ */
30
+ const isArray = Array.isArray;
68
31
 
69
32
  function warn(msg) {
70
33
  // avoid using ...args as it breaks in older Edge builds
@@ -75,7 +38,7 @@ var KduRouter = (function (exports, kdu) {
75
38
  const TRAILING_SLASH_RE = /\/$/;
76
39
  const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, '');
77
40
  /**
78
- * Transforms an URI into a normalized history location
41
+ * Transforms a URI into a normalized history location
79
42
  *
80
43
  * @param parseQuery
81
44
  * @param location - URI to normalize
@@ -86,8 +49,13 @@ var KduRouter = (function (exports, kdu) {
86
49
  function parseURL(parseQuery, location, currentLocation = '/') {
87
50
  let path, query = {}, searchString = '', hash = '';
88
51
  // Could use URL and URLSearchParams but IE 11 doesn't support it
89
- const searchPos = location.indexOf('?');
90
- const hashPos = location.indexOf('#', searchPos > -1 ? searchPos : 0);
52
+ // TODO: move to new URL()
53
+ const hashPos = location.indexOf('#');
54
+ let searchPos = location.indexOf('?');
55
+ // the hash appears before the search, so it's not part of the search string
56
+ if (hashPos < searchPos && hashPos >= 0) {
57
+ searchPos = -1;
58
+ }
91
59
  if (searchPos > -1) {
92
60
  path = location.slice(0, searchPos);
93
61
  searchString = location.slice(searchPos + 1, hashPos > -1 ? hashPos : location.length);
@@ -119,8 +87,7 @@ var KduRouter = (function (exports, kdu) {
119
87
  return location.path + (query && '?') + query + (location.hash || '');
120
88
  }
121
89
  /**
122
- * Strips off the base from the beginning of a location.pathname in a non
123
- * case-sensitive way.
90
+ * Strips off the base from the beginning of a location.pathname in a non-case-sensitive way.
124
91
  *
125
92
  * @param pathname - location.pathname
126
93
  * @param base - base to strip off
@@ -172,9 +139,9 @@ var KduRouter = (function (exports, kdu) {
172
139
  return true;
173
140
  }
174
141
  function isSameRouteLocationParamsValue(a, b) {
175
- return Array.isArray(a)
142
+ return isArray(a)
176
143
  ? isEquivalentArray(a, b)
177
- : Array.isArray(b)
144
+ : isArray(b)
178
145
  ? isEquivalentArray(b, a)
179
146
  : a === b;
180
147
  }
@@ -186,7 +153,7 @@ var KduRouter = (function (exports, kdu) {
186
153
  * @param b - array of values or a single value
187
154
  */
188
155
  function isEquivalentArray(a, b) {
189
- return Array.isArray(b)
156
+ return isArray(b)
190
157
  ? a.length === b.length && a.every((value, i) => value === b[i])
191
158
  : a.length === 1 && a[0] === b;
192
159
  }
@@ -212,18 +179,24 @@ var KduRouter = (function (exports, kdu) {
212
179
  let segment;
213
180
  for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
214
181
  segment = toSegments[toPosition];
215
- // can't go below zero
216
- if (position === 1 || segment === '.')
182
+ // we stay on the same position
183
+ if (segment === '.')
217
184
  continue;
218
- if (segment === '..')
219
- position--;
220
- // found something that is not relative path
185
+ // go up in the from array
186
+ if (segment === '..') {
187
+ // we can't go below zero, but we still need to increment toPosition
188
+ if (position > 1)
189
+ position--;
190
+ // continue
191
+ }
192
+ // we reached a non-relative path, we stop here
221
193
  else
222
194
  break;
223
195
  }
224
196
  return (fromSegments.slice(0, position).join('/') +
225
197
  '/' +
226
198
  toSegments
199
+ // ensure we use at least the last element in the toSegments
227
200
  .slice(toPosition - (toPosition === toSegments.length ? 1 : 0))
228
201
  .join('/'));
229
202
  }
@@ -452,7 +425,7 @@ var KduRouter = (function (exports, kdu) {
452
425
  pauseState = currentLocation.value;
453
426
  }
454
427
  function listen(callback) {
455
- // setup the listener and prepare teardown callbacks
428
+ // set up the listener and prepare teardown callbacks
456
429
  listeners.push(callback);
457
430
  const teardown = () => {
458
431
  const index = listeners.indexOf(callback);
@@ -475,7 +448,7 @@ var KduRouter = (function (exports, kdu) {
475
448
  window.removeEventListener('popstate', popStateHandler);
476
449
  window.removeEventListener('beforeunload', beforeUnloadListener);
477
450
  }
478
- // setup the listeners and prepare teardown callbacks
451
+ // set up the listeners and prepare teardown callbacks
479
452
  window.addEventListener('popstate', popStateHandler);
480
453
  window.addEventListener('beforeunload', beforeUnloadListener);
481
454
  return {
@@ -513,14 +486,14 @@ var KduRouter = (function (exports, kdu) {
513
486
  // the length is off by one, we need to decrease it
514
487
  position: history.length - 1,
515
488
  replaced: true,
516
- // don't add a scroll as the user may have an anchor and we want
489
+ // don't add a scroll as the user may have an anchor, and we want
517
490
  // scrollBehavior to be triggered without a saved position
518
491
  scroll: null,
519
492
  }, true);
520
493
  }
521
494
  function changeLocation(to, state, replace) {
522
495
  /**
523
- * if a base tag is provided and we are on a normal domain, we have to
496
+ * if a base tag is provided, and we are on a normal domain, we have to
524
497
  * respect the provided `base` attribute because pushState() will use it and
525
498
  * potentially erase anything before the `#` where a base of
526
499
  * `/folder/#` but a base of `/` would erase the `/folder/` section. If
@@ -614,7 +587,7 @@ var KduRouter = (function (exports, kdu) {
614
587
  }
615
588
 
616
589
  /**
617
- * Creates a in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere.
590
+ * Creates an in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere.
618
591
  * It's up to the user to replace that location with the starter location by either calling `router.push` or `router.replace`.
619
592
  *
620
593
  * @param base - Base applied to all urls, defaults to '/'
@@ -699,15 +672,13 @@ var KduRouter = (function (exports, kdu) {
699
672
  }
700
673
 
701
674
  /**
702
- * Creates a hash history. Useful for web applications with no host (e.g.
703
- * `file://`) or when configuring a server to handle any URL is not possible.
675
+ * Creates a hash history. Useful for web applications with no host (e.g. `file://`) or when configuring a server to
676
+ * handle any URL is not possible.
704
677
  *
705
- * @param base - optional base to provide. Defaults to `location.pathname +
706
- * location.search` If there is a `<base>` tag in the `head`, its value will be
707
- * ignored in favor of this parameter **but note it affects all the
708
- * history.pushState() calls**, meaning that if you use a `<base>` tag, it's
709
- * `href` value **has to match this parameter** (ignoring anything after the
710
- * `#`).
678
+ * @param base - optional base to provide. Defaults to `location.pathname + location.search` If there is a `<base>` tag
679
+ * in the `head`, its value will be ignored in favor of this parameter **but note it affects all the history.pushState()
680
+ * calls**, meaning that if you use a `<base>` tag, it's `href` value **has to match this parameter** (ignoring anything
681
+ * after the `#`).
711
682
  *
712
683
  * @example
713
684
  * ```js
@@ -772,7 +743,7 @@ var KduRouter = (function (exports, kdu) {
772
743
  redirectedFrom: undefined,
773
744
  };
774
745
 
775
- const NavigationFailureSymbol = /*#__PURE__*/ PolySymbol('navigation failure' );
746
+ const NavigationFailureSymbol = Symbol('navigation failure' );
776
747
  /**
777
748
  * Enumeration with all possible types for navigation failures. Can be passed to
778
749
  * {@link isNavigationFailure} to check for specific failures.
@@ -797,21 +768,21 @@ var KduRouter = (function (exports, kdu) {
797
768
  })(exports.NavigationFailureType || (exports.NavigationFailureType = {}));
798
769
  // DEV only debug messages
799
770
  const ErrorTypeMessages = {
800
- [1 /* MATCHER_NOT_FOUND */]({ location, currentLocation }) {
771
+ [1 /* ErrorTypes.MATCHER_NOT_FOUND */]({ location, currentLocation }) {
801
772
  return `No match for\n ${JSON.stringify(location)}${currentLocation
802
773
  ? '\nwhile being at\n' + JSON.stringify(currentLocation)
803
774
  : ''}`;
804
775
  },
805
- [2 /* NAVIGATION_GUARD_REDIRECT */]({ from, to, }) {
776
+ [2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */]({ from, to, }) {
806
777
  return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`;
807
778
  },
808
- [4 /* NAVIGATION_ABORTED */]({ from, to }) {
779
+ [4 /* ErrorTypes.NAVIGATION_ABORTED */]({ from, to }) {
809
780
  return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`;
810
781
  },
811
- [8 /* NAVIGATION_CANCELLED */]({ from, to }) {
782
+ [8 /* ErrorTypes.NAVIGATION_CANCELLED */]({ from, to }) {
812
783
  return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`;
813
784
  },
814
- [16 /* NAVIGATION_DUPLICATED */]({ from, to }) {
785
+ [16 /* ErrorTypes.NAVIGATION_DUPLICATED */]({ from, to }) {
815
786
  return `Avoided redundant navigation to current location: "${from.fullPath}".`;
816
787
  },
817
788
  };
@@ -843,7 +814,7 @@ var KduRouter = (function (exports, kdu) {
843
814
  return JSON.stringify(location, null, 2);
844
815
  }
845
816
 
846
- // default pattern for a param: non greedy everything but /
817
+ // default pattern for a param: non-greedy everything but /
847
818
  const BASE_PARAM_PATTERN = '[^/]+?';
848
819
  const BASE_PATH_PARSER_OPTIONS = {
849
820
  sensitive: false,
@@ -870,23 +841,23 @@ var KduRouter = (function (exports, kdu) {
870
841
  const keys = [];
871
842
  for (const segment of segments) {
872
843
  // the root segment needs special treatment
873
- const segmentScores = segment.length ? [] : [90 /* Root */];
844
+ const segmentScores = segment.length ? [] : [90 /* PathScore.Root */];
874
845
  // allow trailing slash
875
846
  if (options.strict && !segment.length)
876
847
  pattern += '/';
877
848
  for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
878
849
  const token = segment[tokenIndex];
879
- // resets the score if we are inside a sub segment /:a-other-:b
880
- let subSegmentScore = 40 /* Segment */ +
881
- (options.sensitive ? 0.25 /* BonusCaseSensitive */ : 0);
882
- if (token.type === 0 /* Static */) {
850
+ // resets the score if we are inside a sub-segment /:a-other-:b
851
+ let subSegmentScore = 40 /* PathScore.Segment */ +
852
+ (options.sensitive ? 0.25 /* PathScore.BonusCaseSensitive */ : 0);
853
+ if (token.type === 0 /* TokenType.Static */) {
883
854
  // prepend the slash if we are starting a new segment
884
855
  if (!tokenIndex)
885
856
  pattern += '/';
886
857
  pattern += token.value.replace(REGEX_CHARS_RE, '\\$&');
887
- subSegmentScore += 40 /* Static */;
858
+ subSegmentScore += 40 /* PathScore.Static */;
888
859
  }
889
- else if (token.type === 1 /* Param */) {
860
+ else if (token.type === 1 /* TokenType.Param */) {
890
861
  const { value, repeatable, optional, regexp } = token;
891
862
  keys.push({
892
863
  name: value,
@@ -896,7 +867,7 @@ var KduRouter = (function (exports, kdu) {
896
867
  const re = regexp ? regexp : BASE_PARAM_PATTERN;
897
868
  // the user provided a custom regexp /:id(\\d+)
898
869
  if (re !== BASE_PARAM_PATTERN) {
899
- subSegmentScore += 10 /* BonusCustomRegExp */;
870
+ subSegmentScore += 10 /* PathScore.BonusCustomRegExp */;
900
871
  // make sure the regexp is valid before using it
901
872
  try {
902
873
  new RegExp(`(${re})`);
@@ -919,13 +890,13 @@ var KduRouter = (function (exports, kdu) {
919
890
  if (optional)
920
891
  subPattern += '?';
921
892
  pattern += subPattern;
922
- subSegmentScore += 20 /* Dynamic */;
893
+ subSegmentScore += 20 /* PathScore.Dynamic */;
923
894
  if (optional)
924
- subSegmentScore += -8 /* BonusOptional */;
895
+ subSegmentScore += -8 /* PathScore.BonusOptional */;
925
896
  if (repeatable)
926
- subSegmentScore += -20 /* BonusRepeatable */;
897
+ subSegmentScore += -20 /* PathScore.BonusRepeatable */;
927
898
  if (re === '.*')
928
- subSegmentScore += -50 /* BonusWildcard */;
899
+ subSegmentScore += -50 /* PathScore.BonusWildcard */;
929
900
  }
930
901
  segmentScores.push(subSegmentScore);
931
902
  }
@@ -936,7 +907,7 @@ var KduRouter = (function (exports, kdu) {
936
907
  // only apply the strict bonus to the last score
937
908
  if (options.strict && options.end) {
938
909
  const i = score.length - 1;
939
- score[i][score[i].length - 1] += 0.7000000000000001 /* BonusStrict */;
910
+ score[i][score[i].length - 1] += 0.7000000000000001 /* PathScore.BonusStrict */;
940
911
  }
941
912
  // TODO: dev only warn double trailing slash
942
913
  if (!options.strict)
@@ -968,20 +939,22 @@ var KduRouter = (function (exports, kdu) {
968
939
  path += '/';
969
940
  avoidDuplicatedSlash = false;
970
941
  for (const token of segment) {
971
- if (token.type === 0 /* Static */) {
942
+ if (token.type === 0 /* TokenType.Static */) {
972
943
  path += token.value;
973
944
  }
974
- else if (token.type === 1 /* Param */) {
945
+ else if (token.type === 1 /* TokenType.Param */) {
975
946
  const { value, repeatable, optional } = token;
976
947
  const param = value in params ? params[value] : '';
977
- if (Array.isArray(param) && !repeatable)
948
+ if (isArray(param) && !repeatable) {
978
949
  throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`);
979
- const text = Array.isArray(param) ? param.join('/') : param;
950
+ }
951
+ const text = isArray(param)
952
+ ? param.join('/')
953
+ : param;
980
954
  if (!text) {
981
955
  if (optional) {
982
- // if we have more than one optional param like /:a?-static and there are more segments, we don't need to
983
- // care about the optional param
984
- if (segment.length < 2 && segments.length > 1) {
956
+ // if we have more than one optional param like /:a?-static we don't need to care about the optional param
957
+ if (segment.length < 2) {
985
958
  // remove the last slash as we could be at the end
986
959
  if (path.endsWith('/'))
987
960
  path = path.slice(0, -1);
@@ -997,7 +970,8 @@ var KduRouter = (function (exports, kdu) {
997
970
  }
998
971
  }
999
972
  }
1000
- return path;
973
+ // avoid empty path when we have multiple optional params
974
+ return path || '/';
1001
975
  }
1002
976
  return {
1003
977
  re,
@@ -1028,12 +1002,12 @@ var KduRouter = (function (exports, kdu) {
1028
1002
  // if the last subsegment was Static, the shorter segments should be sorted first
1029
1003
  // otherwise sort the longest segment first
1030
1004
  if (a.length < b.length) {
1031
- return a.length === 1 && a[0] === 40 /* Static */ + 40 /* Segment */
1005
+ return a.length === 1 && a[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */
1032
1006
  ? -1
1033
1007
  : 1;
1034
1008
  }
1035
1009
  else if (a.length > b.length) {
1036
- return b.length === 1 && b[0] === 40 /* Static */ + 40 /* Segment */
1010
+ return b.length === 1 && b[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */
1037
1011
  ? 1
1038
1012
  : -1;
1039
1013
  }
@@ -1084,7 +1058,7 @@ var KduRouter = (function (exports, kdu) {
1084
1058
  }
1085
1059
 
1086
1060
  const ROOT_TOKEN = {
1087
- type: 0 /* Static */,
1061
+ type: 0 /* TokenType.Static */,
1088
1062
  value: '',
1089
1063
  };
1090
1064
  const VALID_PARAM_RE = /[a-zA-Z0-9_]/;
@@ -1104,7 +1078,7 @@ var KduRouter = (function (exports, kdu) {
1104
1078
  function crash(message) {
1105
1079
  throw new Error(`ERR (${state})/"${buffer}": ${message}`);
1106
1080
  }
1107
- let state = 0 /* Static */;
1081
+ let state = 0 /* TokenizerState.Static */;
1108
1082
  let previousState = state;
1109
1083
  const tokens = [];
1110
1084
  // the segment will always be valid because we get into the initial state
@@ -1126,19 +1100,19 @@ var KduRouter = (function (exports, kdu) {
1126
1100
  function consumeBuffer() {
1127
1101
  if (!buffer)
1128
1102
  return;
1129
- if (state === 0 /* Static */) {
1103
+ if (state === 0 /* TokenizerState.Static */) {
1130
1104
  segment.push({
1131
- type: 0 /* Static */,
1105
+ type: 0 /* TokenType.Static */,
1132
1106
  value: buffer,
1133
1107
  });
1134
1108
  }
1135
- else if (state === 1 /* Param */ ||
1136
- state === 2 /* ParamRegExp */ ||
1137
- state === 3 /* ParamRegExpEnd */) {
1109
+ else if (state === 1 /* TokenizerState.Param */ ||
1110
+ state === 2 /* TokenizerState.ParamRegExp */ ||
1111
+ state === 3 /* TokenizerState.ParamRegExpEnd */) {
1138
1112
  if (segment.length > 1 && (char === '*' || char === '+'))
1139
1113
  crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`);
1140
1114
  segment.push({
1141
- type: 1 /* Param */,
1115
+ type: 1 /* TokenType.Param */,
1142
1116
  value: buffer,
1143
1117
  regexp: customRe,
1144
1118
  repeatable: char === '*' || char === '+',
@@ -1155,13 +1129,13 @@ var KduRouter = (function (exports, kdu) {
1155
1129
  }
1156
1130
  while (i < path.length) {
1157
1131
  char = path[i++];
1158
- if (char === '\\' && state !== 2 /* ParamRegExp */) {
1132
+ if (char === '\\' && state !== 2 /* TokenizerState.ParamRegExp */) {
1159
1133
  previousState = state;
1160
- state = 4 /* EscapeNext */;
1134
+ state = 4 /* TokenizerState.EscapeNext */;
1161
1135
  continue;
1162
1136
  }
1163
1137
  switch (state) {
1164
- case 0 /* Static */:
1138
+ case 0 /* TokenizerState.Static */:
1165
1139
  if (char === '/') {
1166
1140
  if (buffer) {
1167
1141
  consumeBuffer();
@@ -1170,32 +1144,32 @@ var KduRouter = (function (exports, kdu) {
1170
1144
  }
1171
1145
  else if (char === ':') {
1172
1146
  consumeBuffer();
1173
- state = 1 /* Param */;
1147
+ state = 1 /* TokenizerState.Param */;
1174
1148
  }
1175
1149
  else {
1176
1150
  addCharToBuffer();
1177
1151
  }
1178
1152
  break;
1179
- case 4 /* EscapeNext */:
1153
+ case 4 /* TokenizerState.EscapeNext */:
1180
1154
  addCharToBuffer();
1181
1155
  state = previousState;
1182
1156
  break;
1183
- case 1 /* Param */:
1157
+ case 1 /* TokenizerState.Param */:
1184
1158
  if (char === '(') {
1185
- state = 2 /* ParamRegExp */;
1159
+ state = 2 /* TokenizerState.ParamRegExp */;
1186
1160
  }
1187
1161
  else if (VALID_PARAM_RE.test(char)) {
1188
1162
  addCharToBuffer();
1189
1163
  }
1190
1164
  else {
1191
1165
  consumeBuffer();
1192
- state = 0 /* Static */;
1166
+ state = 0 /* TokenizerState.Static */;
1193
1167
  // go back one character if we were not modifying
1194
1168
  if (char !== '*' && char !== '?' && char !== '+')
1195
1169
  i--;
1196
1170
  }
1197
1171
  break;
1198
- case 2 /* ParamRegExp */:
1172
+ case 2 /* TokenizerState.ParamRegExp */:
1199
1173
  // TODO: is it worth handling nested regexp? like :p(?:prefix_([^/]+)_suffix)
1200
1174
  // it already works by escaping the closing )
1201
1175
  // is this really something people need since you can also write
@@ -1205,16 +1179,16 @@ var KduRouter = (function (exports, kdu) {
1205
1179
  if (customRe[customRe.length - 1] == '\\')
1206
1180
  customRe = customRe.slice(0, -1) + char;
1207
1181
  else
1208
- state = 3 /* ParamRegExpEnd */;
1182
+ state = 3 /* TokenizerState.ParamRegExpEnd */;
1209
1183
  }
1210
1184
  else {
1211
1185
  customRe += char;
1212
1186
  }
1213
1187
  break;
1214
- case 3 /* ParamRegExpEnd */:
1188
+ case 3 /* TokenizerState.ParamRegExpEnd */:
1215
1189
  // same as finalizing a param
1216
1190
  consumeBuffer();
1217
- state = 0 /* Static */;
1191
+ state = 0 /* TokenizerState.Static */;
1218
1192
  // go back one character if we were not modifying
1219
1193
  if (char !== '*' && char !== '?' && char !== '+')
1220
1194
  i--;
@@ -1225,7 +1199,7 @@ var KduRouter = (function (exports, kdu) {
1225
1199
  break;
1226
1200
  }
1227
1201
  }
1228
- if (state === 2 /* ParamRegExp */)
1202
+ if (state === 2 /* TokenizerState.ParamRegExp */)
1229
1203
  crash(`Unfinished custom RegExp for param "${buffer}"`);
1230
1204
  consumeBuffer();
1231
1205
  finalizeSegment();
@@ -1280,6 +1254,9 @@ var KduRouter = (function (exports, kdu) {
1280
1254
  // used later on to remove by name
1281
1255
  const isRootAdd = !originalRecord;
1282
1256
  const mainNormalizedRecord = normalizeRouteRecord(record);
1257
+ {
1258
+ checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent);
1259
+ }
1283
1260
  // we might be the child of an alias
1284
1261
  mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record;
1285
1262
  const options = mergeOptions(globalOptions, record);
@@ -1323,11 +1300,11 @@ var KduRouter = (function (exports, kdu) {
1323
1300
  throw new Error('Catch all routes ("*") must now be defined using a param with a custom regexp.\n' +
1324
1301
  'See more at https://kdujs-router.web.app/guide/migration/#removed-star-or-catch-all-routes.');
1325
1302
  }
1326
- // create the object before hand so it can be passed to children
1303
+ // create the object beforehand, so it can be passed to children
1327
1304
  matcher = createRouteRecordMatcher(normalizedRecord, parent, options);
1328
1305
  if (parent && path[0] === '/')
1329
1306
  checkMissingParamsInAbsolutePath(matcher, parent);
1330
- // if we are an alias we must tell the original record that we exist
1307
+ // if we are an alias we must tell the original record that we exist,
1331
1308
  // so we can be removed
1332
1309
  if (originalRecord) {
1333
1310
  originalRecord.alias.push(matcher);
@@ -1345,20 +1322,27 @@ var KduRouter = (function (exports, kdu) {
1345
1322
  if (isRootAdd && record.name && !isAliasRecord(matcher))
1346
1323
  removeRoute(record.name);
1347
1324
  }
1348
- if ('children' in mainNormalizedRecord) {
1325
+ if (mainNormalizedRecord.children) {
1349
1326
  const children = mainNormalizedRecord.children;
1350
1327
  for (let i = 0; i < children.length; i++) {
1351
1328
  addRoute(children[i], matcher, originalRecord && originalRecord.children[i]);
1352
1329
  }
1353
1330
  }
1354
1331
  // if there was no original record, then the first one was not an alias and all
1355
- // other alias (if any) need to reference this record when adding children
1332
+ // other aliases (if any) need to reference this record when adding children
1356
1333
  originalRecord = originalRecord || matcher;
1357
1334
  // TODO: add normalized records for more flexibility
1358
1335
  // if (parent && isAliasRecord(originalRecord)) {
1359
1336
  // parent.children.push(originalRecord)
1360
1337
  // }
1361
- insertMatcher(matcher);
1338
+ // Avoid adding a record that doesn't display anything. This allows passing through records without a component to
1339
+ // not be reached and pass through the catch all route
1340
+ if ((matcher.record.components &&
1341
+ Object.keys(matcher.record.components).length) ||
1342
+ matcher.record.name ||
1343
+ matcher.record.redirect) {
1344
+ insertMatcher(matcher);
1345
+ }
1362
1346
  }
1363
1347
  return originalMatcher
1364
1348
  ? () => {
@@ -1412,16 +1396,27 @@ var KduRouter = (function (exports, kdu) {
1412
1396
  if ('name' in location && location.name) {
1413
1397
  matcher = matcherMap.get(location.name);
1414
1398
  if (!matcher)
1415
- throw createRouterError(1 /* MATCHER_NOT_FOUND */, {
1399
+ throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, {
1416
1400
  location,
1417
1401
  });
1402
+ // warn if the user is passing invalid params so they can debug it better when they get removed
1403
+ {
1404
+ const invalidParams = Object.keys(location.params || {}).filter(paramName => !matcher.keys.find(k => k.name === paramName));
1405
+ if (invalidParams.length) {
1406
+ warn(`Discarded invalid param(s) "${invalidParams.join('", "')}" when navigating.`);
1407
+ }
1408
+ }
1418
1409
  name = matcher.record.name;
1419
1410
  params = assign(
1420
1411
  // paramsFromLocation is a new object
1421
1412
  paramsFromLocation(currentLocation.params,
1422
1413
  // only keep params that exist in the resolved location
1423
1414
  // TODO: only keep optional params coming from a parent record
1424
- matcher.keys.filter(k => !k.optional).map(k => k.name)), location.params);
1415
+ matcher.keys.filter(k => !k.optional).map(k => k.name)),
1416
+ // discard any existing params in the current location that do not exist here
1417
+ // #1497 this ensures better active/exact matching
1418
+ location.params &&
1419
+ paramsFromLocation(location.params, matcher.keys.map(k => k.name)));
1425
1420
  // throws if cannot be stringified
1426
1421
  path = matcher.stringify(params);
1427
1422
  }
@@ -1435,7 +1430,6 @@ var KduRouter = (function (exports, kdu) {
1435
1430
  matcher = matchers.find(m => m.re.test(path));
1436
1431
  // matcher should have a value after the loop
1437
1432
  if (matcher) {
1438
- // TODO: dev warning of unused params if provided
1439
1433
  // we know the matcher works because we tested the regexp
1440
1434
  params = matcher.parse(path);
1441
1435
  name = matcher.record.name;
@@ -1448,7 +1442,7 @@ var KduRouter = (function (exports, kdu) {
1448
1442
  ? matcherMap.get(currentLocation.name)
1449
1443
  : matchers.find(m => m.re.test(currentLocation.path));
1450
1444
  if (!matcher)
1451
- throw createRouterError(1 /* MATCHER_NOT_FOUND */, {
1445
+ throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, {
1452
1446
  location,
1453
1447
  currentLocation,
1454
1448
  });
@@ -1506,8 +1500,8 @@ var KduRouter = (function (exports, kdu) {
1506
1500
  updateGuards: new Set(),
1507
1501
  enterCallbacks: {},
1508
1502
  components: 'components' in record
1509
- ? record.components || {}
1510
- : { default: record.component },
1503
+ ? record.components || null
1504
+ : record.component && { default: record.component },
1511
1505
  };
1512
1506
  }
1513
1507
  /**
@@ -1517,7 +1511,7 @@ var KduRouter = (function (exports, kdu) {
1517
1511
  */
1518
1512
  function normalizeRecordProps(record) {
1519
1513
  const propsObject = {};
1520
- // props does not exist on redirect records but we can set false directly
1514
+ // props does not exist on redirect records, but we can set false directly
1521
1515
  const props = record.props || false;
1522
1516
  if ('component' in record) {
1523
1517
  propsObject.default = props;
@@ -1571,17 +1565,31 @@ var KduRouter = (function (exports, kdu) {
1571
1565
  function checkSameParams(a, b) {
1572
1566
  for (const key of a.keys) {
1573
1567
  if (!key.optional && !b.keys.find(isSameParam.bind(null, key)))
1574
- return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
1568
+ return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`);
1575
1569
  }
1576
1570
  for (const key of b.keys) {
1577
1571
  if (!key.optional && !a.keys.find(isSameParam.bind(null, key)))
1578
- return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
1572
+ return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`);
1573
+ }
1574
+ }
1575
+ /**
1576
+ * A route with a name and a child with an empty path without a name should warn when adding the route
1577
+ *
1578
+ * @param mainNormalizedRecord - RouteRecordNormalized
1579
+ * @param parent - RouteRecordMatcher
1580
+ */
1581
+ function checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent) {
1582
+ if (parent &&
1583
+ parent.record.name &&
1584
+ !mainNormalizedRecord.name &&
1585
+ !mainNormalizedRecord.path) {
1586
+ warn(`The route named "${String(parent.record.name)}" has a child without a name and an empty path. Using that name won't render the empty path child so you probably want to move the name to the child instead. If this is intentional, add a name to the child route to remove the warning.`);
1579
1587
  }
1580
1588
  }
1581
1589
  function checkMissingParamsInAbsolutePath(record, parent) {
1582
1590
  for (const key of parent.keys) {
1583
1591
  if (!record.keys.find(isSameParam.bind(null, key)))
1584
- return warn(`Absolute path "${record.record.path}" should have the exact same param named "${key.name}" as its parent "${parent.record.path}".`);
1592
+ return warn(`Absolute path "${record.record.path}" must have the exact same param named "${key.name}" as its parent "${parent.record.path}".`);
1585
1593
  }
1586
1594
  }
1587
1595
  function isRecordChildOf(record, parent) {
@@ -1595,7 +1603,7 @@ var KduRouter = (function (exports, kdu) {
1595
1603
  * On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2)
1596
1604
  * defines some extra characters to be encoded. Most browsers do not encode them
1597
1605
  * in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to
1598
- * also encode `!'()*`. Leaving unencoded only ASCII alphanumeric(`a-zA-Z0-9`)
1606
+ * also encode `!'()*`. Leaving un-encoded only ASCII alphanumeric(`a-zA-Z0-9`)
1599
1607
  * plus `-._~`. This extra safety should be applied to query by patching the
1600
1608
  * string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\`
1601
1609
  * should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\`
@@ -1619,7 +1627,7 @@ var KduRouter = (function (exports, kdu) {
1619
1627
  * application/x-www-form-urlencoded
1620
1628
  * (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo
1621
1629
  * leave the plus character as is in queries. To be more flexible, we allow the
1622
- * plus character on the query but it can also be manually encoded by the user.
1630
+ * plus character on the query, but it can also be manually encoded by the user.
1623
1631
  *
1624
1632
  * Resources:
1625
1633
  * - https://url.spec.whatwg.org/#urlencoded-parsing
@@ -1751,7 +1759,7 @@ var KduRouter = (function (exports, kdu) {
1751
1759
  if (key in query) {
1752
1760
  // an extra variable for ts types
1753
1761
  let currentValue = query[key];
1754
- if (!Array.isArray(currentValue)) {
1762
+ if (!isArray(currentValue)) {
1755
1763
  currentValue = query[key] = [currentValue];
1756
1764
  }
1757
1765
  currentValue.push(value);
@@ -1784,7 +1792,7 @@ var KduRouter = (function (exports, kdu) {
1784
1792
  continue;
1785
1793
  }
1786
1794
  // keep null values
1787
- const values = Array.isArray(value)
1795
+ const values = isArray(value)
1788
1796
  ? value.map(v => v && encodeQueryValue(v))
1789
1797
  : [value && encodeQueryValue(value)];
1790
1798
  values.forEach(value => {
@@ -1813,7 +1821,7 @@ var KduRouter = (function (exports, kdu) {
1813
1821
  for (const key in query) {
1814
1822
  const value = query[key];
1815
1823
  if (value !== undefined) {
1816
- normalizedQuery[key] = Array.isArray(value)
1824
+ normalizedQuery[key] = isArray(value)
1817
1825
  ? value.map(v => (v == null ? null : '' + v))
1818
1826
  : value == null
1819
1827
  ? value
@@ -1823,6 +1831,43 @@ var KduRouter = (function (exports, kdu) {
1823
1831
  return normalizedQuery;
1824
1832
  }
1825
1833
 
1834
+ /**
1835
+ * RouteRecord being rendered by the closest ancestor Router View. Used for
1836
+ * `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View
1837
+ * Location Matched
1838
+ *
1839
+ * @internal
1840
+ */
1841
+ const matchedRouteKey = Symbol('router view location matched' );
1842
+ /**
1843
+ * Allows overriding the router view depth to control which component in
1844
+ * `matched` is rendered. rvd stands for Router View Depth
1845
+ *
1846
+ * @internal
1847
+ */
1848
+ const viewDepthKey = Symbol('router view depth' );
1849
+ /**
1850
+ * Allows overriding the router instance returned by `useRouter` in tests. r
1851
+ * stands for router
1852
+ *
1853
+ * @internal
1854
+ */
1855
+ const routerKey = Symbol('router' );
1856
+ /**
1857
+ * Allows overriding the current route returned by `useRoute` in tests. rl
1858
+ * stands for route location
1859
+ *
1860
+ * @internal
1861
+ */
1862
+ const routeLocationKey = Symbol('route location' );
1863
+ /**
1864
+ * Allows overriding the current route used by router-view. Internally this is
1865
+ * used when the `route` prop is passed.
1866
+ *
1867
+ * @internal
1868
+ */
1869
+ const routerViewLocationKey = Symbol('router view location' );
1870
+
1826
1871
  /**
1827
1872
  * Create a list of callbacks that can be reset. Used to create before and after navigation guards list
1828
1873
  */
@@ -1873,7 +1918,7 @@ var KduRouter = (function (exports, kdu) {
1873
1918
  // to avoid warning
1874
1919
  {}).value;
1875
1920
  if (!activeRecord) {
1876
- warn('No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.kdu?');
1921
+ warn('No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside a component child of <router-view>. Maybe you called it inside of App.kdu?');
1877
1922
  return;
1878
1923
  }
1879
1924
  registerGuard(activeRecord, 'leaveGuards', leaveGuard);
@@ -1894,7 +1939,7 @@ var KduRouter = (function (exports, kdu) {
1894
1939
  // to avoid warning
1895
1940
  {}).value;
1896
1941
  if (!activeRecord) {
1897
- warn('No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.kdu?');
1942
+ warn('No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside a component child of <router-view>. Maybe you called it inside of App.kdu?');
1898
1943
  return;
1899
1944
  }
1900
1945
  registerGuard(activeRecord, 'updateGuards', updateGuard);
@@ -1906,16 +1951,17 @@ var KduRouter = (function (exports, kdu) {
1906
1951
  (record.enterCallbacks[name] = record.enterCallbacks[name] || []);
1907
1952
  return () => new Promise((resolve, reject) => {
1908
1953
  const next = (valid) => {
1909
- if (valid === false)
1910
- reject(createRouterError(4 /* NAVIGATION_ABORTED */, {
1954
+ if (valid === false) {
1955
+ reject(createRouterError(4 /* ErrorTypes.NAVIGATION_ABORTED */, {
1911
1956
  from,
1912
1957
  to,
1913
1958
  }));
1959
+ }
1914
1960
  else if (valid instanceof Error) {
1915
1961
  reject(valid);
1916
1962
  }
1917
1963
  else if (isRouteLocation(valid)) {
1918
- reject(createRouterError(2 /* NAVIGATION_GUARD_REDIRECT */, {
1964
+ reject(createRouterError(2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */, {
1919
1965
  from: to,
1920
1966
  to: valid,
1921
1967
  }));
@@ -1924,8 +1970,9 @@ var KduRouter = (function (exports, kdu) {
1924
1970
  if (enterCallbackArray &&
1925
1971
  // since enterCallbackArray is truthy, both record and name also are
1926
1972
  record.enterCallbacks[name] === enterCallbackArray &&
1927
- typeof valid === 'function')
1973
+ typeof valid === 'function') {
1928
1974
  enterCallbackArray.push(valid);
1975
+ }
1929
1976
  resolve();
1930
1977
  }
1931
1978
  };
@@ -1945,7 +1992,6 @@ var KduRouter = (function (exports, kdu) {
1945
1992
  }
1946
1993
  return resolvedValue;
1947
1994
  });
1948
- // TODO: test me!
1949
1995
  }
1950
1996
  else if (guardReturn !== undefined) {
1951
1997
  // @ts-expect-error: _called is added at canOnlyBeCalledOnce
@@ -1973,6 +2019,10 @@ var KduRouter = (function (exports, kdu) {
1973
2019
  function extractComponentsGuards(matched, guardType, to, from) {
1974
2020
  const guards = [];
1975
2021
  for (const record of matched) {
2022
+ if (!record.components && !record.children.length) {
2023
+ warn(`Record with path "${record.path}" is either missing a "component(s)"` +
2024
+ ` or "children" property.`);
2025
+ }
1976
2026
  for (const name in record.components) {
1977
2027
  let rawComponent = record.components[name];
1978
2028
  {
@@ -2029,6 +2079,7 @@ var KduRouter = (function (exports, kdu) {
2029
2079
  ? resolved.default
2030
2080
  : resolved;
2031
2081
  // replace the function with the resolved component
2082
+ // cannot be null or undefined because we went into the for loop
2032
2083
  record.components[name] = resolvedComponent;
2033
2084
  // __kccOpts is added by kdu-class-component and contain the regular options
2034
2085
  const options = resolvedComponent.__kccOpts || resolvedComponent;
@@ -2042,6 +2093,7 @@ var KduRouter = (function (exports, kdu) {
2042
2093
  }
2043
2094
  /**
2044
2095
  * Allows differentiating lazy components from functional components and kdu-class-component
2096
+ * @internal
2045
2097
  *
2046
2098
  * @param component
2047
2099
  */
@@ -2050,6 +2102,34 @@ var KduRouter = (function (exports, kdu) {
2050
2102
  'displayName' in component ||
2051
2103
  'props' in component ||
2052
2104
  '__kccOpts' in component);
2105
+ }
2106
+ /**
2107
+ * Ensures a route is loaded, so it can be passed as o prop to `<RouterView>`.
2108
+ *
2109
+ * @param route - resolved route to load
2110
+ */
2111
+ function loadRouteLocation(route) {
2112
+ return route.matched.every(record => record.redirect)
2113
+ ? Promise.reject(new Error('Cannot load a route that redirects.'))
2114
+ : Promise.all(route.matched.map(record => record.components &&
2115
+ Promise.all(Object.keys(record.components).reduce((promises, name) => {
2116
+ const rawComponent = record.components[name];
2117
+ if (typeof rawComponent === 'function' &&
2118
+ !('displayName' in rawComponent)) {
2119
+ promises.push(rawComponent().then(resolved => {
2120
+ if (!resolved)
2121
+ return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}". Ensure you passed a function that returns a promise.`));
2122
+ const resolvedComponent = isESModule(resolved)
2123
+ ? resolved.default
2124
+ : resolved;
2125
+ // replace the function with the resolved component
2126
+ // cannot be null or undefined because we went into the for loop
2127
+ record.components[name] = resolvedComponent;
2128
+ return;
2129
+ }));
2130
+ }
2131
+ return promises;
2132
+ }, [])))).then(() => route);
2053
2133
  }
2054
2134
 
2055
2135
  // TODO: we could allow currentRoute as a prop to expose `isActive` and
@@ -2115,6 +2195,9 @@ var KduRouter = (function (exports, kdu) {
2115
2195
  }, { flush: 'post' });
2116
2196
  }
2117
2197
  }
2198
+ /**
2199
+ * NOTE: update {@link _RouterLinkI}'s `$slots` type when updating this
2200
+ */
2118
2201
  return {
2119
2202
  route,
2120
2203
  href: kdu.computed(() => route.value.href),
@@ -2164,7 +2247,7 @@ var KduRouter = (function (exports, kdu) {
2164
2247
  : null,
2165
2248
  href: link.href,
2166
2249
  // this would override user added attrs but Kdu will still add
2167
- // the listener so we end up triggering both
2250
+ // the listener, so we end up triggering both
2168
2251
  onClick: link.navigate,
2169
2252
  class: elClass.value,
2170
2253
  }, children);
@@ -2209,7 +2292,7 @@ var KduRouter = (function (exports, kdu) {
2209
2292
  return false;
2210
2293
  }
2211
2294
  else {
2212
- if (!Array.isArray(outerValue) ||
2295
+ if (!isArray(outerValue) ||
2213
2296
  outerValue.length !== innerValue.length ||
2214
2297
  innerValue.some((value, i) => value !== outerValue[i]))
2215
2298
  return false;
@@ -2253,9 +2336,21 @@ var KduRouter = (function (exports, kdu) {
2253
2336
  warnDeprecatedUsage();
2254
2337
  const injectedRoute = kdu.inject(routerViewLocationKey);
2255
2338
  const routeToDisplay = kdu.computed(() => props.route || injectedRoute.value);
2256
- const depth = kdu.inject(viewDepthKey, 0);
2257
- const matchedRouteRef = kdu.computed(() => routeToDisplay.value.matched[depth]);
2258
- kdu.provide(viewDepthKey, depth + 1);
2339
+ const injectedDepth = kdu.inject(viewDepthKey, 0);
2340
+ // The depth changes based on empty components option, which allows passthrough routes e.g. routes with children
2341
+ // that are used to reuse the `path` property
2342
+ const depth = kdu.computed(() => {
2343
+ let initialDepth = kdu.unref(injectedDepth);
2344
+ const { matched } = routeToDisplay.value;
2345
+ let matchedRoute;
2346
+ while ((matchedRoute = matched[initialDepth]) &&
2347
+ !matchedRoute.components) {
2348
+ initialDepth++;
2349
+ }
2350
+ return initialDepth;
2351
+ });
2352
+ const matchedRouteRef = kdu.computed(() => routeToDisplay.value.matched[depth.value]);
2353
+ kdu.provide(viewDepthKey, kdu.computed(() => depth.value + 1));
2259
2354
  kdu.provide(matchedRouteKey, matchedRouteRef);
2260
2355
  kdu.provide(routerViewLocationKey, routeToDisplay);
2261
2356
  const viewRef = kdu.ref();
@@ -2267,7 +2362,7 @@ var KduRouter = (function (exports, kdu) {
2267
2362
  // this will update the instance for new instances as well as reused
2268
2363
  // instances when navigating to a new route
2269
2364
  to.instances[name] = instance;
2270
- // the component instance is reused for a different route or name so
2365
+ // the component instance is reused for a different route or name, so
2271
2366
  // we copy any saved update or leave guards. With async setup, the
2272
2367
  // mounting component will mount before the matchedRoute changes,
2273
2368
  // making instance === oldInstance, so we check if guards have been
@@ -2293,16 +2388,16 @@ var KduRouter = (function (exports, kdu) {
2293
2388
  }, { flush: 'post' });
2294
2389
  return () => {
2295
2390
  const route = routeToDisplay.value;
2296
- const matchedRoute = matchedRouteRef.value;
2297
- const ViewComponent = matchedRoute && matchedRoute.components[props.name];
2298
2391
  // we need the value at the time we render because when we unmount, we
2299
2392
  // navigated to a different location so the value is different
2300
2393
  const currentName = props.name;
2394
+ const matchedRoute = matchedRouteRef.value;
2395
+ const ViewComponent = matchedRoute && matchedRoute.components[currentName];
2301
2396
  if (!ViewComponent) {
2302
2397
  return normalizeSlot(slots.default, { Component: ViewComponent, route });
2303
2398
  }
2304
2399
  // props from route configuration
2305
- const routePropsOption = matchedRoute.props[props.name];
2400
+ const routePropsOption = matchedRoute.props[currentName];
2306
2401
  const routeProps = routePropsOption
2307
2402
  ? routePropsOption === true
2308
2403
  ? route.params
@@ -2324,12 +2419,12 @@ var KduRouter = (function (exports, kdu) {
2324
2419
  component.ref) {
2325
2420
  // TODO: can display if it's an alias, its props
2326
2421
  const info = {
2327
- depth,
2422
+ depth: depth.value,
2328
2423
  name: matchedRoute.name,
2329
2424
  path: matchedRoute.path,
2330
2425
  meta: matchedRoute.meta,
2331
2426
  };
2332
- const internalInstances = Array.isArray(component.ref)
2427
+ const internalInstances = isArray(component.ref)
2333
2428
  ? component.ref.map(r => r.i)
2334
2429
  : [component.ref.i];
2335
2430
  internalInstances.forEach(instance => {
@@ -2391,6 +2486,30 @@ var KduRouter = (function (exports, kdu) {
2391
2486
  const HOOK_SETUP = 'devtools-plugin:setup';
2392
2487
  const HOOK_PLUGIN_SETTINGS_SET = 'plugin:settings:set';
2393
2488
 
2489
+ let supported;
2490
+ let perf;
2491
+ function isPerformanceSupported() {
2492
+ var _a;
2493
+ if (supported !== undefined) {
2494
+ return supported;
2495
+ }
2496
+ if (typeof window !== 'undefined' && window.performance) {
2497
+ supported = true;
2498
+ perf = window.performance;
2499
+ }
2500
+ else if (typeof global !== 'undefined' && ((_a = global.perf_hooks) === null || _a === void 0 ? void 0 : _a.performance)) {
2501
+ supported = true;
2502
+ perf = global.perf_hooks.performance;
2503
+ }
2504
+ else {
2505
+ supported = false;
2506
+ }
2507
+ return supported;
2508
+ }
2509
+ function now() {
2510
+ return isPerformanceSupported() ? perf.now() : Date.now();
2511
+ }
2512
+
2394
2513
  class ApiProxy {
2395
2514
  constructor(plugin, hook) {
2396
2515
  this.target = null;
@@ -2428,6 +2547,9 @@ var KduRouter = (function (exports, kdu) {
2428
2547
  }
2429
2548
  currentSettings = value;
2430
2549
  },
2550
+ now() {
2551
+ return now();
2552
+ },
2431
2553
  };
2432
2554
  if (hook) {
2433
2555
  hook.on(HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => {
@@ -2495,17 +2617,18 @@ var KduRouter = (function (exports, kdu) {
2495
2617
  }
2496
2618
 
2497
2619
  function setupDevtoolsPlugin(pluginDescriptor, setupFn) {
2620
+ const descriptor = pluginDescriptor;
2498
2621
  const target = getTarget();
2499
2622
  const hook = getDevtoolsGlobalHook();
2500
- const enableProxy = isProxyAvailable && pluginDescriptor.enableEarlyProxy;
2623
+ const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy;
2501
2624
  if (hook && (target.__KDU_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) {
2502
2625
  hook.emit(HOOK_SETUP, pluginDescriptor, setupFn);
2503
2626
  }
2504
2627
  else {
2505
- const proxy = enableProxy ? new ApiProxy(pluginDescriptor, hook) : null;
2628
+ const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null;
2506
2629
  const list = target.__KDU_DEVTOOLS_PLUGINS__ = target.__KDU_DEVTOOLS_PLUGINS__ || [];
2507
2630
  list.push({
2508
- pluginDescriptor,
2631
+ pluginDescriptor: descriptor,
2509
2632
  setupFn,
2510
2633
  proxy,
2511
2634
  });
@@ -2514,6 +2637,13 @@ var KduRouter = (function (exports, kdu) {
2514
2637
  }
2515
2638
  }
2516
2639
 
2640
+ /**
2641
+ * Copies a route location and removes any problematic properties that cannot be shown in devtools (e.g. Kdu instances).
2642
+ *
2643
+ * @param routeLocation - routeLocation to format
2644
+ * @param tooltip - optional tooltip
2645
+ * @returns a copy of the routeLocation
2646
+ */
2517
2647
  function formatRouteLocation(routeLocation, tooltip) {
2518
2648
  const copy = assign({}, routeLocation, {
2519
2649
  // remove variables that can contain kdu instances
@@ -2555,6 +2685,9 @@ var KduRouter = (function (exports, kdu) {
2555
2685
  componentStateTypes: ['Routing'],
2556
2686
  app,
2557
2687
  }, api => {
2688
+ if (typeof api.now !== 'function') {
2689
+ console.warn('[Kdu Router]: You seem to be using an outdated version of Kdu Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://kdujs-devtools.web.app/guide/installation.html.');
2690
+ }
2558
2691
  // display state added by the router
2559
2692
  api.on.inspectComponent((payload, ctx) => {
2560
2693
  if (payload.instanceData) {
@@ -2578,7 +2711,7 @@ var KduRouter = (function (exports, kdu) {
2578
2711
  });
2579
2712
  }
2580
2713
  // if multiple useLink are used
2581
- if (Array.isArray(componentInstance.__krl_devtools)) {
2714
+ if (isArray(componentInstance.__krl_devtools)) {
2582
2715
  componentInstance.__devtoolsApi = api;
2583
2716
  componentInstance.__krl_devtools.forEach(devtoolsData => {
2584
2717
  let backgroundColor = ORANGE_400;
@@ -2626,7 +2759,6 @@ var KduRouter = (function (exports, kdu) {
2626
2759
  title: 'Error during Navigation',
2627
2760
  subtitle: to.fullPath,
2628
2761
  logType: 'error',
2629
- // @ts-ignore
2630
2762
  time: api.now(),
2631
2763
  data: { error },
2632
2764
  groupId: to.meta.__navigationId,
@@ -2648,7 +2780,6 @@ var KduRouter = (function (exports, kdu) {
2648
2780
  api.addTimelineEvent({
2649
2781
  layerId: navigationsLayerId,
2650
2782
  event: {
2651
- // @ts-ignore
2652
2783
  time: api.now(),
2653
2784
  title: 'Start of navigation',
2654
2785
  subtitle: to.fullPath,
@@ -2684,7 +2815,6 @@ var KduRouter = (function (exports, kdu) {
2684
2815
  event: {
2685
2816
  title: 'End of navigation',
2686
2817
  subtitle: to.fullPath,
2687
- // @ts-ignore
2688
2818
  time: api.now(),
2689
2819
  data,
2690
2820
  logType: failure ? 'warning' : 'default',
@@ -2734,7 +2864,7 @@ var KduRouter = (function (exports, kdu) {
2734
2864
  api.on.getInspectorState(payload => {
2735
2865
  if (payload.app === app && payload.inspectorId === routerInspectorId) {
2736
2866
  const routes = matcher.getRoutes();
2737
- const route = routes.find(route => route.record.__vd_id === payload.nodeId);
2867
+ const route = routes.find(route => route.record.__kd_id === payload.nodeId);
2738
2868
  if (route) {
2739
2869
  payload.state = {
2740
2870
  options: formatRouteRecordMatcherForStateInspector(route),
@@ -2798,6 +2928,13 @@ var KduRouter = (function (exports, kdu) {
2798
2928
  value: route.alias.map(alias => alias.record.path),
2799
2929
  });
2800
2930
  }
2931
+ if (Object.keys(route.record.meta).length) {
2932
+ fields.push({
2933
+ editable: false,
2934
+ key: 'meta',
2935
+ value: route.record.meta,
2936
+ });
2937
+ }
2801
2938
  fields.push({
2802
2939
  key: 'score',
2803
2940
  editable: false,
@@ -2840,21 +2977,21 @@ var KduRouter = (function (exports, kdu) {
2840
2977
  backgroundColor: ORANGE_400,
2841
2978
  });
2842
2979
  }
2843
- if (route.__vd_match) {
2980
+ if (route.__kd_match) {
2844
2981
  tags.push({
2845
2982
  label: 'matches',
2846
2983
  textColor: 0,
2847
2984
  backgroundColor: PINK_500,
2848
2985
  });
2849
2986
  }
2850
- if (route.__vd_exactActive) {
2987
+ if (route.__kd_exactActive) {
2851
2988
  tags.push({
2852
2989
  label: 'exact',
2853
2990
  textColor: 0,
2854
2991
  backgroundColor: LIME_500,
2855
2992
  });
2856
2993
  }
2857
- if (route.__vd_active) {
2994
+ if (route.__kd_active) {
2858
2995
  tags.push({
2859
2996
  label: 'active',
2860
2997
  textColor: 0,
@@ -2863,18 +3000,19 @@ var KduRouter = (function (exports, kdu) {
2863
3000
  }
2864
3001
  if (record.redirect) {
2865
3002
  tags.push({
2866
- label: 'redirect: ' +
2867
- (typeof record.redirect === 'string' ? record.redirect : 'Object'),
3003
+ label: typeof record.redirect === 'string'
3004
+ ? `redirect: ${record.redirect}`
3005
+ : 'redirects',
2868
3006
  textColor: 0xffffff,
2869
3007
  backgroundColor: DARK,
2870
3008
  });
2871
3009
  }
2872
3010
  // add an id to be able to select it. Using the `path` is not possible because
2873
3011
  // empty path children would collide with their parents
2874
- let id = record.__vd_id;
3012
+ let id = record.__kd_id;
2875
3013
  if (id == null) {
2876
3014
  id = String(routeRecordId++);
2877
- record.__vd_id = id;
3015
+ record.__kd_id = id;
2878
3016
  }
2879
3017
  return {
2880
3018
  id,
@@ -2891,19 +3029,19 @@ var KduRouter = (function (exports, kdu) {
2891
3029
  // reset the matching state
2892
3030
  const isExactActive = currentRoute.matched.length &&
2893
3031
  isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record);
2894
- route.__vd_exactActive = route.__vd_active = isExactActive;
3032
+ route.__kd_exactActive = route.__kd_active = isExactActive;
2895
3033
  if (!isExactActive) {
2896
- route.__vd_active = currentRoute.matched.some(match => isSameRouteRecord(match, route.record));
3034
+ route.__kd_active = currentRoute.matched.some(match => isSameRouteRecord(match, route.record));
2897
3035
  }
2898
3036
  route.children.forEach(childRoute => markRouteRecordActive(childRoute, currentRoute));
2899
3037
  }
2900
3038
  function resetMatchStateOnRouteRecord(route) {
2901
- route.__vd_match = false;
3039
+ route.__kd_match = false;
2902
3040
  route.children.forEach(resetMatchStateOnRouteRecord);
2903
3041
  }
2904
3042
  function isRouteMatching(route, filter) {
2905
3043
  const found = String(route.re).match(EXTRACT_REGEXP_RE);
2906
- route.__vd_match = false;
3044
+ route.__kd_match = false;
2907
3045
  if (!found || found.length < 3) {
2908
3046
  return false;
2909
3047
  }
@@ -2914,7 +3052,7 @@ var KduRouter = (function (exports, kdu) {
2914
3052
  route.children.forEach(child => isRouteMatching(child, filter));
2915
3053
  // exception case: `/`
2916
3054
  if (route.record.path !== '/' || filter === '/') {
2917
- route.__vd_match = route.re.test(filter);
3055
+ route.__kd_match = route.re.test(filter);
2918
3056
  return true;
2919
3057
  }
2920
3058
  // hide the / route
@@ -3043,7 +3181,7 @@ var KduRouter = (function (exports, kdu) {
3043
3181
  delete targetParams[key];
3044
3182
  }
3045
3183
  }
3046
- // pass encoded values to the matcher so it can produce encoded path and fullPath
3184
+ // pass encoded values to the matcher, so it can produce encoded path and fullPath
3047
3185
  matcherLocation = assign({}, rawLocation, {
3048
3186
  params: encodeParams(rawLocation.params),
3049
3187
  });
@@ -3056,7 +3194,7 @@ var KduRouter = (function (exports, kdu) {
3056
3194
  if (hash && !hash.startsWith('#')) {
3057
3195
  warn(`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`);
3058
3196
  }
3059
- // decoding them) the matcher might have merged current location params so
3197
+ // the matcher might have merged current location params, so
3060
3198
  // we need to run the decoding again
3061
3199
  matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params));
3062
3200
  const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, {
@@ -3097,7 +3235,7 @@ var KduRouter = (function (exports, kdu) {
3097
3235
  }
3098
3236
  function checkCanceledNavigation(to, from) {
3099
3237
  if (pendingLocation !== to) {
3100
- return createRouterError(8 /* NAVIGATION_CANCELLED */, {
3238
+ return createRouterError(8 /* ErrorTypes.NAVIGATION_CANCELLED */, {
3101
3239
  from,
3102
3240
  to,
3103
3241
  });
@@ -3132,7 +3270,8 @@ var KduRouter = (function (exports, kdu) {
3132
3270
  return assign({
3133
3271
  query: to.query,
3134
3272
  hash: to.hash,
3135
- params: to.params,
3273
+ // avoid transferring params if the redirect has a path
3274
+ params: 'path' in newTargetLocation ? {} : to.params,
3136
3275
  }, newTargetLocation);
3137
3276
  }
3138
3277
  }
@@ -3146,7 +3285,9 @@ var KduRouter = (function (exports, kdu) {
3146
3285
  const shouldRedirect = handleRedirectRecord(targetLocation);
3147
3286
  if (shouldRedirect)
3148
3287
  return pushWithRedirect(assign(locationAsObject(shouldRedirect), {
3149
- state: data,
3288
+ state: typeof shouldRedirect === 'object'
3289
+ ? assign({}, data, shouldRedirect.state)
3290
+ : data,
3150
3291
  force,
3151
3292
  replace,
3152
3293
  }),
@@ -3157,7 +3298,7 @@ var KduRouter = (function (exports, kdu) {
3157
3298
  toLocation.redirectedFrom = redirectedFrom;
3158
3299
  let failure;
3159
3300
  if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
3160
- failure = createRouterError(16 /* NAVIGATION_DUPLICATED */, { to: toLocation, from });
3301
+ failure = createRouterError(16 /* ErrorTypes.NAVIGATION_DUPLICATED */, { to: toLocation, from });
3161
3302
  // trigger scroll to allow scrolling to the same anchor
3162
3303
  handleScroll(from, from,
3163
3304
  // this is a push, the only way for it to be triggered from a
@@ -3170,14 +3311,14 @@ var KduRouter = (function (exports, kdu) {
3170
3311
  return (failure ? Promise.resolve(failure) : navigate(toLocation, from))
3171
3312
  .catch((error) => isNavigationFailure(error)
3172
3313
  ? // navigation redirects still mark the router as ready
3173
- isNavigationFailure(error, 2 /* NAVIGATION_GUARD_REDIRECT */)
3314
+ isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)
3174
3315
  ? error
3175
3316
  : markAsReady(error) // also returns the error
3176
3317
  : // reject any unknown error
3177
3318
  triggerError(error, toLocation, from))
3178
3319
  .then((failure) => {
3179
3320
  if (failure) {
3180
- if (isNavigationFailure(failure, 2 /* NAVIGATION_GUARD_REDIRECT */)) {
3321
+ if (isNavigationFailure(failure, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) {
3181
3322
  if (// we are redirecting to the same location we were already at
3182
3323
  isSameRouteLocation(stringifyQuery$1, resolve(failure.to), toLocation) &&
3183
3324
  // and we have done it a couple of times
@@ -3192,10 +3333,14 @@ var KduRouter = (function (exports, kdu) {
3192
3333
  }
3193
3334
  return pushWithRedirect(
3194
3335
  // keep options
3195
- assign(locationAsObject(failure.to), {
3196
- state: data,
3197
- force,
3336
+ assign({
3337
+ // preserve an existing replacement but allow the redirect to override it
3198
3338
  replace,
3339
+ }, locationAsObject(failure.to), {
3340
+ state: typeof failure.to === 'object'
3341
+ ? assign({}, data, failure.to.state)
3342
+ : data,
3343
+ force,
3199
3344
  }),
3200
3345
  // preserve the original redirectedFrom if any
3201
3346
  redirectedFrom || toLocation);
@@ -3261,7 +3406,7 @@ var KduRouter = (function (exports, kdu) {
3261
3406
  for (const record of to.matched) {
3262
3407
  // do not trigger beforeEnter on reused views
3263
3408
  if (record.beforeEnter && !from.matched.includes(record)) {
3264
- if (Array.isArray(record.beforeEnter)) {
3409
+ if (isArray(record.beforeEnter)) {
3265
3410
  for (const beforeEnter of record.beforeEnter)
3266
3411
  guards.push(guardToPromiseFn(beforeEnter, to, from));
3267
3412
  }
@@ -3294,7 +3439,7 @@ var KduRouter = (function (exports, kdu) {
3294
3439
  return runGuardQueue(guards);
3295
3440
  })
3296
3441
  // catch any navigation canceled
3297
- .catch(err => isNavigationFailure(err, 8 /* NAVIGATION_CANCELLED */)
3442
+ .catch(err => isNavigationFailure(err, 8 /* ErrorTypes.NAVIGATION_CANCELLED */)
3298
3443
  ? err
3299
3444
  : Promise.reject(err)));
3300
3445
  }
@@ -3341,6 +3486,8 @@ var KduRouter = (function (exports, kdu) {
3341
3486
  if (removeHistoryListener)
3342
3487
  return;
3343
3488
  removeHistoryListener = routerHistory.listen((to, _from, info) => {
3489
+ if (!router.listening)
3490
+ return;
3344
3491
  // cannot be a redirect route because it was in history
3345
3492
  const toLocation = resolve(to);
3346
3493
  // due to dynamic routing, and to hash history with manual navigation
@@ -3359,15 +3506,15 @@ var KduRouter = (function (exports, kdu) {
3359
3506
  }
3360
3507
  navigate(toLocation, from)
3361
3508
  .catch((error) => {
3362
- if (isNavigationFailure(error, 4 /* NAVIGATION_ABORTED */ | 8 /* NAVIGATION_CANCELLED */)) {
3509
+ if (isNavigationFailure(error, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) {
3363
3510
  return error;
3364
3511
  }
3365
- if (isNavigationFailure(error, 2 /* NAVIGATION_GUARD_REDIRECT */)) {
3512
+ if (isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) {
3366
3513
  // Here we could call if (info.delta) routerHistory.go(-info.delta,
3367
3514
  // false) but this is bug prone as we have no way to wait the
3368
3515
  // navigation to be finished before calling pushWithRedirect. Using
3369
- // a setTimeout of 16ms seems to work but there is not guarantee for
3370
- // it to work on every browser. So Instead we do not restore the
3516
+ // a setTimeout of 16ms seems to work but there is no guarantee for
3517
+ // it to work on every browser. So instead we do not restore the
3371
3518
  // history entry and trigger a new navigation as requested by the
3372
3519
  // navigation guard.
3373
3520
  // the error is already handled by router.push we just want to avoid
@@ -3377,10 +3524,10 @@ var KduRouter = (function (exports, kdu) {
3377
3524
  )
3378
3525
  .then(failure => {
3379
3526
  // manual change in hash history #916 ending up in the URL not
3380
- // changing but it was changed by the manual url change, so we
3527
+ // changing, but it was changed by the manual url change, so we
3381
3528
  // need to manually change it ourselves
3382
- if (isNavigationFailure(failure, 4 /* NAVIGATION_ABORTED */ |
3383
- 16 /* NAVIGATION_DUPLICATED */) &&
3529
+ if (isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ |
3530
+ 16 /* ErrorTypes.NAVIGATION_DUPLICATED */) &&
3384
3531
  !info.delta &&
3385
3532
  info.type === NavigationType.pop) {
3386
3533
  routerHistory.go(-1, false);
@@ -3391,8 +3538,9 @@ var KduRouter = (function (exports, kdu) {
3391
3538
  return Promise.reject();
3392
3539
  }
3393
3540
  // do not restore history on unknown direction
3394
- if (info.delta)
3541
+ if (info.delta) {
3395
3542
  routerHistory.go(-info.delta, false);
3543
+ }
3396
3544
  // unrecognized error, transfer to the global handler
3397
3545
  return triggerError(error, toLocation, from);
3398
3546
  })
@@ -3404,11 +3552,14 @@ var KduRouter = (function (exports, kdu) {
3404
3552
  toLocation, from, false);
3405
3553
  // revert the navigation
3406
3554
  if (failure) {
3407
- if (info.delta) {
3555
+ if (info.delta &&
3556
+ // a new navigation has been triggered, so we do not want to revert, that will change the current history
3557
+ // entry while a different route is displayed
3558
+ !isNavigationFailure(failure, 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) {
3408
3559
  routerHistory.go(-info.delta, false);
3409
3560
  }
3410
3561
  else if (info.type === NavigationType.pop &&
3411
- isNavigationFailure(failure, 4 /* NAVIGATION_ABORTED */ | 16 /* NAVIGATION_DUPLICATED */)) {
3562
+ isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 16 /* ErrorTypes.NAVIGATION_DUPLICATED */)) {
3412
3563
  // manual change in hash history #916
3413
3564
  // it's like a push but lacks the information of the direction
3414
3565
  routerHistory.go(-1, false);
@@ -3484,6 +3635,7 @@ var KduRouter = (function (exports, kdu) {
3484
3635
  const installedApps = new Set();
3485
3636
  const router = {
3486
3637
  currentRoute,
3638
+ listening: true,
3487
3639
  addRoute,
3488
3640
  removeRoute,
3489
3641
  hasRoute,
@@ -3547,6 +3699,7 @@ var KduRouter = (function (exports, kdu) {
3547
3699
  }
3548
3700
  unmountApp();
3549
3701
  };
3702
+ // TODO: this probably needs to be updated so it can be used by kdu-termui
3550
3703
  if (isBrowser) {
3551
3704
  addDevtools(app, router, matcher);
3552
3705
  }
@@ -3605,6 +3758,7 @@ var KduRouter = (function (exports, kdu) {
3605
3758
  exports.createWebHashHistory = createWebHashHistory;
3606
3759
  exports.createWebHistory = createWebHistory;
3607
3760
  exports.isNavigationFailure = isNavigationFailure;
3761
+ exports.loadRouteLocation = loadRouteLocation;
3608
3762
  exports.matchedRouteKey = matchedRouteKey;
3609
3763
  exports.onBeforeRouteLeave = onBeforeRouteLeave;
3610
3764
  exports.onBeforeRouteUpdate = onBeforeRouteUpdate;