@tanstack/router-core 0.0.1-beta.9 → 1.97.24

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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/cjs/Matches.cjs +13 -0
  4. package/dist/cjs/Matches.cjs.map +1 -0
  5. package/dist/cjs/Matches.d.cts +28 -0
  6. package/dist/cjs/RouterProvider.d.cts +18 -0
  7. package/dist/cjs/defer.cjs +25 -0
  8. package/dist/cjs/defer.cjs.map +1 -0
  9. package/dist/cjs/defer.d.cts +20 -0
  10. package/dist/cjs/index.cjs +50 -0
  11. package/dist/cjs/index.cjs.map +1 -0
  12. package/dist/cjs/index.d.cts +25 -0
  13. package/dist/cjs/link.cjs +5 -0
  14. package/dist/cjs/link.cjs.map +1 -0
  15. package/dist/cjs/link.d.cts +51 -0
  16. package/dist/cjs/location.d.cts +12 -0
  17. package/dist/cjs/manifest.d.cts +24 -0
  18. package/dist/cjs/path.cjs +289 -0
  19. package/dist/cjs/path.cjs.map +1 -0
  20. package/dist/cjs/path.d.cts +34 -0
  21. package/dist/cjs/qss.cjs +51 -0
  22. package/dist/cjs/qss.cjs.map +1 -0
  23. package/dist/cjs/qss.d.cts +27 -0
  24. package/dist/cjs/root.cjs +5 -0
  25. package/dist/cjs/root.cjs.map +1 -0
  26. package/dist/cjs/root.d.cts +2 -0
  27. package/dist/cjs/route.d.cts +148 -0
  28. package/dist/cjs/router.cjs +19 -0
  29. package/dist/cjs/router.cjs.map +1 -0
  30. package/dist/cjs/router.d.cts +31 -0
  31. package/dist/cjs/searchMiddleware.cjs +42 -0
  32. package/dist/cjs/searchMiddleware.cjs.map +1 -0
  33. package/dist/cjs/searchMiddleware.d.cts +5 -0
  34. package/dist/cjs/searchParams.cjs +61 -0
  35. package/dist/cjs/searchParams.cjs.map +1 -0
  36. package/dist/cjs/searchParams.d.cts +7 -0
  37. package/dist/cjs/serializer.d.cts +15 -0
  38. package/dist/cjs/structuralSharing.d.cts +4 -0
  39. package/dist/cjs/utils.cjs +155 -0
  40. package/dist/cjs/utils.cjs.map +1 -0
  41. package/dist/cjs/utils.d.cts +81 -0
  42. package/dist/cjs/validators.d.cts +51 -0
  43. package/dist/esm/Matches.d.ts +28 -0
  44. package/dist/esm/Matches.js +13 -0
  45. package/dist/esm/Matches.js.map +1 -0
  46. package/dist/esm/RouterProvider.d.ts +18 -0
  47. package/dist/esm/defer.d.ts +20 -0
  48. package/dist/esm/defer.js +25 -0
  49. package/dist/esm/defer.js.map +1 -0
  50. package/dist/esm/index.d.ts +25 -0
  51. package/dist/esm/index.js +50 -0
  52. package/dist/esm/index.js.map +1 -0
  53. package/dist/esm/link.d.ts +51 -0
  54. package/dist/esm/link.js +5 -0
  55. package/dist/esm/link.js.map +1 -0
  56. package/dist/esm/location.d.ts +12 -0
  57. package/dist/esm/manifest.d.ts +24 -0
  58. package/dist/esm/path.d.ts +34 -0
  59. package/dist/esm/path.js +289 -0
  60. package/dist/esm/path.js.map +1 -0
  61. package/dist/esm/qss.d.ts +27 -0
  62. package/dist/esm/qss.js +51 -0
  63. package/dist/esm/qss.js.map +1 -0
  64. package/dist/esm/root.d.ts +2 -0
  65. package/dist/esm/root.js +5 -0
  66. package/dist/esm/root.js.map +1 -0
  67. package/dist/esm/route.d.ts +148 -0
  68. package/dist/esm/router.d.ts +31 -0
  69. package/dist/esm/router.js +19 -0
  70. package/dist/esm/router.js.map +1 -0
  71. package/dist/esm/searchMiddleware.d.ts +5 -0
  72. package/dist/esm/searchMiddleware.js +42 -0
  73. package/dist/esm/searchMiddleware.js.map +1 -0
  74. package/dist/esm/searchParams.d.ts +7 -0
  75. package/dist/esm/searchParams.js +61 -0
  76. package/dist/esm/searchParams.js.map +1 -0
  77. package/dist/esm/serializer.d.ts +15 -0
  78. package/dist/esm/structuralSharing.d.ts +4 -0
  79. package/dist/esm/utils.d.ts +81 -0
  80. package/dist/esm/utils.js +155 -0
  81. package/dist/esm/utils.js.map +1 -0
  82. package/dist/esm/validators.d.ts +51 -0
  83. package/package.json +35 -32
  84. package/src/Matches.ts +94 -0
  85. package/src/RouterProvider.ts +20 -0
  86. package/src/defer.ts +52 -0
  87. package/src/index.ts +206 -19
  88. package/src/link.ts +122 -300
  89. package/src/location.ts +13 -0
  90. package/src/manifest.ts +32 -0
  91. package/src/path.ts +238 -47
  92. package/src/qss.ts +56 -19
  93. package/src/root.ts +2 -0
  94. package/src/route.ts +296 -223
  95. package/src/router.ts +34 -1281
  96. package/src/searchMiddleware.ts +54 -0
  97. package/src/searchParams.ts +43 -20
  98. package/src/serializer.ts +24 -0
  99. package/src/structuralSharing.ts +7 -0
  100. package/src/utils.ts +314 -75
  101. package/src/validators.ts +121 -0
  102. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -33
  103. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  104. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
  105. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
  106. package/build/cjs/node_modules/history/index.js +0 -815
  107. package/build/cjs/node_modules/history/index.js.map +0 -1
  108. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
  109. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
  110. package/build/cjs/packages/router-core/src/index.js +0 -58
  111. package/build/cjs/packages/router-core/src/index.js.map +0 -1
  112. package/build/cjs/packages/router-core/src/path.js +0 -222
  113. package/build/cjs/packages/router-core/src/path.js.map +0 -1
  114. package/build/cjs/packages/router-core/src/qss.js +0 -71
  115. package/build/cjs/packages/router-core/src/qss.js.map +0 -1
  116. package/build/cjs/packages/router-core/src/route.js +0 -150
  117. package/build/cjs/packages/router-core/src/route.js.map +0 -1
  118. package/build/cjs/packages/router-core/src/routeConfig.js +0 -69
  119. package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
  120. package/build/cjs/packages/router-core/src/routeMatch.js +0 -266
  121. package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
  122. package/build/cjs/packages/router-core/src/router.js +0 -822
  123. package/build/cjs/packages/router-core/src/router.js.map +0 -1
  124. package/build/cjs/packages/router-core/src/searchParams.js +0 -70
  125. package/build/cjs/packages/router-core/src/searchParams.js.map +0 -1
  126. package/build/cjs/packages/router-core/src/utils.js +0 -125
  127. package/build/cjs/packages/router-core/src/utils.js.map +0 -1
  128. package/build/esm/index.js +0 -2481
  129. package/build/esm/index.js.map +0 -1
  130. package/build/stats-html.html +0 -4034
  131. package/build/stats-react.json +0 -493
  132. package/build/types/index.d.ts +0 -618
  133. package/build/umd/index.development.js +0 -2514
  134. package/build/umd/index.development.js.map +0 -1
  135. package/build/umd/index.production.js +0 -12
  136. package/build/umd/index.production.js.map +0 -1
  137. package/src/frameworks.ts +0 -12
  138. package/src/routeConfig.ts +0 -495
  139. package/src/routeInfo.ts +0 -228
  140. package/src/routeMatch.ts +0 -374
