@tanstack/router-core 1.132.0-alpha.1 → 1.132.0-alpha.15

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.
Files changed (145) hide show
  1. package/dist/cjs/Matches.cjs.map +1 -1
  2. package/dist/cjs/Matches.d.cts +9 -11
  3. package/dist/cjs/config.cjs +10 -0
  4. package/dist/cjs/config.cjs.map +1 -0
  5. package/dist/cjs/config.d.cts +17 -0
  6. package/dist/cjs/fileRoute.d.cts +3 -2
  7. package/dist/cjs/index.cjs +15 -3
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/index.d.cts +11 -4
  10. package/dist/cjs/load-matches.cjs +636 -0
  11. package/dist/cjs/load-matches.cjs.map +1 -0
  12. package/dist/cjs/load-matches.d.cts +16 -0
  13. package/dist/cjs/location.d.cts +38 -0
  14. package/dist/cjs/path.cjs +6 -49
  15. package/dist/cjs/path.cjs.map +1 -1
  16. package/dist/cjs/path.d.cts +3 -6
  17. package/dist/cjs/qss.cjs +19 -19
  18. package/dist/cjs/qss.cjs.map +1 -1
  19. package/dist/cjs/qss.d.cts +6 -4
  20. package/dist/cjs/redirect.cjs +3 -3
  21. package/dist/cjs/redirect.cjs.map +1 -1
  22. package/dist/cjs/rewrite.cjs +63 -0
  23. package/dist/cjs/rewrite.cjs.map +1 -0
  24. package/dist/cjs/rewrite.d.cts +22 -0
  25. package/dist/cjs/route.cjs.map +1 -1
  26. package/dist/cjs/route.d.cts +42 -41
  27. package/dist/cjs/router.cjs +134 -681
  28. package/dist/cjs/router.cjs.map +1 -1
  29. package/dist/cjs/router.d.cts +68 -25
  30. package/dist/cjs/scroll-restoration.cjs +32 -29
  31. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  32. package/dist/cjs/scroll-restoration.d.cts +1 -10
  33. package/dist/cjs/searchParams.cjs +7 -15
  34. package/dist/cjs/searchParams.cjs.map +1 -1
  35. package/dist/cjs/ssr/constants.cjs +5 -0
  36. package/dist/cjs/ssr/constants.cjs.map +1 -0
  37. package/dist/cjs/ssr/constants.d.cts +1 -0
  38. package/dist/cjs/ssr/{seroval-plugins.cjs → serializer/ShallowErrorPlugin.cjs} +2 -2
  39. package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -0
  40. package/dist/cjs/ssr/{seroval-plugins.d.cts → serializer/ShallowErrorPlugin.d.cts} +1 -2
  41. package/dist/cjs/ssr/serializer/seroval-plugins.cjs +11 -0
  42. package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -0
  43. package/dist/cjs/ssr/serializer/seroval-plugins.d.cts +2 -0
  44. package/dist/cjs/ssr/serializer/transformer.cjs +52 -0
  45. package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -0
  46. package/dist/cjs/ssr/serializer/transformer.d.cts +56 -0
  47. package/dist/cjs/ssr/server.d.cts +5 -0
  48. package/dist/cjs/ssr/ssr-client.cjs +53 -40
  49. package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
  50. package/dist/cjs/ssr/ssr-client.d.cts +5 -1
  51. package/dist/cjs/ssr/ssr-server.cjs +12 -10
  52. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  53. package/dist/cjs/ssr/ssr-server.d.cts +0 -1
  54. package/dist/cjs/ssr/tsrScript.cjs +1 -1
  55. package/dist/cjs/ssr/tsrScript.cjs.map +1 -1
  56. package/dist/cjs/typePrimitives.d.cts +6 -6
  57. package/dist/cjs/utils.cjs +14 -7
  58. package/dist/cjs/utils.cjs.map +1 -1
  59. package/dist/cjs/utils.d.cts +2 -1
  60. package/dist/esm/Matches.d.ts +9 -11
  61. package/dist/esm/Matches.js.map +1 -1
  62. package/dist/esm/config.d.ts +17 -0
  63. package/dist/esm/config.js +10 -0
  64. package/dist/esm/config.js.map +1 -0
  65. package/dist/esm/fileRoute.d.ts +3 -2
  66. package/dist/esm/index.d.ts +11 -4
  67. package/dist/esm/index.js +17 -5
  68. package/dist/esm/index.js.map +1 -1
  69. package/dist/esm/load-matches.d.ts +16 -0
  70. package/dist/esm/load-matches.js +636 -0
  71. package/dist/esm/load-matches.js.map +1 -0
  72. package/dist/esm/location.d.ts +38 -0
  73. package/dist/esm/path.d.ts +3 -6
  74. package/dist/esm/path.js +6 -49
  75. package/dist/esm/path.js.map +1 -1
  76. package/dist/esm/qss.d.ts +6 -4
  77. package/dist/esm/qss.js +19 -19
  78. package/dist/esm/qss.js.map +1 -1
  79. package/dist/esm/redirect.js +3 -3
  80. package/dist/esm/redirect.js.map +1 -1
  81. package/dist/esm/rewrite.d.ts +22 -0
  82. package/dist/esm/rewrite.js +63 -0
  83. package/dist/esm/rewrite.js.map +1 -0
  84. package/dist/esm/route.d.ts +42 -41
  85. package/dist/esm/route.js.map +1 -1
  86. package/dist/esm/router.d.ts +68 -25
  87. package/dist/esm/router.js +136 -683
  88. package/dist/esm/router.js.map +1 -1
  89. package/dist/esm/scroll-restoration.d.ts +1 -10
  90. package/dist/esm/scroll-restoration.js +32 -29
  91. package/dist/esm/scroll-restoration.js.map +1 -1
  92. package/dist/esm/searchParams.js +7 -15
  93. package/dist/esm/searchParams.js.map +1 -1
  94. package/dist/esm/ssr/constants.d.ts +1 -0
  95. package/dist/esm/ssr/constants.js +5 -0
  96. package/dist/esm/ssr/constants.js.map +1 -0
  97. package/dist/esm/ssr/{seroval-plugins.d.ts → serializer/ShallowErrorPlugin.d.ts} +1 -2
  98. package/dist/esm/ssr/{seroval-plugins.js → serializer/ShallowErrorPlugin.js} +2 -2
  99. package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -0
  100. package/dist/esm/ssr/serializer/seroval-plugins.d.ts +2 -0
  101. package/dist/esm/ssr/serializer/seroval-plugins.js +11 -0
  102. package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -0
  103. package/dist/esm/ssr/serializer/transformer.d.ts +56 -0
  104. package/dist/esm/ssr/serializer/transformer.js +52 -0
  105. package/dist/esm/ssr/serializer/transformer.js.map +1 -0
  106. package/dist/esm/ssr/server.d.ts +5 -0
  107. package/dist/esm/ssr/ssr-client.d.ts +5 -1
  108. package/dist/esm/ssr/ssr-client.js +53 -40
  109. package/dist/esm/ssr/ssr-client.js.map +1 -1
  110. package/dist/esm/ssr/ssr-server.d.ts +0 -1
  111. package/dist/esm/ssr/ssr-server.js +12 -10
  112. package/dist/esm/ssr/ssr-server.js.map +1 -1
  113. package/dist/esm/ssr/tsrScript.js +1 -1
  114. package/dist/esm/ssr/tsrScript.js.map +1 -1
  115. package/dist/esm/typePrimitives.d.ts +6 -6
  116. package/dist/esm/utils.d.ts +2 -1
  117. package/dist/esm/utils.js +14 -7
  118. package/dist/esm/utils.js.map +1 -1
  119. package/package.json +1 -1
  120. package/src/Matches.ts +18 -10
  121. package/src/config.ts +42 -0
  122. package/src/fileRoute.ts +15 -3
  123. package/src/index.ts +32 -3
  124. package/src/load-matches.ts +955 -0
  125. package/src/location.ts +38 -0
  126. package/src/path.ts +5 -66
  127. package/src/qss.ts +27 -24
  128. package/src/redirect.ts +3 -3
  129. package/src/rewrite.ts +70 -0
  130. package/src/route.ts +146 -35
  131. package/src/router.ts +263 -972
  132. package/src/scroll-restoration.ts +42 -37
  133. package/src/searchParams.ts +8 -19
  134. package/src/ssr/constants.ts +1 -0
  135. package/src/ssr/{seroval-plugins.ts → serializer/ShallowErrorPlugin.ts} +2 -2
  136. package/src/ssr/serializer/seroval-plugins.ts +9 -0
  137. package/src/ssr/serializer/transformer.ts +215 -0
  138. package/src/ssr/server.ts +6 -0
  139. package/src/ssr/ssr-client.ts +72 -44
  140. package/src/ssr/ssr-server.ts +18 -10
  141. package/src/ssr/tsrScript.ts +5 -1
  142. package/src/typePrimitives.ts +6 -6
  143. package/src/utils.ts +21 -10
  144. package/dist/cjs/ssr/seroval-plugins.cjs.map +0 -1
  145. package/dist/esm/ssr/seroval-plugins.js.map +0 -1
