@tanstack/router-core 1.145.11 → 1.146.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
- "version": "1.145.11",
3
+ "version": "1.146.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -8,6 +8,7 @@ export const SEGMENT_TYPE_PARAM = 1
8
8
  export const SEGMENT_TYPE_WILDCARD = 2
9
9
  export const SEGMENT_TYPE_OPTIONAL_PARAM = 3
10
10
  const SEGMENT_TYPE_INDEX = 4
11
+ const SEGMENT_TYPE_PATHLESS = 5 // only used in matching to represent pathless routes that need to carry more information
11
12
 
12
13
  /**
13
14
  * All the kinds of segments that can be present in a route path.
@@ -21,7 +22,10 @@ export type SegmentKind =
21
22
  /**
22
23
  * All the kinds of segments that can be present in the segment tree.
23
24
  */
24
- type ExtendedSegmentKind = SegmentKind | typeof SEGMENT_TYPE_INDEX
25
+ type ExtendedSegmentKind =
26
+ | SegmentKind
27
+ | typeof SEGMENT_TYPE_INDEX
28
+ | typeof SEGMENT_TYPE_PATHLESS
25
29
 
26
30
  const PARAM_W_CURLY_BRACES_RE =
27
31
  /^([^{]*)\{\$([a-zA-Z_$][a-zA-Z0-9_$]*)\}([^}]*)$/ // prefix{$paramName}suffix
