@tanstack/react-router 1.81.4 → 1.81.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.
package/src/router.ts CHANGED
@@ -42,8 +42,6 @@ import type {
42
42
  AnyContext,
43
43
  AnyRoute,
44
44
  AnyRouteWithContext,
45
- AnySearchSchema,
46
- AnySearchValidator,
47
45
  BeforeLoadContextOptions,
48
46
  ErrorRouteComponent,
49
47
  LoaderFnContext,
@@ -84,6 +82,7 @@ import type { AnyRedirect, ResolvedRedirect } from './redirects'
84
82
  import type { NotFoundError } from './not-found'
85
83
  import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
86
84
  import type { RouterTransformer } from './transformer'
85
+ import type { AnySchema, AnyValidator } from './validators'
87
86
 
88
87
  declare global {
89
88
  interface Window {
@@ -579,10 +578,7 @@ function routeNeedsPreload(route: AnyRoute) {
579
578
  return false
580
579
  }
581
580
 
582
- function validateSearch(
583
- validateSearch: AnySearchValidator,
584
- input: unknown,
585
- ): unknown {
581
+ function validateSearch(validateSearch: AnyValidator, input: unknown): unknown {
586
582
  if (validateSearch == null) return {}
587
583
 
588
584
  if ('~standard' in validateSearch) {
@@ -1075,7 +1071,7 @@ export class Router<
1075
1071
  */
1076
1072
  public matchRoutes(
1077
1073
  pathname: string,
1078
- locationSearch: AnySearchSchema,
1074
+ locationSearch: AnySchema,
1079
1075
  opts?: MatchRoutesOpts,
1080
1076
  ): Array<AnyRouteMatch>
1081
1077
  public matchRoutes(
@@ -1085,7 +1081,7 @@ export class Router<
1085
1081
 
1086
1082
  public matchRoutes(
1087
1083
  pathnameOrNext: string | ParsedLocation,
1088
- locationSearchOrOpts?: AnySearchSchema | MatchRoutesOpts,
1084
+ locationSearchOrOpts?: AnySchema | MatchRoutesOpts,
1089
1085
  opts?: MatchRoutesOpts,
1090
1086
  ) {
1091
1087
  if (typeof pathnameOrNext === 'string') {
@@ -1514,8 +1510,8 @@ export class Router<
1514
1510
  })
1515
1511
 
1516
1512
  let search = fromSearch
1517
- if (opts._includeValidateSearch) {
1518
- let validatedSearch = this.options.search?.strict ? {} : search
1513
+ if (opts._includeValidateSearch && this.options.search?.strict) {
1514
+ let validatedSearch = {}
1519
1515
  matchedRoutesResult?.matchedRoutes.forEach((route) => {
1520
1516
  try {
1521
1517
  if (route.options.validateSearch) {
@@ -1538,10 +1534,10 @@ export class Router<
1538
1534
  const allMiddlewares =
1539
1535
  matchedRoutesResult?.matchedRoutes.reduce(
1540
1536
  (acc, route) => {
1541
- let middlewares: Array<SearchMiddleware<any>> = []
1537
+ const middlewares: Array<SearchMiddleware<any>> = []
1542
1538
  if ('search' in route.options) {
1543
1539
  if (route.options.search?.middlewares) {
1544
- middlewares = route.options.search.middlewares
1540
+ middlewares.push(...route.options.search.middlewares)
1545
1541
  }
1546
1542
  }
1547
1543
  // TODO remove preSearchFilters and postSearchFilters in v2
@@ -1575,7 +1571,25 @@ export class Router<
1575
1571
  }
1576
1572
  return result
1577
1573
  }
1578
- middlewares = [legacyMiddleware]
1574
+ middlewares.push(legacyMiddleware)
1575
+ }
1576
+ if (opts._includeValidateSearch && route.options.validateSearch) {
1577
+ const validate: SearchMiddleware<any> = ({ search, next }) => {
1578
+ try {
1579
+ const result = next(search)
1580
+ const validatedSearch = {
1581
+ ...result,
1582
+ ...(validateSearch(
1583
+ route.options.validateSearch,
1584
+ result,
1585
+ ) ?? {}),
1586
+ }
1587
+ return validatedSearch
1588
+ } catch (e) {
1589
+ // ignore errors here because they are already handled in matchRoutes
1590
+ }
1591
+ }
1592
+ middlewares.push(validate)
1579
1593
  }
1580
1594
  return acc.concat(middlewares)
1581
1595
  },
@@ -2542,6 +2556,10 @@ export class Router<
2542
2556
  // to be preloaded before we resolve the match
2543
2557
  await route._componentsPromise
2544
2558
  } catch (err) {
2559
+ updateMatch(matchId, (prev) => ({
2560
+ ...prev,
2561
+ loaderPromise: undefined,
2562
+ }))
2545
2563
  handleRedirectAndNotFound(this.getMatch(matchId)!, err)
2546
2564
  }
2547
2565
  }
@@ -1,5 +1,5 @@
1
1
  import { decode, encode } from './qss'
2
- import type { AnySearchSchema } from './route'
2
+ import type { AnySchema } from './validators'
3
3
 
4
4
  export const defaultParseSearch = parseSearchWith(JSON.parse)
5
5
  export const defaultStringifySearch = stringifySearchWith(
@@ -8,7 +8,7 @@ export const defaultStringifySearch = stringifySearchWith(
8
8
  )
9
9
 
10
10
  export function parseSearchWith(parser: (str: string) => any) {
11
- return (searchStr: string): AnySearchSchema => {
11
+ return (searchStr: string): AnySchema => {
12
12
  if (searchStr.substring(0, 1) === '?') {
13
13
  searchStr = searchStr.substring(1)
14
14
  }
package/src/utils.ts CHANGED
@@ -72,12 +72,12 @@ export type NonNullableUpdater<TPrevious, TResult = TPrevious> =
72
72
  | TResult
73
73
  | ((prev: TPrevious) => TResult)
74
74
 
75
- export type MergeUnionObjects<TUnion> = TUnion extends MergeUnionPrimitive
75
+ export type ExtractObjects<TUnion> = TUnion extends MergeAllPrimitive
76
76
  ? never
77
77
  : TUnion
78
78
 
79
- export type MergeUnionObject<TUnion> =
80
- MergeUnionObjects<TUnion> extends infer TObj
79
+ export type PartialMergeAllObject<TUnion> =
80
+ ExtractObjects<TUnion> extends infer TObj
81
81
  ? {
82
82
  [TKey in TObj extends any ? keyof TObj : never]?: TObj extends any
83
83
  ? TKey extends keyof TObj
@@ -87,23 +87,53 @@ export type MergeUnionObject<TUnion> =
87
87
  }
88
88
  : never
89
89
 
90
- export type MergeUnionPrimitive =
90
+ export type MergeAllPrimitive =
91
91
  | ReadonlyArray<any>
92
92
  | number
93
93
  | string
94
94
  | bigint
95
95
  | boolean
96
96
  | symbol
97
+ | undefined
98
+ | null
97
99
 
98
- export type MergeUnionPrimitives<TUnion> = TUnion extends MergeUnionPrimitive
100
+ export type ExtractPrimitives<TUnion> = TUnion extends MergeAllPrimitive
99
101
  ? TUnion
100
102
  : TUnion extends object
101
103
  ? never
102
104
  : TUnion
103
105
 
104
- export type MergeUnion<TUnion> =
105
- | MergeUnionPrimitives<TUnion>
106
- | MergeUnionObject<TUnion>
106
+ export type PartialMergeAll<TUnion> =
107
+ | ExtractPrimitives<TUnion>
108
+ | PartialMergeAllObject<TUnion>
109
+
110
+ /**
111
+ * To be added to router types
112
+ */
113
+ export type UnionToIntersection<T> = (
114
+ T extends any ? (arg: T) => any : never
115
+ ) extends (arg: infer T) => any
116
+ ? T
117
+ : never
118
+
119
+ /**
120
+ * Merges everything in a union into one object.
121
+ * This mapped type is homomorphic which means it preserves stuff! :)
122
+ */
123
+ export type MergeAllObjects<
124
+ TUnion,
125
+ TIntersected = UnionToIntersection<ExtractObjects<TUnion>>,
126
+ > = [keyof TIntersected] extends [never]
127
+ ? never
128
+ : {
129
+ [TKey in keyof TIntersected]: TUnion extends any
130
+ ? TUnion[TKey & keyof TUnion]
131
+ : never
132
+ }
133
+
134
+ export type MergeAll<TUnion> =
135
+ | MergeAllObjects<TUnion>
136
+ | ExtractPrimitives<TUnion>
107
137
 
108
138
  export type Constrain<T, TConstraint, TDefault = TConstraint> =
109
139
  | (T extends TConstraint ? T : never)
@@ -0,0 +1,106 @@
1
+ import type { SearchSchemaInput } from './route'
2
+
3
+ export interface StandardSchemaValidatorProps<TInput, TOutput> {
4
+ readonly types?: StandardSchemaValidatorTypes<TInput, TOutput> | undefined
5
+ readonly validate: AnyStandardSchemaValidate
6
+ }
7
+
8
+ export interface StandardSchemaValidator<TInput, TOutput> {
9
+ readonly '~standard': StandardSchemaValidatorProps<TInput, TOutput>
10
+ }
11
+
12
+ export type AnyStandardSchemaValidator = StandardSchemaValidator<any, any>
13
+
14
+ export interface StandardSchemaValidatorTypes<TInput, TOutput> {
15
+ readonly input: TInput
16
+ readonly output: TOutput
17
+ }
18
+
19
+ export interface AnyStandardSchemaValidateSuccess {
20
+ readonly value: any
21
+ readonly issues?: undefined
22
+ }
23
+
24
+ export interface AnyStandardSchemaValidateFailure {
25
+ readonly issues: ReadonlyArray<AnyStandardSchemaValidateIssue>
26
+ }
27
+
28
+ export interface AnyStandardSchemaValidateIssue {
29
+ readonly message: string
30
+ }
31
+
32
+ export interface AnyStandardSchemaValidateInput {
33
+ readonly value: any
34
+ }
35
+
36
+ export type AnyStandardSchemaValidate = (
37
+ value: unknown,
38
+ ) =>
39
+ | (AnyStandardSchemaValidateSuccess | AnyStandardSchemaValidateFailure)
40
+ | Promise<AnyStandardSchemaValidateSuccess | AnyStandardSchemaValidateFailure>
41
+
42
+ export interface ValidatorObj<TInput, TOutput> {
43
+ parse: ValidatorFn<TInput, TOutput>
44
+ }
45
+
46
+ export type AnyValidatorObj = ValidatorObj<any, any>
47
+
48
+ export interface ValidatorAdapter<TInput, TOutput> {
49
+ types: {
50
+ input: TInput
51
+ output: TOutput
52
+ }
53
+ parse: (input: unknown) => TOutput
54
+ }
55
+
56
+ export type AnyValidatorAdapter = ValidatorAdapter<any, any>
57
+
58
+ export type AnyValidatorFn = ValidatorFn<any, any>
59
+
60
+ export type ValidatorFn<TInput, TOutput> = (input: TInput) => TOutput
61
+
62
+ export type Validator<TInput, TOutput> =
63
+ | ValidatorObj<TInput, TOutput>
64
+ | ValidatorFn<TInput, TOutput>
65
+ | ValidatorAdapter<TInput, TOutput>
66
+ | StandardSchemaValidator<TInput, TOutput>
67
+ | undefined
68
+
69
+ export type AnyValidator = Validator<any, any>
70
+
71
+ export type AnySchema = {}
72
+
73
+ export type DefaultValidator = Validator<Record<string, unknown>, AnySchema>
74
+
75
+ export type ResolveValidatorInputFn<TValidator> = TValidator extends (
76
+ input: infer TSchemaInput,
77
+ ) => any
78
+ ? TSchemaInput extends SearchSchemaInput
79
+ ? Omit<TSchemaInput, keyof SearchSchemaInput>
80
+ : ResolveValidatorOutputFn<TValidator>
81
+ : AnySchema
82
+
83
+ export type ResolveValidatorInput<TValidator> =
84
+ TValidator extends AnyStandardSchemaValidator
85
+ ? NonNullable<TValidator['~standard']['types']>['input']
86
+ : TValidator extends AnyValidatorAdapter
87
+ ? TValidator['types']['input']
88
+ : TValidator extends AnyValidatorObj
89
+ ? ResolveValidatorInputFn<TValidator['parse']>
90
+ : ResolveValidatorInputFn<TValidator>
91
+
92
+ export type ResolveValidatorOutputFn<TValidator> = TValidator extends (
93
+ ...args: any
94
+ ) => infer TSchema
95
+ ? TSchema
96
+ : AnySchema
97
+
98
+ export type ResolveValidatorOutput<TValidator> = unknown extends TValidator
99
+ ? TValidator
100
+ : TValidator extends AnyStandardSchemaValidator
101
+ ? NonNullable<TValidator['~standard']['types']>['output']
102
+ : TValidator extends AnyValidatorAdapter
103
+ ? TValidator['types']['output']
104
+ : TValidator extends AnyValidatorObj
105
+ ? ResolveValidatorOutputFn<TValidator['parse']>
106
+ : ResolveValidatorOutputFn<TValidator>