package/src/location.ts CHANGED
@@ -2,12 +2,50 @@ import type { ParsedHistoryState } from '@tanstack/history'
2
2
  import type { AnySchema } from './validators'
3
3
 
4
4
  export interface ParsedLocation<TSearchObj extends AnySchema = {}> {
5
+ /**
6
+ * The full path of the location, including pathname, search, and hash.
7
+ * Does not include the origin. Is the equivalent of calling
8
+ * `url.replace(url.origin, '')`
9
+ */
5
10
  href: string
11
+ /**
12
+ * @description The pathname of the location, including the leading slash.
13
+ */
6
14
  pathname: string
15
+ /**
16
+ * The parsed search parameters of the location in object form.
17
+ */
7
18
  search: TSearchObj
19
+ /**
20
+ * The search string of the location, including the leading question mark.
21
+ */
8
22
  searchStr: string
23
+ /**
24
+ * The in-memory state of the location as it *may* exist in the browser's history.
25
+ */
9
26
  state: ParsedHistoryState
27
+ /**
28
+ * The hash of the location, including the leading hash character.
29
+ */
10
30
  hash: string
31
+ /**
32
+ * The masked location of the location.
33
+ */
11
34
  maskedLocation?: ParsedLocation<TSearchObj>
35
+ /**
36
+ * Whether to unmask the location on reload.
37
+ */
12
38
  unmaskOnReload?: boolean
39
+ /**
40
+ * @private
41
+ * @description The public href of the location, including the origin before any rewrites.
42
+ * If a rewrite is applied, the `href` property will be the rewritten URL.
43
+ */
44
+ publicHref: string
45
+ /**
46
+ * @private
47
+ * @description The full URL of the location, including the origin.
48
+ * @private
49
+ */
50
+ url: string
13
51
  }