package/src/path.ts CHANGED
@@ -1,14 +1,20 @@
1
- import { AnyPathParams } from './routeConfig'
2
- import { MatchLocation } from './router'
3
1
  import { last } from './utils'
2
+ import type { MatchLocation } from './RouterProvider'
3
+ import type { AnyPathParams } from './route'
4
4
 
5
5
  export interface Segment {
6
6
  type: 'pathname' | 'param' | 'wildcard'
7
7
  value: string
8
8
  }
9
9
 
10
- export function joinPaths(paths: (string | undefined)[]) {
11
- return cleanPath(paths.filter(Boolean).join('/'))
10
+ export function joinPaths(paths: Array<string | undefined>) {
11
+ return cleanPath(
12
+ paths
13
+ .filter((val) => {
14
+ return val !== undefined
15
+ })
16
+ .join('/'),
17
+ )
12
18
  }
13
19
 
14
20
  export function cleanPath(path: string) {
@@ -28,13 +34,79 @@ export function trimPath(path: string) {
28
34
  return trimPathRight(trimPathLeft(path))
29
35
  }
30
36
 
31
- export function resolvePath(basepath: string, base: string, to: string) {
32
- base = base.replace(new RegExp(`^${basepath}`), '/')
33
- to = to.replace(new RegExp(`^${basepath}`), '/')
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)
34
102
 
35
103
  let baseSegments = parsePathname(base)
36
104
  const toSegments = parsePathname(to)
37
105
 
106
+ if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
107
+ baseSegments.pop()
108
+ }
109
+
38
110
  toSegments.forEach((toSegment, index) => {
39
111
  if (toSegment.value === '/') {
40
112
  if (!index) {
@@ -47,31 +119,36 @@ export function resolvePath(basepath: string, base: string, to: string) {
47
119
  // ignore inter-slashes
48
120
  }
49
121
  } else if (toSegment.value === '..') {
50
- // Extra trailing slash? pop it off
51
- if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
52
- baseSegments.pop()
53
- }
54
122
  baseSegments.pop()
55
123
  } else if (toSegment.value === '.') {
56
- return
124
+ // ignore
57
125
  } else {
58
126
  baseSegments.push(toSegment)
59
127
  }
60
128
  })
61
129
 
62
- const joined = joinPaths([basepath, ...baseSegments.map((d) => d.value)])
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
+ }
63
139
 
