@tanstack/react-router 1.97.19 → 1.97.21

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 (182) hide show
  1. package/dist/cjs/Match.cjs +4 -5
  2. package/dist/cjs/Match.cjs.map +1 -1
  3. package/dist/cjs/Matches.cjs +0 -10
  4. package/dist/cjs/Matches.cjs.map +1 -1
  5. package/dist/cjs/Matches.d.cts +3 -30
  6. package/dist/cjs/RouterProvider.cjs.map +1 -1
  7. package/dist/cjs/RouterProvider.d.cts +2 -2
  8. package/dist/cjs/Transitioner.cjs +2 -2
  9. package/dist/cjs/Transitioner.cjs.map +1 -1
  10. package/dist/cjs/awaited.cjs +6 -6
  11. package/dist/cjs/awaited.cjs.map +1 -1
  12. package/dist/cjs/awaited.d.cts +1 -1
  13. package/dist/cjs/fileRoute.cjs.map +1 -1
  14. package/dist/cjs/fileRoute.d.cts +2 -3
  15. package/dist/cjs/index.cjs +136 -42
  16. package/dist/cjs/index.cjs.map +1 -1
  17. package/dist/cjs/index.d.cts +12 -26
  18. package/dist/cjs/link.cjs +8 -9
  19. package/dist/cjs/link.cjs.map +1 -1
  20. package/dist/cjs/link.d.cts +2 -49
  21. package/dist/cjs/redirects.cjs.map +1 -1
  22. package/dist/cjs/redirects.d.cts +1 -1
  23. package/dist/cjs/route.cjs +14 -15
  24. package/dist/cjs/route.cjs.map +1 -1
  25. package/dist/cjs/route.d.cts +4 -151
  26. package/dist/cjs/routeInfo.d.cts +2 -3
  27. package/dist/cjs/router.cjs +51 -70
  28. package/dist/cjs/router.cjs.map +1 -1
  29. package/dist/cjs/router.d.cts +4 -19
  30. package/dist/cjs/routerContext.d.cts +1 -1
  31. package/dist/cjs/scroll-restoration.cjs +2 -2
  32. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  33. package/dist/cjs/scroll-restoration.d.cts +1 -1
  34. package/dist/cjs/structuralSharing.d.cts +1 -4
  35. package/dist/cjs/typePrimitives.d.cts +1 -1
  36. package/dist/cjs/useLoaderData.cjs.map +1 -1
  37. package/dist/cjs/useLoaderData.d.cts +2 -1
  38. package/dist/cjs/useLoaderDeps.cjs.map +1 -1
  39. package/dist/cjs/useLoaderDeps.d.cts +2 -1
  40. package/dist/cjs/useMatch.cjs.map +1 -1
  41. package/dist/cjs/useMatch.d.cts +2 -1
  42. package/dist/cjs/useParams.cjs.map +1 -1
  43. package/dist/cjs/useParams.d.cts +2 -1
  44. package/dist/cjs/useRouteContext.cjs.map +1 -1
  45. package/dist/cjs/useRouteContext.d.cts +2 -1
  46. package/dist/cjs/useRouterState.cjs +2 -2
  47. package/dist/cjs/useRouterState.cjs.map +1 -1
  48. package/dist/cjs/useSearch.cjs.map +1 -1
  49. package/dist/cjs/useSearch.d.cts +2 -1
  50. package/dist/cjs/utils.cjs +0 -152
  51. package/dist/cjs/utils.cjs.map +1 -1
  52. package/dist/cjs/utils.d.cts +1 -79
  53. package/dist/esm/Match.js +1 -2
  54. package/dist/esm/Match.js.map +1 -1
  55. package/dist/esm/Matches.d.ts +3 -30
  56. package/dist/esm/Matches.js +0 -10
  57. package/dist/esm/Matches.js.map +1 -1
  58. package/dist/esm/RouterProvider.d.ts +2 -2
  59. package/dist/esm/RouterProvider.js.map +1 -1
  60. package/dist/esm/Transitioner.js +1 -1
  61. package/dist/esm/Transitioner.js.map +1 -1
  62. package/dist/esm/awaited.d.ts +1 -1
  63. package/dist/esm/awaited.js +1 -1
  64. package/dist/esm/awaited.js.map +1 -1
  65. package/dist/esm/fileRoute.d.ts +2 -3
  66. package/dist/esm/fileRoute.js.map +1 -1
  67. package/dist/esm/index.d.ts +12 -26
  68. package/dist/esm/index.js +5 -10
  69. package/dist/esm/index.js.map +1 -1
  70. package/dist/esm/link.d.ts +2 -49
  71. package/dist/esm/link.js +2 -3
  72. package/dist/esm/link.js.map +1 -1
  73. package/dist/esm/redirects.d.ts +1 -1
  74. package/dist/esm/redirects.js.map +1 -1
  75. package/dist/esm/route.d.ts +4 -151
  76. package/dist/esm/route.js +1 -2
  77. package/dist/esm/route.js.map +1 -1
  78. package/dist/esm/routeInfo.d.ts +2 -3
  79. package/dist/esm/router.d.ts +4 -19
  80. package/dist/esm/router.js +1 -20
  81. package/dist/esm/router.js.map +1 -1
  82. package/dist/esm/routerContext.d.ts +1 -1
  83. package/dist/esm/scroll-restoration.d.ts +1 -1
  84. package/dist/esm/scroll-restoration.js +1 -1
  85. package/dist/esm/scroll-restoration.js.map +1 -1
  86. package/dist/esm/structuralSharing.d.ts +1 -4
  87. package/dist/esm/typePrimitives.d.ts +1 -1
  88. package/dist/esm/useLoaderData.d.ts +2 -1
  89. package/dist/esm/useLoaderData.js.map +1 -1
  90. package/dist/esm/useLoaderDeps.d.ts +2 -1
  91. package/dist/esm/useLoaderDeps.js.map +1 -1
  92. package/dist/esm/useMatch.d.ts +2 -1
  93. package/dist/esm/useMatch.js.map +1 -1
  94. package/dist/esm/useParams.d.ts +2 -1
  95. package/dist/esm/useParams.js.map +1 -1
  96. package/dist/esm/useRouteContext.d.ts +2 -1
  97. package/dist/esm/useRouteContext.js.map +1 -1
  98. package/dist/esm/useRouterState.js +1 -1
  99. package/dist/esm/useRouterState.js.map +1 -1
  100. package/dist/esm/useSearch.d.ts +2 -1
  101. package/dist/esm/useSearch.js.map +1 -1
  102. package/dist/esm/utils.d.ts +1 -79
  103. package/dist/esm/utils.js +0 -152
  104. package/dist/esm/utils.js.map +1 -1
  105. package/package.json +3 -2
  106. package/src/Match.tsx +5 -2
  107. package/src/Matches.tsx +8 -101
  108. package/src/RouterProvider.tsx +4 -2
  109. package/src/Transitioner.tsx +1 -1
  110. package/src/awaited.tsx +2 -2
  111. package/src/fileRoute.ts +6 -3
  112. package/src/index.tsx +128 -127
  113. package/src/link.tsx +27 -155
  114. package/src/redirects.ts +1 -1
  115. package/src/route.ts +43 -316
  116. package/src/routeInfo.ts +7 -3
  117. package/src/router.ts +32 -52
  118. package/src/scroll-restoration.tsx +2 -3
  119. package/src/structuralSharing.ts +5 -7
  120. package/src/typePrimitives.ts +1 -1
  121. package/src/useLoaderData.tsx +2 -1
  122. package/src/useLoaderDeps.tsx +2 -1
  123. package/src/useMatch.tsx +2 -1
  124. package/src/useParams.tsx +2 -1
  125. package/src/useRouteContext.ts +2 -1
  126. package/src/useRouterState.tsx +1 -1
  127. package/src/useSearch.tsx +2 -1
  128. package/src/utils.ts +1 -389
  129. package/dist/cjs/defer.cjs +0 -25
  130. package/dist/cjs/defer.cjs.map +0 -1
  131. package/dist/cjs/defer.d.cts +0 -20
  132. package/dist/cjs/location.d.cts +0 -12
  133. package/dist/cjs/manifest.d.cts +0 -24
  134. package/dist/cjs/path.cjs +0 -289
  135. package/dist/cjs/path.cjs.map +0 -1
  136. package/dist/cjs/path.d.cts +0 -34
  137. package/dist/cjs/qss.cjs +0 -51
  138. package/dist/cjs/qss.cjs.map +0 -1
  139. package/dist/cjs/qss.d.cts +0 -27
  140. package/dist/cjs/root.cjs +0 -5
  141. package/dist/cjs/root.cjs.map +0 -1
  142. package/dist/cjs/root.d.cts +0 -2
  143. package/dist/cjs/searchMiddleware.cjs +0 -42
  144. package/dist/cjs/searchMiddleware.cjs.map +0 -1
  145. package/dist/cjs/searchMiddleware.d.cts +0 -5
  146. package/dist/cjs/searchParams.cjs +0 -61
  147. package/dist/cjs/searchParams.cjs.map +0 -1
  148. package/dist/cjs/searchParams.d.cts +0 -7
  149. package/dist/cjs/serializer.d.cts +0 -15
  150. package/dist/cjs/validators.d.cts +0 -51
  151. package/dist/esm/defer.d.ts +0 -20
  152. package/dist/esm/defer.js +0 -25
  153. package/dist/esm/defer.js.map +0 -1
  154. package/dist/esm/location.d.ts +0 -12
  155. package/dist/esm/manifest.d.ts +0 -24
  156. package/dist/esm/path.d.ts +0 -34
  157. package/dist/esm/path.js +0 -289
  158. package/dist/esm/path.js.map +0 -1
  159. package/dist/esm/qss.d.ts +0 -27
  160. package/dist/esm/qss.js +0 -51
  161. package/dist/esm/qss.js.map +0 -1
  162. package/dist/esm/root.d.ts +0 -2
  163. package/dist/esm/root.js +0 -5
  164. package/dist/esm/root.js.map +0 -1
  165. package/dist/esm/searchMiddleware.d.ts +0 -5
  166. package/dist/esm/searchMiddleware.js +0 -42
  167. package/dist/esm/searchMiddleware.js.map +0 -1
  168. package/dist/esm/searchParams.d.ts +0 -7
  169. package/dist/esm/searchParams.js +0 -61
  170. package/dist/esm/searchParams.js.map +0 -1
  171. package/dist/esm/serializer.d.ts +0 -15
  172. package/dist/esm/validators.d.ts +0 -51
  173. package/src/defer.ts +0 -52
  174. package/src/location.ts +0 -13
  175. package/src/manifest.ts +0 -32
  176. package/src/path.ts +0 -427
  177. package/src/qss.ts +0 -91
  178. package/src/root.ts +0 -2
  179. package/src/searchMiddleware.ts +0 -54
  180. package/src/searchParams.ts +0 -77
  181. package/src/serializer.ts +0 -24
  182. package/src/validators.ts +0 -121