@@ -183,6 +187,10 @@ function parseSegments<TRouteLike extends RouteLike>(
183
187
  const path = route.fullPath ?? route.from
184
188
  const length = path.length
185
189
  const caseSensitive = route.options?.caseSensitive ?? defaultCaseSensitive
190
+ const skipOnParamError = !!(
191
+ route.options?.params?.parse &&
192
+ route.options?.skipRouteOnParseError?.params
193
+ )
186
194
  while (cursor < length) {
187
195
  const segment = parseSegment(path, cursor, data)
188
196
  let nextNode: AnySegmentNode<TRouteLike>
@@ -241,12 +249,15 @@ function parseSegments<TRouteLike extends RouteLike>(
241
249
  : actuallyCaseSensitive
242
250
  ? suffix_raw
243
251
  : suffix_raw.toLowerCase()
244
- const existingNode = node.dynamic?.find(
245
- (s) =>
246
- s.caseSensitive === actuallyCaseSensitive &&
247
- s.prefix === prefix &&
248
- s.suffix === suffix,
249
- )
252
+ const existingNode =
253
+ !skipOnParamError &&
254
+ node.dynamic?.find(
255
+ (s) =>
256
+ !s.skipOnParamError &&
257
+ s.caseSensitive === actuallyCaseSensitive &&
258
+ s.prefix === prefix &&
259
+ s.suffix === suffix,
260
+ )
250
261
  if (existingNode) {
251
262
  nextNode = existingNode
252
263
  } else {
@@ -280,12 +291,15 @@ function parseSegments<TRouteLike extends RouteLike>(
280
291
  : actuallyCaseSensitive
281
292
  ? suffix_raw
282
293
  : suffix_raw.toLowerCase()
283
- const existingNode = node.optional?.find(
284
- (s) =>
285
- s.caseSensitive === actuallyCaseSensitive &&
286
- s.prefix === prefix &&
287
- s.suffix === suffix,
288
- )
294
+ const existingNode =
295
+ !skipOnParamError &&
296
+ node.optional?.find(
297
+ (s) =>
298
+ !s.skipOnParamError &&
299
+ s.caseSensitive === actuallyCaseSensitive &&
300
+ s.prefix === prefix &&
301
+ s.suffix === suffix,
302
+ )
289
303
  if (existingNode) {
290
304
  nextNode = existingNode
291
305
  } else {
@@ -336,8 +350,27 @@ function parseSegments<TRouteLike extends RouteLike>(
336
350
  node = nextNode
337
351
  }
338
352
 
339
- const isLeaf = (route.path || !route.children) && !route.isRoot
353
+ // create pathless node
354
+ if (
355
+ skipOnParamError &&
356
+ route.children &&
357
+ !route.isRoot &&
358
+ route.id &&
359
+ route.id.charCodeAt(route.id.lastIndexOf('/') + 1) === 95 /* '_' */
360
+ ) {
361
+ const pathlessNode = createStaticNode<TRouteLike>(
362
+ route.fullPath ?? route.from,
363
+ )
364
+ pathlessNode.kind = SEGMENT_TYPE_PATHLESS
365
+ pathlessNode.parent = node
366
+ depth++
367
+ pathlessNode.depth = depth
368
+ node.pathless ??= []
369
+ node.pathless.push(pathlessNode)
370
+ node = pathlessNode
371
+ }
340
372
 
373
+ const isLeaf = (route.path || !route.children) && !route.isRoot
341
374
  // create index node
342
375
  if (isLeaf && path.endsWith('/')) {
343
376
  const indexNode = createStaticNode<TRouteLike>(
@@ -351,6 +384,10 @@ function parseSegments<TRouteLike extends RouteLike>(
351
384
  node = indexNode
352
385
  }
353
386
 
387
+ node.parse = route.options?.params?.parse ?? null
388
+ node.skipOnParamError = skipOnParamError
389
+ node.parsingPriority = route.options?.skipRouteOnParseError?.priority ?? 0
390
+
354
391
  // make node "matchable"
355
392
  if (isLeaf && !node.route) {
356
393
  node.route = route
@@ -372,9 +409,29 @@ function parseSegments<TRouteLike extends RouteLike>(
372
409
  }
373
410
 
374
411
  function sortDynamic(
375
- a: { prefix?: string; suffix?: string; caseSensitive: boolean },
376
- b: { prefix?: string; suffix?: string; caseSensitive: boolean },
412
+ a: {
413
+ prefix?: string
414
+ suffix?: string
415
+ caseSensitive: boolean
416
+ skipOnParamError: boolean
417
+ parsingPriority: number
418
+ },
419
+ b: {
420
+ prefix?: string
421
+ suffix?: string
422
+ caseSensitive: boolean
423
+ skipOnParamError: boolean
424
+ parsingPriority: number
425
+ },
377
426
  ) {
427
+ if (a.skipOnParamError && !b.skipOnParamError) return -1
428
+ if (!a.skipOnParamError && b.skipOnParamError) return 1
429
+ if (
430
+ a.skipOnParamError &&
431
+ b.skipOnParamError &&
432
+ (a.parsingPriority || b.parsingPriority)
433
+ )
434
+ return b.parsingPriority - a.parsingPriority
378
435
  if (a.prefix && b.prefix && a.prefix !== b.prefix) {
379
436
  if (a.prefix.startsWith(b.prefix)) return -1
380
437
  if (b.prefix.startsWith(a.prefix)) return 1
@@ -396,6 +453,11 @@ function sortDynamic(
396
453
  }
397
454
 
398
455
  function sortTreeNodes(node: SegmentNode<RouteLike>) {
456
+ if (node.pathless) {
457
+ for (const child of node.pathless) {
458
+ sortTreeNodes(child)
459
+ }
460
+ }
399
461
  if (node.static) {
400
462
  for (const child of node.static.values()) {
401
463
  sortTreeNodes(child)
@@ -432,6 +494,7 @@ function createStaticNode<T extends RouteLike>(
432
494
  return {
433
495
  kind: SEGMENT_TYPE_PATHNAME,
434
496
  depth: 0,
497
+ pathless: null,
435
498
  index: null,
436
499
  static: null,
437
500
  staticInsensitive: null,
@@ -441,6 +504,9 @@ function createStaticNode<T extends RouteLike>(
441
504
  route: null,
442
505
  fullPath,
443
506
  parent: null,
507
+ parse: null,
508
+ skipOnParamError: false,
509
+ parsingPriority: 0,
444
510
  }
445
511
  }
446
512
 
@@ -461,6 +527,7 @@ function createDynamicNode<T extends RouteLike>(
461
527
  return {
462
528
  kind,
463
529
  depth: 0,
530
+ pathless: null,
464
531
  index: null,
465
532
  static: null,
466
533
  staticInsensitive: null,
@@ -470,6 +537,9 @@ function createDynamicNode<T extends RouteLike>(
470
537
  route: null,
471
538
  fullPath,
472
539
  parent: null,
540
+ parse: null,
541
+ skipOnParamError: false,
542
+ parsingPriority: 0,
473
543
  caseSensitive,
474
544
  prefix,
475
545
  suffix,
@@ -477,7 +547,10 @@ function createDynamicNode<T extends RouteLike>(
477
547
  }
478
548
 
479
549
  type StaticSegmentNode<T extends RouteLike> = SegmentNode<T> & {
480
- kind: typeof SEGMENT_TYPE_PATHNAME | typeof SEGMENT_TYPE_INDEX
550
+ kind:
551
+ | typeof SEGMENT_TYPE_PATHNAME
552
+ | typeof SEGMENT_TYPE_PATHLESS
553
+ | typeof SEGMENT_TYPE_INDEX
481
554
  }
482
555
 
483
556
  type DynamicSegmentNode<T extends RouteLike> = SegmentNode<T> & {
@@ -497,6 +570,8 @@ type AnySegmentNode<T extends RouteLike> =
497
570
  type SegmentNode<T extends RouteLike> = {
498
571
  kind: ExtendedSegmentKind
499
572
 
573
+ pathless: Array<StaticSegmentNode<T>> | null
574
+
500
575
  /** Exact index segment (highest priority) */
501
576
  index: StaticSegmentNode<T> | null
502
577
 
@@ -524,15 +599,32 @@ type SegmentNode<T extends RouteLike> = {
524
599
  parent: AnySegmentNode<T> | null
525
600
 
526
601
  depth: number
602
+
603
+ /** route.options.params.parse function, set on the last node of the route */
604
+ parse: null | ((params: Record<string, string>) => any)
605
+
606
+ /** options.skipRouteOnParseError.params ?? false */
607
+ skipOnParamError: boolean
608
+
609
+ /** options.skipRouteOnParseError.priority ?? 0 */
610
+ parsingPriority: number
527
611
  }
528
612
 
529
613
  type RouteLike = {
614
+ id?: string
530
615
  path?: string // relative path from the parent,
531
616
  children?: Array<RouteLike> // child routes,
532
617
  parentRoute?: RouteLike // parent route,
533
618
  isRoot?: boolean
534
619
  options?: {
620
+ skipRouteOnParseError?: {
621
+ params?: boolean
622
+ priority?: number
623
+ }
535
624
  caseSensitive?: boolean
625
+ params?: {
626
+ parse?: (params: Record<string, string>) => any
627
+ }
536
628
  }
537
629
  } &
538
630
  // router tree
@@ -621,7 +713,8 @@ export function findSingleMatch(
621
713
 
622
714
  type RouteMatch<T extends Extract<RouteLike, { fullPath: string }>> = {
623
715
  route: T
624
- params: Record<string, string>
716
+ rawParams: Record<string, string>
717
+ parsedParams?: Record<string, unknown>
625
718
  branch: ReadonlyArray<T>
626
719
  }
627
720
 
@@ -718,32 +811,57 @@ function findMatch<T extends RouteLike>(
718
811
  path: string,
719
812
  segmentTree: AnySegmentNode<T>,
720
813
  fuzzy = false,
721
- ): { route: T; params: Record<string, string> } | null {
814
+ ): {
815
+ route: T
816
+ /**
817
+ * The raw (unparsed) params extracted from the path.
818
+ * This will be the exhaustive list of all params defined in the route's path.
819
+ */
820
+ rawParams: Record<string, string>
821
+ /**
822
+ * The accumlulated parsed params of each route in the branch that had `skipRouteOnParseError` enabled.
823
+ * Will not contain all params defined in the route's path. Those w/ a `params.parse` but no `skipRouteOnParseError` will need to be parsed separately.
824
+ */
825
+ parsedParams?: Record<string, unknown>
826
+ } | null {
722
827
  const parts = path.split('/')
723
828
  const leaf = getNodeMatch(path, parts, segmentTree, fuzzy)
724
829
  if (!leaf) return null
725
- const params = extractParams(path, parts, leaf)
726
- if ('**' in leaf) params['**'] = leaf['**']!
727
- const route = leaf.node.route!
830
+ const [rawParams] = extractParams(path, parts, leaf)
728
831
  return {
729
- route,
730
- params,
832
+ route: leaf.node.route!,
833
+ rawParams,
834
+ parsedParams: leaf.parsedParams,
731
835
  }
732
836
  }
733
837
 
838
+ /**
839
+ * This function is "resumable":
840
+ * - the `leaf` input can contain `extract` and `rawParams` properties from a previous `extractParams` call
841
+ * - the returned `state` can be passed back as `extract` in a future call to continue extracting params from where we left off
842
+ *
843
+ * Inputs are *not* mutated.
844
+ */
734
845
  function extractParams<T extends RouteLike>(
735
846
  path: string,
736
847
  parts: Array<string>,
737
- leaf: { node: AnySegmentNode<T>; skipped: number },
738
- ) {
848
+ leaf: {
849
+ node: AnySegmentNode<T>
850
+ skipped: number
851
+ extract?: { part: number; node: number; path: number }
852
+ rawParams?: Record<string, string>
853
+ },
854
+ ): [
855
+ rawParams: Record<string, string>,
856
+ state: { part: number; node: number; path: number },
857
+ ] {
739
858
  const list = buildBranch(leaf.node)
740
859
  let nodeParts: Array<string> | null = null
741
- const params: Record<string, string> = {}
742
- for (
743
- let partIndex = 0, nodeIndex = 0, pathIndex = 0;
744
- nodeIndex < list.length;
745
- partIndex++, nodeIndex++, pathIndex++
746
- ) {
860
+ const rawParams: Record<string, string> = {}
861
+ let partIndex = leaf.extract?.part ?? 0
862
+ let nodeIndex = leaf.extract?.node ?? 0
863
+ let pathIndex = leaf.extract?.path ?? 0
864
+ for (; nodeIndex < list.length; partIndex++, nodeIndex++, pathIndex++) {
747
865
  const node = list[nodeIndex]!
748
866
  const part = parts[partIndex]
749
867
  const currentPathIndex = pathIndex
@@ -762,10 +880,10 @@ function extractParams<T extends RouteLike>(
762
880
  nodePart.length - sufLength - 1,
763
881
  )
764
882
  const value = part!.substring(preLength, part!.length - sufLength)
765
- params[name] = decodeURIComponent(value)
883
+ rawParams[name] = decodeURIComponent(value)
766
884
  } else {
767
885
  const name = nodePart.substring(1)
768
- params[name] = decodeURIComponent(part!)
886
+ rawParams[name] = decodeURIComponent(part!)
769
887
  }
770
888
  } else if (node.kind === SEGMENT_TYPE_OPTIONAL_PARAM) {
771
889
  if (leaf.skipped & (1 << nodeIndex)) {
@@ -784,7 +902,7 @@ function extractParams<T extends RouteLike>(
784
902
  node.suffix || node.prefix
785
903
  ? part!.substring(preLength, part!.length - sufLength)
786
904
  : part
787
- if (value) params[name] = decodeURIComponent(value)
905
+ if (value) rawParams[name] = decodeURIComponent(value)
788
906
  } else if (node.kind === SEGMENT_TYPE_WILDCARD) {
789
907
  const n = node
790
908
  const value = path.substring(
@@ -793,12 +911,13 @@ function extractParams<T extends RouteLike>(
793
911
  )
794
912
  const splat = decodeURIComponent(value)
795
913
  // TODO: Deprecate *
796
- params['*'] = splat
797
- params._splat = splat
914
+ rawParams['*'] = splat
915
+ rawParams._splat = splat
798
916
  break
799
917
  }
800
918
  }
801
- return params
919
+ if (leaf.rawParams) Object.assign(rawParams, leaf.rawParams)
920
+ return [rawParams, { part: partIndex, node: nodeIndex, path: pathIndex }]
802
921
  }
803
922
 
804
923
  function buildRouteBranch<T extends RouteLike>(route: T) {
@@ -836,6 +955,11 @@ type MatchStackFrame<T extends RouteLike> = {
836
955
  statics: number
837
956
  dynamics: number
838
957
  optionals: number
958
+ /** intermediary state for param extraction */
959
+ extract?: { part: number; node: number; path: number }
960
+ /** intermediary params from param extraction */
961
+ rawParams?: Record<string, string>
962
+ parsedParams?: Record<string, unknown>
839
963
  }
840
964
 
841
965
  function getNodeMatch<T extends RouteLike>(
@@ -847,7 +971,10 @@ function getNodeMatch<T extends RouteLike>(
847
971
  // quick check for root index
848
972
  // this is an optimization, algorithm should work correctly without this block
849
973
  if (path === '/' && segmentTree.index)
850
- return { node: segmentTree.index, skipped: 0 }
974
+ return { node: segmentTree.index, skipped: 0 } as Pick<
975
+ Frame,
976
+ 'node' | 'skipped' | 'parsedParams'
977
+ >
851
978
 
852
979
  const trailingSlash = !last(parts)
853
980
  const pathIsIndex = trailingSlash && path !== '/'
@@ -880,8 +1007,16 @@ function getNodeMatch<T extends RouteLike>(
880
1007
 
881
1008
  while (stack.length) {
882
1009
  const frame = stack.pop()!
883
- // eslint-disable-next-line prefer-const
884
- let { node, index, skipped, depth, statics, dynamics, optionals } = frame
1010
+ const { node, index, skipped, depth, statics, dynamics, optionals } = frame
1011
+ let { extract, rawParams, parsedParams } = frame
1012
+
1013
+ if (node.skipOnParamError) {
1014
+ const result = validateMatchParams(path, parts, frame)
1015
+ if (!result) continue
1016
+ rawParams = frame.rawParams
1017
+ extract = frame.extract
1018
+ parsedParams = frame.parsedParams
1019
+ }
885
1020
 
886
1021
  // In fuzzy mode, track the best partial match we've found so far
887
1022
  if (
@@ -898,8 +1033,9 @@ function getNodeMatch<T extends RouteLike>(
898
1033
  if (node.route && !pathIsIndex && isFrameMoreSpecific(bestMatch, frame)) {
899
1034
  bestMatch = frame
900
1035
  }
901
- // beyond the length of the path parts, only index segments, or skipped optional segments, or wildcard segments can match
902
- if (!node.optional && !node.wildcard && !node.index) continue
1036
+ // beyond the length of the path parts, only some segment types can match
1037
+ if (!node.optional && !node.wildcard && !node.index && !node.pathless)
1038
+ continue
903
1039
  }
904
1040
 
905
1041
  const part = isBeyondPath ? undefined : parts[index]!
@@ -915,6 +1051,13 @@ function getNodeMatch<T extends RouteLike>(
915
1051
  statics,
916
1052
  dynamics,
917
1053
  optionals,
1054
+ extract,
1055
+ rawParams,
1056
+ parsedParams,
1057
+ }
1058
+ if (node.index.skipOnParamError) {
1059
+ const result = validateMatchParams(path, parts, indexFrame)
1060
+ if (!result) continue
918
1061
  }
919
1062
  // perfect match, no need to continue
920
1063
  // this is an optimization, algorithm should work correctly without this block
@@ -946,7 +1089,7 @@ function getNodeMatch<T extends RouteLike>(
946
1089
  }
947
1090
  // the first wildcard match is the highest priority one
948
1091
  // wildcard matches skip the stack because they cannot have children
949
- wildcardMatch = {
1092
+ const frame = {
950
1093
  node: segment,
951
1094
  index: partsLength,
952
1095
  skipped,
@@ -954,7 +1097,15 @@ function getNodeMatch<T extends RouteLike>(
954
1097
  statics,
955
1098
  dynamics,
956
1099
  optionals,
1100
+ extract,
1101
+ rawParams,
1102
+ parsedParams,
957
1103
  }
1104
+ if (segment.skipOnParamError) {
1105
+ const result = validateMatchParams(path, parts, frame)
1106
+ if (!result) continue
1107
+ }
1108
+ wildcardMatch = frame
958
1109
  break
959
1110
  }
960
1111
  }
@@ -974,6 +1125,9 @@ function getNodeMatch<T extends RouteLike>(
974
1125
  statics,
975
1126
  dynamics,
976
1127
  optionals,
1128
+ extract,
1129
+ rawParams,
1130
+ parsedParams,
977
1131
  }) // enqueue skipping the optional
978
1132
  }
979
1133
  if (!isBeyondPath) {
@@ -995,6 +1149,9 @@ function getNodeMatch<T extends RouteLike>(
995
1149
  statics,
996
1150
  dynamics,
997
1151
  optionals: optionals + 1,
1152
+ extract,
1153
+ rawParams,
1154
+ parsedParams,
998
1155
  })
999
1156
  }
1000
1157
  }
@@ -1020,6 +1177,9 @@ function getNodeMatch<T extends RouteLike>(
1020
1177
  statics,
1021
1178
  dynamics: dynamics + 1,
1022
1179
  optionals,
1180
+ extract,
1181
+ rawParams,
1182
+ parsedParams,
1023
1183
  })
1024
1184
  }
1025
1185
  }
@@ -1038,6 +1198,9 @@ function getNodeMatch<T extends RouteLike>(
1038
1198
  statics: statics + 1,
1039
1199
  dynamics,
1040
1200
  optionals,
1201
+ extract,
1202
+ rawParams,
1203
+ parsedParams,
1041
1204
  })
1042
1205
  }
1043
1206
  }
@@ -1054,6 +1217,29 @@ function getNodeMatch<T extends RouteLike>(
1054
1217
  statics: statics + 1,
1055
1218
  dynamics,
1056
1219
  optionals,
1220
+ extract,
1221
+ rawParams,
1222
+ parsedParams,
1223
+ })
1224
+ }
1225
+ }
1226
+
1227
+ // 0. Try pathless match
1228
+ if (node.pathless) {
1229
+ const nextDepth = depth + 1
1230
+ for (let i = node.pathless.length - 1; i >= 0; i--) {
1231
+ const segment = node.pathless[i]!
1232
+ stack.push({
1233
+ node: segment,
1234
+ index,
1235
+ skipped,
1236
+ depth: nextDepth,
1237
+ statics,
1238
+ dynamics,
1239
+ optionals,
1240
+ extract,
1241
+ rawParams,
1242
+ parsedParams,
1057
1243
  })
1058
1244
  }
1059
1245
  }
@@ -1075,16 +1261,31 @@ function getNodeMatch<T extends RouteLike>(
1075
1261
  sliceIndex += parts[i]!.length
1076
1262
  }
1077
1263
  const splat = sliceIndex === path.length ? '/' : path.slice(sliceIndex)
1078
- return {
1079
- node: bestFuzzy.node,
1080
- skipped: bestFuzzy.skipped,
1081
- '**': decodeURIComponent(splat),
1082
- }
1264
+ bestFuzzy.rawParams ??= {}
1265
+ bestFuzzy.rawParams['**'] = decodeURIComponent(splat)
1266
+ return bestFuzzy
1083
1267
  }
1084
1268
 
1085
1269
  return null
1086
1270
  }
1087
1271
 
1272
+ function validateMatchParams<T extends RouteLike>(
1273
+ path: string,
1274
+ parts: Array<string>,
1275
+ frame: MatchStackFrame<T>,
1276
+ ) {
1277
+ try {
1278
+ const [rawParams, state] = extractParams(path, parts, frame)
1279
+ frame.rawParams = rawParams
1280
+ frame.extract = state
1281
+ const parsed = frame.node.parse!(rawParams)
1282
+ frame.parsedParams = Object.assign({}, frame.parsedParams, parsed)
1283
+ return true
1284
+ } catch {
1285
+ return null
1286
+ }
1287
+ }
1288
+
1088
1289
  function isFrameMoreSpecific(
1089
1290
  // the stack frame previously saved as "best match"
1090
1291
  prev: MatchStackFrame<any> | null,
package/src/route.ts CHANGED
@@ -1188,9 +1188,46 @@ export interface UpdatableRouteOptions<
1188
1188
  in out TBeforeLoadFn,
1189
1189
  > extends UpdatableStaticRouteOption,
1190
1190
  UpdatableRouteOptionsExtensions {
1191
- // If true, this route will be matched as case-sensitive
1191
+ /**
1192
+ * Options to control route matching behavior with runtime code.
1193
+ *
1194
+ * @experimental 🚧 this feature is subject to change
1195
+ *
1196
+ * @link https://tanstack.com/router/latest/docs/framework/react/api/router/RouteOptionsType
1197
+ */
1198
+ skipRouteOnParseError?: {
1199
+ /**
1200
+ * If `true`, skip this route during matching if `params.parse` fails.
1201
+ *
1202
+ * Without this option, a `/$param` route could match *any* value for `param`,
1203
+ * and only later during the route lifecycle would `params.parse` run and potentially
1204
+ * show the `errorComponent` if validation failed.
1205
+ *
1206
+ * With this option enabled, the route will only match if `params.parse` succeeds.
1207
+ * If it fails, the router will continue trying to match other routes, potentially
1208
+ * finding a different route that works, or ultimately showing the `notFoundComponent`.
1209
+ *
1210
+ * @default false
1211
+ */
1212
+ params?: boolean
1213
+ /**
1214
+ * In cases where multiple routes would need to run `params.parse` during matching
1215
+ * to determine which route to pick, this priority number can be used as a tie-breaker
1216
+ * for which route to try first. Higher number = higher priority.
1217
+ *
1218
+ * @default 0
1219
+ */
1220
+ priority?: number
1221
+ }
1222
+ /**
1223
+ * If true, this route will be matched as case-sensitive
1224
+ *
1225
+ * @default false
1226
+ */
1192
1227
  caseSensitive?: boolean
1193
- // If true, this route will be forcefully wrapped in a suspense boundary
1228
+ /**
1229
+ * If true, this route will be forcefully wrapped in a suspense boundary
1230
+ */
1194
1231
  wrapInSuspense?: boolean
1195
1232
  // The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />`
1196
1233