140
+ const joined = joinPaths([basepath, ...baseSegments.map((d) => d.value)])
64
141
  return cleanPath(joined)
65
142
  }
66
143
 
67
- export function parsePathname(pathname?: string): Segment[] {
144
+ export function parsePathname(pathname?: string): Array<Segment> {
68
145
  if (!pathname) {
69
146
  return []
70
147
  }
71
148
 
72
149
  pathname = cleanPath(pathname)
73
150
 
74
- const segments: Segment[] = []
151
+ const segments: Array<Segment> = []
75
152
 
76
153
  if (pathname.slice(0, 1) === '/') {
77
154
  pathname = pathname.substring(1)
@@ -90,14 +167,14 @@ export function parsePathname(pathname?: string): Segment[] {
90
167
 
91
168
  segments.push(
92
169
  ...split.map((part): Segment => {
93
- if (part.startsWith('*')) {
170
+ if (part === '$' || part === '*') {
94
171
  return {
95
172
  type: 'wildcard',
96
173
  value: part,
97
174
  }
98
175
  }
99
176
 
100
- if (part.charAt(0) === ':') {
177
+ if (part.charAt(0) === '$') {
101
178
  return {
102
179
  type: 'param',
103
180
  value: part,
@@ -106,7 +183,7 @@ export function parsePathname(pathname?: string): Segment[] {
106
183
 
107
184
  return {
108
185
  type: 'pathname',
109
- value: part,
186
+ value: decodeURI(part),
110
187
  }
111
188
  }),
112
189
  )
@@ -122,21 +199,52 @@ export function parsePathname(pathname?: string): Segment[] {
122
199
  return segments
123
200
  }
124
201
 
125
- export function interpolatePath(
126
- path: string | undefined,
127
- params: any,
128
- leaveWildcard?: boolean,
129
- ) {
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) {
130
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
+ }
131
233
 
132
234
  return joinPaths(
133
235
  interpolatedPathSegments.map((segment) => {
134
- if (segment.value === '*' && !leaveWildcard) {
135
- return ''
236
+ if (segment.type === 'wildcard') {
237
+ const value = encodedParams._splat
238
+ if (leaveWildcards) return `${segment.value}${value ?? ''}`
239
+ return value
136
240
  }
137
241
 
138
242
  if (segment.type === 'param') {
139
- return params![segment.value.substring(1)] ?? ''
243
+ if (leaveParams) {
244
+ const value = encodedParams[segment.value]
245
+ return `${segment.value}${value ?? ''}`
246
+ }
247
+ return encodedParams![segment.value.substring(1)] ?? 'undefined'
140
248
  }
141
249
 
142
250
  return segment.value
@@ -144,34 +252,111 @@ export function interpolatePath(
144
252
  )
145
253
  }
146
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
+
147
265
  export function matchPathname(
266
+ basepath: string,
148
267
  currentPathname: string,
149
268
  matchLocation: Pick<MatchLocation, 'to' | 'fuzzy' | 'caseSensitive'>,
150
269
  ): AnyPathParams | undefined {
151
- const pathParams = matchByPath(currentPathname, matchLocation)
152
- // const searchMatched = matchBySearch(currentLocation.search, matchLocation)
270
+ const pathParams = matchByPath(basepath, currentPathname, matchLocation)
271
+ // const searchMatched = matchBySearch(location.search, matchLocation)
153
272
 
154
273
  if (matchLocation.to && !pathParams) {
155
274
  return
156
275
  }
157
276
 
158
- // if (matchLocation.search && !searchMatched) {
159
- // return
160
- // }
161
-
162
277
  return pathParams ?? {}
163
278
  }
164
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
+
165
321
  export function matchByPath(
322
+ basepath: string,
166
323
  from: string,
167
324
  matchLocation: Pick<MatchLocation, 'to' | 'caseSensitive' | 'fuzzy'>,
168
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
169
340
  const baseSegments = parsePathname(from)
170
- const routeSegments = parsePathname(`${matchLocation.to ?? '*'}`)
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
+ }
171
356
 
172
357
  const params: Record<string, string> = {}
173
358
 
174
- let isMatch = (() => {
359
+ const isMatch = (() => {
175
360
  for (
176
361
  let i = 0;
177
362
  i < Math.max(baseSegments.length, routeSegments.length);
@@ -180,16 +365,18 @@ export function matchByPath(
180
365
  const baseSegment = baseSegments[i]
181
366
  const routeSegment = routeSegments[i]
182
367
 
183
- const isLastRouteSegment = i === routeSegments.length - 1
184
- const isLastBaseSegment = i === baseSegments.length - 1
368
+ const isLastBaseSegment = i >= baseSegments.length - 1
369
+ const isLastRouteSegment = i >= routeSegments.length - 1
185
370
 
186
371
  if (routeSegment) {
187
372
  if (routeSegment.type === 'wildcard') {
188
- if (baseSegment?.value) {
189
- params['*'] = joinPaths(baseSegments.slice(i).map((d) => d.value))
190
- return true
191
- }
192
- return false
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
193
380
  }
194
381
 
195
382
  if (routeSegment.type === 'pathname') {
@@ -216,21 +403,25 @@ export function matchByPath(
216
403
  }
217
404
 
218
405
  if (routeSegment.type === 'param') {
219
- if (baseSegment?.value === '/') {
406
+ if (baseSegment.value === '/') {
220
407
  return false
221
408
  }
222
- if (!baseSegment.value.startsWith(':')) {
223
- params[routeSegment.value.substring(1)] = baseSegment.value
409
+ if (baseSegment.value.charAt(0) !== '$') {
410
+ params[routeSegment.value.substring(1)] = decodeURIComponent(
411
+ baseSegment.value,
412
+ )
224
413
  }
225
414
  }
226
415
  }
227
416
 
228
- if (isLastRouteSegment && !isLastBaseSegment) {
229
- return !!matchLocation.fuzzy
417
+ if (!isLastBaseSegment && isLastRouteSegment) {
418
+ params['**'] = joinPaths(baseSegments.slice(i + 1).map((d) => d.value))
419
+ return !!matchLocation.fuzzy && routeSegment?.value !== '/'
230
420
  }
231
421
  }
422
+
232
423
  return true
233
424
  })()
234
425
 
235
- return isMatch ? (params as Record<string, string>) : undefined
426
+ return isMatch ? params : undefined
236
427
  }
package/src/qss.ts CHANGED
@@ -1,9 +1,22 @@
1
- // @ts-nocheck
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
+ */
2
6
 
3
- // qss has been slightly modified and inlined here for our use cases (and compression's sake). We've included it as a hard dependency for MIT license attribution.
4
-
5
- export function encode(obj, pfx?: string) {
6
- var k,
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,
7
20
  i,
8
21
  tmp,
9
22
  str = ''
@@ -25,28 +38,52 @@ export function encode(obj, pfx?: string) {
25
38
  return (pfx || '') + str
26
39
  }
27
40
 
28
- function toValue(mix) {
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) {
29
50
  if (!mix) return ''
30
- var str = decodeURIComponent(mix)
51
+ const str = decodeURIComponent(mix)
31
52
  if (str === 'false') return false
32
53
  if (str === 'true') return true
33
- if (str.charAt(0) === '0') return str
34
- return +str * 0 === 0 ? +str : str
54
+ return +str * 0 === 0 && +str + '' === str ? +str : str
35
55
  }
36
56
 
37
- export function decode(str) {
38
- var tmp,
39
- k,
40
- out = {},
41
- arr = str.split('&')
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('&')
42
70
 
43
71
  while ((tmp = arr.shift())) {
44
- tmp = tmp.split('=')
45
- k = tmp.shift()
46
- if (out[k] !== void 0) {
47
- out[k] = [].concat(out[k], toValue(tmp.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
+ }
48
83
  } else {
49
- out[k] = toValue(tmp.shift())
84
+ k = tmp
85
+ k = decodeURIComponent(k)
86
+ out[k] = ''
50
87
  }
51
88
  }
52
89
 
package/src/root.ts ADDED
@@ -0,0 +1,2 @@
1
+ export const rootRouteId = '__root__'
2
+ export type RootRouteId = typeof rootRouteId