package/src/path.ts DELETED
@@ -1,427 +0,0 @@
1
- import { last } from './utils'
2
- import type { MatchLocation } from './RouterProvider'
3
- import type { AnyPathParams } from './route'
4
-
5
- export interface Segment {
6
- type: 'pathname' | 'param' | 'wildcard'
7
- value: string
8
- }
9
-
10
- export function joinPaths(paths: Array<string | undefined>) {
11
- return cleanPath(
12
- paths
13
- .filter((val) => {
14
- return val !== undefined
15
- })
16
- .join('/'),
17
- )
18
- }
19
-
20
- export function cleanPath(path: string) {
21
- // remove double slashes
22
- return path.replace(/\/{2,}/g, '/')
23
- }
24
-
25
- export function trimPathLeft(path: string) {
26
- return path === '/' ? path : path.replace(/^\/{1,}/, '')
27
- }
28
-
29
- export function trimPathRight(path: string) {
30
- return path === '/' ? path : path.replace(/\/{1,}$/, '')
31
- }
32
-
33
- export function trimPath(path: string) {
34
- return trimPathRight(trimPathLeft(path))
35
- }
36
-
37
- export function removeTrailingSlash(value: string, basepath: string): string {
38
- if (value.endsWith('/') && value !== '/' && value !== `${basepath}/`) {
39
- return value.slice(0, -1)
40
- }
41
- return value
42
- }
43
-
44
- // intended to only compare path name
45
- // see the usage in the isActive under useLinkProps
46
- // /sample/path1 = /sample/path1/
47
- // /sample/path1/some <> /sample/path1
48
- export function exactPathTest(
49
- pathName1: string,
50
- pathName2: string,
51
- basepath: string,
52
- ): boolean {
53
- return (
54
- removeTrailingSlash(pathName1, basepath) ===
55
- removeTrailingSlash(pathName2, basepath)
56
- )
57
- }
58
-
59
- // When resolving relative paths, we treat all paths as if they are trailing slash
60
- // documents. All trailing slashes are removed after the path is resolved.
61
- // Here are a few examples:
62
- //
63
- // /a/b/c + ./d = /a/b/c/d
64
- // /a/b/c + ../d = /a/b/d
65
- // /a/b/c + ./d/ = /a/b/c/d
66
- // /a/b/c + ../d/ = /a/b/d
67
- // /a/b/c + ./ = /a/b/c
68
- //
69
- // Absolute paths that start with `/` short circuit the resolution process to the root
70
- // path.
71
- //
72
- // Here are some examples:
73
- //
74
- // /a/b/c + /d = /d
75
- // /a/b/c + /d/ = /d
76
- // /a/b/c + / = /
77
- //
78
- // Non-.-prefixed paths are still treated as relative paths, resolved like `./`
79
- //
80
- // Here are some examples:
81
- //
82
- // /a/b/c + d = /a/b/c/d
83
- // /a/b/c + d/ = /a/b/c/d
84
- // /a/b/c + d/e = /a/b/c/d/e
85
- interface ResolvePathOptions {
86
- basepath: string
87
- base: string
88
- to: string
89
- trailingSlash?: 'always' | 'never' | 'preserve'
90
- caseSensitive?: boolean
91
- }
92
-
93
- export function resolvePath({
94
- basepath,
95
- base,
96
- to,
97
- trailingSlash = 'never',
98
- caseSensitive,
99
- }: ResolvePathOptions) {
100
- base = removeBasepath(basepath, base, caseSensitive)
101
- to = removeBasepath(basepath, to, caseSensitive)
102
-
103
- let baseSegments = parsePathname(base)
104
- const toSegments = parsePathname(to)
105
-
106
- if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
107
- baseSegments.pop()
108
- }
109
-
110
- toSegments.forEach((toSegment, index) => {
111
- if (toSegment.value === '/') {
112
- if (!index) {
113
- // Leading slash
114
- baseSegments = [toSegment]
115
- } else if (index === toSegments.length - 1) {
116
- // Trailing Slash
117
- baseSegments.push(toSegment)
118
- } else {
119
- // ignore inter-slashes
120
- }
121
- } else if (toSegment.value === '..') {
122
- baseSegments.pop()
123
- } else if (toSegment.value === '.') {
124
- // ignore
125
- } else {
126
- baseSegments.push(toSegment)
127
- }
128
- })
129
-
130
- if (baseSegments.length > 1) {
131
- if (last(baseSegments)?.value === '/') {
132
- if (trailingSlash === 'never') {
133
- baseSegments.pop()
134
- }
135
- } else if (trailingSlash === 'always') {
136
- baseSegments.push({ type: 'pathname', value: '/' })
137
- }
138
- }
139
-
140
- const joined = joinPaths([basepath, ...baseSegments.map((d) => d.value)])
141
- return cleanPath(joined)
142
- }
143
-
144
- export function parsePathname(pathname?: string): Array<Segment> {
145
- if (!pathname) {
146
- return []
147
- }
148
-
149
- pathname = cleanPath(pathname)
150
-
151
- const segments: Array<Segment> = []
152
-
153
- if (pathname.slice(0, 1) === '/') {
154
- pathname = pathname.substring(1)
155
- segments.push({
156
- type: 'pathname',
157
- value: '/',
158
- })
159
- }
160
-
161
- if (!pathname) {
162
- return segments
163
- }
164
-
165
- // Remove empty segments and '.' segments
166
- const split = pathname.split('/').filter(Boolean)
167
-
168
- segments.push(
169
- ...split.map((part): Segment => {
170
- if (part === '$' || part === '*') {
171
- return {
172
- type: 'wildcard',
173
- value: part,
174
- }
175
- }
176
-
177
- if (part.charAt(0) === '$') {
178
- return {
179
- type: 'param',
180
- value: part,
181
- }
182
- }
183
-
184
- return {
185
- type: 'pathname',
186
- value: decodeURI(part),
187
- }
188
- }),
189
- )
190
-
191
- if (pathname.slice(-1) === '/') {
192
- pathname = pathname.substring(1)
193
- segments.push({
194
- type: 'pathname',
195
- value: '/',
196
- })
197
- }
198
-
199
- return segments
200
- }
201
-
202
- interface InterpolatePathOptions {
203
- path?: string
204
- params: Record<string, unknown>
205
- leaveWildcards?: boolean
206
- leaveParams?: boolean
207
- // Map of encoded chars to decoded chars (e.g. '%40' -> '@') that should remain decoded in path params
208
- decodeCharMap?: Map<string, string>
209
- }
210
-
211
- export function interpolatePath({
212
- path,
213
- params,
214
- leaveWildcards,
215
- leaveParams,
216
- decodeCharMap,
217
- }: InterpolatePathOptions) {
218
- const interpolatedPathSegments = parsePathname(path)
219
- const encodedParams: any = {}
220
-
221
- for (const [key, value] of Object.entries(params)) {
222
- const isValueString = typeof value === 'string'
223
-
224
- if (['*', '_splat'].includes(key)) {
225
- // the splat/catch-all routes shouldn't have the '/' encoded out
226
- encodedParams[key] = isValueString ? encodeURI(value) : value
227
- } else {
228
- encodedParams[key] = isValueString
229
- ? encodePathParam(value, decodeCharMap)
230
- : value
231
- }
232
- }
233
-
234
- return joinPaths(
235
- interpolatedPathSegments.map((segment) => {
236
- if (segment.type === 'wildcard') {
237
- const value = encodedParams._splat
238
- if (leaveWildcards) return `${segment.value}${value ?? ''}`
239
- return value
240
- }
241
-
242
- if (segment.type === 'param') {
243
- if (leaveParams) {
244
- const value = encodedParams[segment.value]
245
- return `${segment.value}${value ?? ''}`
246
- }
247
- return encodedParams![segment.value.substring(1)] ?? 'undefined'
248
- }
249
-
250
- return segment.value
251
- }),
252
- )
253
- }
254
-
255
- function encodePathParam(value: string, decodeCharMap?: Map<string, string>) {
256
- let encoded = encodeURIComponent(value)
257
- if (decodeCharMap) {
258
- for (const [encodedChar, char] of decodeCharMap) {
259
- encoded = encoded.replaceAll(encodedChar, char)
260
- }
261
- }
262
- return encoded
263
- }
264
-
265
- export function matchPathname(
266
- basepath: string,
267
- currentPathname: string,
268
- matchLocation: Pick<MatchLocation, 'to' | 'fuzzy' | 'caseSensitive'>,
269
- ): AnyPathParams | undefined {
270
- const pathParams = matchByPath(basepath, currentPathname, matchLocation)
271
- // const searchMatched = matchBySearch(location.search, matchLocation)
272
-
273
- if (matchLocation.to && !pathParams) {
274
- return
275
- }
276
-
277
- return pathParams ?? {}
278
- }
279
-
280
- export function removeBasepath(
281
- basepath: string,
282
- pathname: string,
283
- caseSensitive: boolean = false,
284
- ) {
285
- // normalize basepath and pathname for case-insensitive comparison if needed
286
- const normalizedBasepath = caseSensitive ? basepath : basepath.toLowerCase()
287
- const normalizedPathname = caseSensitive ? pathname : pathname.toLowerCase()
288
-
289
- switch (true) {
290
- // default behaviour is to serve app from the root - pathname
291
- // left untouched
292
- case normalizedBasepath === '/':
293
- return pathname
294
-
295
- // shortcut for removing the basepath if it matches the pathname
296
- case normalizedPathname === normalizedBasepath:
297
- return ''
298
-
299
- // in case pathname is shorter than basepath - there is
300
- // nothing to remove
301
- case pathname.length < basepath.length:
302
- return pathname
303
-
304
- // avoid matching partial segments - strict equality handled
305
- // earlier, otherwise, basepath separated from pathname with
306
- // separator, therefore lack of separator means partial
307
- // segment match (`/app` should not match `/application`)
308
- case normalizedPathname[normalizedBasepath.length] !== '/':
309
- return pathname
310
-
311
- // remove the basepath from the pathname if it starts with it
312
- case normalizedPathname.startsWith(normalizedBasepath):
313
- return pathname.slice(basepath.length)
314
-
315
- // otherwise, return the pathname as is
316
- default:
317
- return pathname
318
- }
319
- }
320
-
321
- export function matchByPath(
322
- basepath: string,
323
- from: string,
324
- matchLocation: Pick<MatchLocation, 'to' | 'caseSensitive' | 'fuzzy'>,
325
- ): Record<string, string> | undefined {
326
- // check basepath first
327
- if (basepath !== '/' && !from.startsWith(basepath)) {
328
- return undefined
329
- }
330
- // Remove the base path from the pathname
331
- from = removeBasepath(basepath, from, matchLocation.caseSensitive)
332
- // Default to to $ (wildcard)
333
- const to = removeBasepath(
334
- basepath,
335
- `${matchLocation.to ?? '$'}`,
336
- matchLocation.caseSensitive,
337
- )
338
-
339
- // Parse the from and to
340
- const baseSegments = parsePathname(from)
341
- const routeSegments = parsePathname(to)
342
-
343
- if (!from.startsWith('/')) {
344
- baseSegments.unshift({
345
- type: 'pathname',
346
- value: '/',
347
- })
348
- }
349
-
350
- if (!to.startsWith('/')) {
351
- routeSegments.unshift({
352
- type: 'pathname',
353
- value: '/',
354
- })
355
- }
356
-
357
- const params: Record<string, string> = {}
358
-
359
- const isMatch = (() => {
360
- for (
361
- let i = 0;
362
- i < Math.max(baseSegments.length, routeSegments.length);
363
- i++
364
- ) {
365
- const baseSegment = baseSegments[i]
366
- const routeSegment = routeSegments[i]
367
-
368
- const isLastBaseSegment = i >= baseSegments.length - 1
369
- const isLastRouteSegment = i >= routeSegments.length - 1
370
-
371
- if (routeSegment) {
372
- if (routeSegment.type === 'wildcard') {
373
- const _splat = decodeURI(
374
- joinPaths(baseSegments.slice(i).map((d) => d.value)),
375
- )
376
- // TODO: Deprecate *
377
- params['*'] = _splat
378
- params['_splat'] = _splat
379
- return true
380
- }
381
-
382
- if (routeSegment.type === 'pathname') {
383
- if (routeSegment.value === '/' && !baseSegment?.value) {
384
- return true
385
- }
386
-
387
- if (baseSegment) {
388
- if (matchLocation.caseSensitive) {
389
- if (routeSegment.value !== baseSegment.value) {
390
- return false
391
- }
392
- } else if (
393
- routeSegment.value.toLowerCase() !==
394
- baseSegment.value.toLowerCase()
395
- ) {
396
- return false
397
- }
398
- }
399
- }
400
-
401
- if (!baseSegment) {
402
- return false
403
- }
404
-
405
- if (routeSegment.type === 'param') {
406
- if (baseSegment.value === '/') {
407
- return false
408
- }
409
- if (baseSegment.value.charAt(0) !== '$') {
410
- params[routeSegment.value.substring(1)] = decodeURIComponent(
411
- baseSegment.value,
412
- )
413
- }
414
- }
415
- }
416
-
417
- if (!isLastBaseSegment && isLastRouteSegment) {
418
- params['**'] = joinPaths(baseSegments.slice(i + 1).map((d) => d.value))
419
- return !!matchLocation.fuzzy && routeSegment?.value !== '/'
420
- }
421
- }
422
-
423
- return true
424
- })()
425
-
426
- return isMatch ? params : undefined
427
- }
package/src/qss.ts DELETED
@@ -1,91 +0,0 @@
1
- /**
2
- * Program uses a modified version of the `qss` package:
3
- * Copyright (c) Luke Edwards luke.edwards05@gmail.com, MIT License
4
- * https://github.com/lukeed/qss/blob/master/license.md
5
- */
6
-
7
- /**
8
- * Encodes an object into a query string.
9
- * @param obj - The object to encode into a query string.
10
- * @param [pfx] - An optional prefix to add before the query string.
11
- * @returns The encoded query string.
12
- * @example
13
- * ```
14
- * // Example input: encode({ token: 'foo', key: 'value' })
15
- * // Expected output: "token=foo&key=value"
16
- * ```
17
- */
18
- export function encode(obj: any, pfx?: string) {
19
- let k,
20
- i,
21
- tmp,
22
- str = ''
23
-
24
- for (k in obj) {
25
- if ((tmp = obj[k]) !== void 0) {
26
- if (Array.isArray(tmp)) {
27
- for (i = 0; i < tmp.length; i++) {
28
- str && (str += '&')
29
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i])
30
- }
31
- } else {
32
- str && (str += '&')
33
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp)
34
- }
35
- }
36
- }
37
-
38
- return (pfx || '') + str
39
- }
40
-
41
- /**
42
- * Converts a string value to its appropriate type (string, number, boolean).
43
- * @param mix - The string value to convert.
44
- * @returns The converted value.
45
- * @example
46
- * // Example input: toValue("123")
47
- * // Expected output: 123
48
- */
49
- function toValue(mix: any) {
50
- if (!mix) return ''
51
- const str = decodeURIComponent(mix)
52
- if (str === 'false') return false
53
- if (str === 'true') return true
54
- return +str * 0 === 0 && +str + '' === str ? +str : str
55
- }
56
-
57
- /**
58
- * Decodes a query string into an object.
59
- * @param str - The query string to decode.
60
- * @param [pfx] - An optional prefix to filter out from the query string.
61
- * @returns The decoded key-value pairs in an object format.
62
- * @example
63
- * // Example input: decode("token=foo&key=value")
64
- * // Expected output: { "token": "foo", "key": "value" }
65
- */
66
- export function decode(str: any, pfx?: string) {
67
- let tmp, k
68
- const out: any = {},
69
- arr = (pfx ? str.substr(pfx.length) : str).split('&')
70
-
71
- while ((tmp = arr.shift())) {
72
- const equalIndex = tmp.indexOf('=')
73
- if (equalIndex !== -1) {
74
- k = tmp.slice(0, equalIndex)
75
- k = decodeURIComponent(k)
76
- const value = tmp.slice(equalIndex + 1)
77
- if (out[k] !== void 0) {
78
- // @ts-expect-error
79
- out[k] = [].concat(out[k], toValue(value))
80
- } else {
81
- out[k] = toValue(value)
82
- }
83
- } else {
84
- k = tmp
85
- k = decodeURIComponent(k)
86
- out[k] = ''
87
- }
88
- }
89
-
90
- return out
91
- }
package/src/root.ts DELETED
@@ -1,2 +0,0 @@
1
- export const rootRouteId = '__root__'
2
- export type RootRouteId = typeof rootRouteId
@@ -1,54 +0,0 @@
1
- import { deepEqual } from './utils'
2
- import type { NoInfer, PickOptional } from './utils'
3
- import type { SearchMiddleware } from './route'
4
- import type { IsRequiredParams } from './link'
5
-
6
- export function retainSearchParams<TSearchSchema extends object>(
7
- keys: Array<keyof TSearchSchema> | true,
8
- ): SearchMiddleware<TSearchSchema> {
9
- return ({ search, next }) => {
10
- const result = next(search)
11
- if (keys === true) {
12
- return { ...search, ...result }
13
- }
14
- // add missing keys from search to result
15
- keys.forEach((key) => {
16
- if (!(key in result)) {
17
- result[key] = search[key]
18
- }
19
- })
20
- return result
21
- }
22
- }
23
-
24
- export function stripSearchParams<
25
- TSearchSchema,
26
- TOptionalProps = PickOptional<NoInfer<TSearchSchema>>,
27
- const TValues =
28
- | Partial<NoInfer<TOptionalProps>>
29
- | Array<keyof TOptionalProps>,
30
- const TInput = IsRequiredParams<TSearchSchema> extends never
31
- ? TValues | true
32
- : TValues,
33
- >(input: NoInfer<TInput>): SearchMiddleware<TSearchSchema> {
34
- return ({ search, next }) => {
35
- if (input === true) {
36
- return {}
37
- }
38
- const result = next(search) as Record<string, unknown>
39
- if (Array.isArray(input)) {
40
- input.forEach((key) => {
41
- delete result[key]
42
- })
43
- } else {
44
- Object.entries(input as Record<string, unknown>).forEach(
45
- ([key, value]) => {
46
- if (deepEqual(result[key], value)) {
47
- delete result[key]
48
- }
49
- },
50
- )
51
- }
52
- return result as any
53
- }
54
- }
@@ -1,77 +0,0 @@
1
- import { decode, encode } from './qss'
2
- import type { AnySchema } from './validators'
3
-
4
- export const defaultParseSearch = parseSearchWith(JSON.parse)
5
- export const defaultStringifySearch = stringifySearchWith(
6
- JSON.stringify,
7
- JSON.parse,
8
- )
9
-
10
- export function parseSearchWith(parser: (str: string) => any) {
11
- return (searchStr: string): AnySchema => {
12
- if (searchStr.substring(0, 1) === '?') {
13
- searchStr = searchStr.substring(1)
14
- }
15
-
16
- const query: Record<string, unknown> = decode(searchStr)
17
-
18
- // Try to parse any query params that might be json
19
- for (const key in query) {
20
- const value = query[key]
21
- if (typeof value === 'string') {
22
- try {
23
- query[key] = parser(value)
24
- } catch (err) {
25
- //
26
- }
27
- }
28
- }
29
-
30
- return query
31
- }
32
- }
33
-
34
- export function stringifySearchWith(
35
- stringify: (search: any) => string,
36
- parser?: (str: string) => any,
37
- ) {
38
- function stringifyValue(val: any) {
39
- if (typeof val === 'object' && val !== null) {
40
- try {
41
- return stringify(val)
42
- } catch (err) {
43
- // silent
44
- }
45
- } else if (typeof val === 'string' && typeof parser === 'function') {
46
- try {
47
- // Check if it's a valid parseable string.
48
- // If it is, then stringify it again.
49
- parser(val)
50
- return stringify(val)
51
- } catch (err) {
52
- // silent
53
- }
54
- }
55
- return val
56
- }
57
-
58
- return (search: Record<string, any>) => {
59
- search = { ...search }
60
-
61
- Object.keys(search).forEach((key) => {
62
- const val = search[key]
63
- if (typeof val === 'undefined' || val === undefined) {
64
- delete search[key]
65
- } else {
66
- search[key] = stringifyValue(val)
67
- }
68
- })
69
-
70
- const searchStr = encode(search as Record<string, string>).toString()
71
-
72
- return searchStr ? `?${searchStr}` : ''
73
- }
74
- }
75
-
76
- export type SearchSerializer = (searchObj: Record<string, any>) => string
77
- export type SearchParser = (searchStr: string) => Record<string, any>
package/src/serializer.ts DELETED
@@ -1,24 +0,0 @@
1
- export interface StartSerializer {
2
- stringify: (obj: unknown) => string
3
- parse: (str: string) => unknown
4
- encode: <T>(value: T) => T
5
- decode: <T>(value: T) => T
6
- }
7
-
8
- export type SerializerStringifyBy<T, TSerializable> = T extends TSerializable
9
- ? T
10
- : T extends (...args: Array<any>) => any
11
- ? 'Function is not serializable'
12
- : { [K in keyof T]: SerializerStringifyBy<T[K], TSerializable> }
13
-
14
- export type SerializerParseBy<T, TSerializable> = T extends TSerializable
15
- ? T
16
- : T extends React.JSX.Element
17
- ? ReadableStream
18
- : { [K in keyof T]: SerializerParseBy<T[K], TSerializable> }
19
-
20
- export type Serializable = Date | undefined | Error | FormData
21
-
22
- export type SerializerStringify<T> = SerializerStringifyBy<T, Serializable>
23
-
24
- export type SerializerParse<T> = SerializerParseBy<T, Serializable>