package/src/path.ts CHANGED
@@ -97,11 +97,9 @@ export function exactPathTest(
97
97
  // /a/b/c + d/ = /a/b/c/d
98
98
  // /a/b/c + d/e = /a/b/c/d/e
99
99
  interface ResolvePathOptions {
100
- basepath: string
101
100
  base: string
102
101
  to: string
103
102
  trailingSlash?: 'always' | 'never' | 'preserve'
104
- caseSensitive?: boolean
105
103
  parseCache?: ParsePathnameCache
106
104
  }
107
105
 
@@ -151,16 +149,11 @@ function segmentToString(segment: Segment): string {
151
149
  }
152
150
 
153
151
  export function resolvePath({
154
- basepath,
155
152
  base,
156
153
  to,
157
154
  trailingSlash = 'never',
158
- caseSensitive,
159
155
  parseCache,
160
156
  }: ResolvePathOptions) {
161
- base = removeBasepath(basepath, base, caseSensitive)
162
- to = removeBasepath(basepath, to, caseSensitive)
163
-
164
157
  let baseSegments = parsePathname(base, parseCache).slice()
165
158
  const toSegments = parsePathname(to, parseCache)
166
159
 
@@ -201,7 +194,8 @@ export function resolvePath({
201
194
  }
202
195
 
203
196
  const segmentValues = baseSegments.map(segmentToString)
204
- const joined = joinPaths([basepath, ...segmentValues])
197
+ // const joined = joinPaths([basepath, ...segmentValues])
198
+ const joined = joinPaths(segmentValues)
205
199
  return joined
206
200
  }
207
201
 
@@ -491,17 +485,11 @@ function encodePathParam(value: string, decodeCharMap?: Map<string, string>) {
491
485
  }
492
486
 
493
487
  export function matchPathname(
494
- basepath: string,
495
488
  currentPathname: string,
496
489
  matchLocation: Pick<MatchLocation, 'to' | 'fuzzy' | 'caseSensitive'>,
497
490
  parseCache?: ParsePathnameCache,
498
491
  ): AnyPathParams | undefined {
499
- const pathParams = matchByPath(
500
- basepath,
501
- currentPathname,
502
- matchLocation,
503
- parseCache,
504
- )
492
+ const pathParams = matchByPath(currentPathname, matchLocation, parseCache)
505
493
  // const searchMatched = matchBySearch(location.search, matchLocation)
506
494
 
507
495
  if (matchLocation.to && !pathParams) {
@@ -511,49 +499,7 @@ export function matchPathname(
511
499
  return pathParams ?? {}
512
500
  }
513
501
 
514
- export function removeBasepath(
515
- basepath: string,
516
- pathname: string,
517
- caseSensitive: boolean = false,
518
- ) {
519
- // normalize basepath and pathname for case-insensitive comparison if needed
520
- const normalizedBasepath = caseSensitive ? basepath : basepath.toLowerCase()
521
- const normalizedPathname = caseSensitive ? pathname : pathname.toLowerCase()
522
-
523
- switch (true) {
524
- // default behaviour is to serve app from the root - pathname
525
- // left untouched
526
- case normalizedBasepath === '/':
527
- return pathname
528
-
529
- // shortcut for removing the basepath if it matches the pathname
530
- case normalizedPathname === normalizedBasepath:
531
- return ''
532
-
533
- // in case pathname is shorter than basepath - there is
534
- // nothing to remove
535
- case pathname.length < basepath.length:
536
- return pathname
537
-
538
- // avoid matching partial segments - strict equality handled
539
- // earlier, otherwise, basepath separated from pathname with
540
- // separator, therefore lack of separator means partial
541
- // segment match (`/app` should not match `/application`)
542
- case normalizedPathname[normalizedBasepath.length] !== '/':
543
- return pathname
544
-
545
- // remove the basepath from the pathname if it starts with it
546
- case normalizedPathname.startsWith(normalizedBasepath):
547
- return pathname.slice(basepath.length)
548
-
549
- // otherwise, return the pathname as is
550
- default:
551
- return pathname
552
- }
553
- }
554
-
555
502
  export function matchByPath(
556
- basepath: string,
557
503
  from: string,
558
504
  {
559
505
  to,
@@ -562,14 +508,7 @@ export function matchByPath(
562
508
  }: Pick<MatchLocation, 'to' | 'caseSensitive' | 'fuzzy'>,
563
509
  parseCache?: ParsePathnameCache,
564
510
  ): Record<string, string> | undefined {
565
- // check basepath first
566
- if (basepath !== '/' && !from.startsWith(basepath)) {
567
- return undefined
568
- }
569
- // Remove the base path from the pathname
570
- from = removeBasepath(basepath, from, caseSensitive)
571
- // Default to to $ (wildcard)
572
- to = removeBasepath(basepath, `${to ?? '$'}`, caseSensitive)
511
+ const stringTo = to as string
573
512
 
574
513
  // Parse the from and to
575
514
  const baseSegments = parsePathname(
@@ -577,7 +516,7 @@ export function matchByPath(
577
516
  parseCache,
578
517
  )
579
518
  const routeSegments = parsePathname(
580
- to.startsWith('/') ? to : `/${to}`,
519
+ stringTo.startsWith('/') ? stringTo : `/${stringTo}`,
581
520
  parseCache,
582
521
  )
583
522
 
package/src/qss.ts CHANGED
@@ -6,12 +6,15 @@
6
6
  * This reimplementation uses modern browser APIs
7
7
  * (namely URLSearchParams) and TypeScript while still
8
8
  * maintaining the original functionality and interface.
9
+ *
10
+ * Update: this implementation has also been mangled to
11
+ * fit exactly our use-case (single value per key in encoding).
9
12
  */
10
13
 
11
14
  /**
12
15
  * Encodes an object into a query string.
13
16
  * @param obj - The object to encode into a query string.
14
- * @param [pfx] - An optional prefix to add before the query string.
17
+ * @param stringify - An optional custom stringify function.
15
18
  * @returns The encoded query string.
16
19
  * @example
17
20
  * ```
@@ -19,18 +22,20 @@
19
22
  * // Expected output: "token=foo&key=value"
20
23
  * ```
21
24
  */
22
- export function encode(obj: any, pfx?: string) {
23
- const normalizedObject = Object.entries(obj).flatMap(([key, value]) => {
24
- if (Array.isArray(value)) {
25
- return value.map((v) => [key, String(v)])
26
- } else {
27
- return [[key, String(value)]]
28
- }
29
- })
25
+ export function encode(
26
+ obj: Record<string, any>,
27
+ stringify: (value: any) => string = String,
28
+ ): string {
29
+ const result = new URLSearchParams()
30
30
 
31
- const searchParams = new URLSearchParams(normalizedObject)
31
+ for (const key in obj) {
32
+ const val = obj[key]
33
+ if (val !== undefined) {
34
+ result.set(key, stringify(val))
35
+ }
36
+ }
32
37
 
33
- return (pfx || '') + searchParams.toString()
38
+ return result.toString()
34
39
  }
35
40
 
36
41
  /**
@@ -52,28 +57,26 @@ function toValue(str: unknown) {
52
57
  /**
53
58
  * Decodes a query string into an object.
54
59
  * @param str - The query string to decode.
55
- * @param [pfx] - An optional prefix to filter out from the query string.
56
60
  * @returns The decoded key-value pairs in an object format.
57
61
  * @example
58
62
  * // Example input: decode("token=foo&key=value")
59
63
  * // Expected output: { "token": "foo", "key": "value" }
60
64
  */
61
- export function decode(str: any, pfx?: string): any {
62
- const searchParamsPart = pfx ? str.slice(pfx.length) : str
63
- const searchParams = new URLSearchParams(searchParamsPart)
65
+ export function decode(str: any): any {
66
+ const searchParams = new URLSearchParams(str)
64
67
 
65
- const entries = [...searchParams.entries()]
68
+ const result: Record<string, unknown> = {}
66
69
 
67
- return entries.reduce<Record<string, unknown>>((acc, [key, value]) => {
68
- const previousValue = acc[key]
70
+ for (const [key, value] of searchParams.entries()) {
71
+ const previousValue = result[key]
69
72
  if (previousValue == null) {
70
- acc[key] = toValue(value)
73
+ result[key] = toValue(value)
74
+ } else if (Array.isArray(previousValue)) {
75
+ previousValue.push(toValue(value))
71
76
  } else {
72
- acc[key] = Array.isArray(previousValue)
73
- ? [...previousValue, toValue(value)]
74
- : [previousValue, toValue(value)]
77
+ result[key] = [previousValue, toValue(value)]
75
78
  }
79
+ }
76
80
 
77
- return acc
78
- }, {})
81
+ return result
79
82
  }
package/src/redirect.ts CHANGED
@@ -65,9 +65,9 @@ export function redirect<
65
65
  ): Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> {
66
66
  opts.statusCode = opts.statusCode || opts.code || 307
67
67
 
68
- if (!opts.reloadDocument) {
68
+ if (!opts.reloadDocument && typeof opts.href === 'string') {
69
69
  try {
70
- new URL(`${opts.href}`)
70
+ new URL(opts.href)
71
71
  opts.reloadDocument = true
72
72
  } catch {}
73
73
  }
@@ -103,7 +103,7 @@ export function isResolvedRedirect(
103
103
  }
104
104
 
105
105
  export function parseRedirect(obj: any) {
106
- if (typeof obj === 'object' && obj.isSerializedRedirect) {
106
+ if (obj !== null && typeof obj === 'object' && obj.isSerializedRedirect) {
107
107
  return redirect(obj)
108
108
  }
109
109
 
package/src/rewrite.ts ADDED
@@ -0,0 +1,70 @@
1
+ import { joinPaths, trimPath } from './path'
2
+ import type { LocationRewrite } from './router'
3
+
4
+ export function composeRewrites(rewrites: Array<LocationRewrite>) {
5
+ return {
6
+ input: ({ url }) => {
7
+ for (const rewrite of rewrites) {
8
+ url = executeRewriteInput(rewrite, url)
9
+ }
10
+ return url
11
+ },
12
+ output: ({ url }) => {
13
+ for (let i = rewrites.length - 1; i >= 0; i--) {
14
+ url = executeRewriteOutput(rewrites[i], url)
15
+ }
16
+ return url
17
+ },
18
+ } satisfies LocationRewrite
19
+ }
20
+
21
+ export function rewriteBasepath(opts: {
22
+ basepath: string
23
+ caseSensitive?: boolean
24
+ }) {
25
+ const trimmedBasepath = trimPath(opts.basepath)
26
+ const regex = new RegExp(
27
+ `^/${trimmedBasepath}/`,
28
+ opts.caseSensitive ? '' : 'i',
29
+ )
30
+ return {
31
+ input: ({ url }) => {
32
+ url.pathname = url.pathname.replace(regex, '/')
33
+ return url
34
+ },
35
+ output: ({ url }) => {
36
+ url.pathname = joinPaths(['/', trimmedBasepath, url.pathname])
37
+ return url
38
+ },
39
+ } satisfies LocationRewrite
40
+ }
41
+
42
+ export function executeRewriteInput(
43
+ rewrite: LocationRewrite | undefined,
44
+ url: URL,
45
+ ): URL {
46
+ const res = rewrite?.input?.({ url })
47
+ if (res) {
48
+ if (typeof res === 'string') {
49
+ return new URL(res)
50
+ } else if (res instanceof URL) {
51
+ return res
52
+ }
53
+ }
54
+ return url
55
+ }
56
+
57
+ export function executeRewriteOutput(
58
+ rewrite: LocationRewrite | undefined,
59
+ url: URL,
60
+ ): URL {
61
+ const res = rewrite?.output?.({ url })
62
+ if (res) {
63
+ if (typeof res === 'string') {
64
+ return new URL(res)
65
+ } else if (res instanceof URL) {
66
+ return res
67
+ }
68
+ }
69
+ return url
70
+ }