Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 3 additions & 20 deletions packages/router-core/src/qss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,6 @@ export function encode(
return result.toString()
}

/**
* Converts a string value to its appropriate type (string, number, boolean).
* @param mix - The string value to convert.
* @returns The converted value.
* @example
* // Example input: toValue("123")
* // Expected output: 123
*/
/** Convert a string into a primitive boolean/number when possible. */
function toValue(str: unknown) {
if (!str) return ''

if (str === 'false') return false
if (str === 'true') return true
return +str * 0 === 0 && +str + '' === str ? +str : str
}

/**
* Decodes a query string into an object.
* @param str - The query string to decode.
Expand All @@ -73,11 +56,11 @@ export function decode(str: any): any {
for (const [key, value] of searchParams.entries()) {
const previousValue = result[key]
if (previousValue == null) {
result[key] = toValue(value)
result[key] = value
} else if (Array.isArray(previousValue)) {
previousValue.push(toValue(value))
previousValue.push(value)
} else {
result[key] = [previousValue, toValue(value)]
result[key] = [previousValue, value]
}
}

Expand Down
11 changes: 8 additions & 3 deletions packages/router-core/src/searchParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ export function parseSearchWith(parser: (str: string) => any) {
const value = query[key]
if (typeof value === 'string') {
try {
query[key] = parser(value)
const parsed = parser(value)
if (parsed && typeof parsed === 'object') {
query[key] = parsed
}
} catch (_err) {
// silent
}
Expand Down Expand Up @@ -75,8 +78,10 @@ export function stringifySearchWith(
try {
// Check if it's a valid parseable string.
// If it is, then stringify it again.
parser(val)
return stringify(val)
const parsed = parser(val)
if (parsed && typeof parsed === 'object') {
return stringify(val)
}
} catch (_err) {
// silent
}
Expand Down
2 changes: 1 addition & 1 deletion packages/router-core/tests/qss.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('decode function', () => {
it('should handle decoding a top-level key with a special character', () => {
const queryString = 'foo%3Dbar=1'
const decodedObj = decode(queryString)
expect(decodedObj).toEqual({ 'foo=bar': 1 })
expect(decodedObj).toEqual({ 'foo=bar': '1' })
})

it('should handle decoding a top-level key with a special character and without a value', () => {
Expand Down
76 changes: 38 additions & 38 deletions packages/router-core/tests/searchParams.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,43 @@ import { describe, expect, test } from 'vitest'
import { defaultParseSearch, defaultStringifySearch } from '../src'

describe('Search Params serialization and deserialization', () => {
/*
* JSON-compatible objects can be serialized into a string,
* and then deserialized back into the original object.
*/
test.each([
[{}, ''],
[{ foo: '' }, '?foo='],
[{ foo: 'bar' }, '?foo=bar'],
[{ foo: 'bar baz' }, '?foo=bar+baz'],
[{ foo: 123 }, '?foo=123'],
[{ foo: '123' }, '?foo=%22123%22'],
[{ foo: true }, '?foo=true'],
[{ foo: 'true' }, '?foo=%22true%22'],
[{ foo: null }, '?foo=null'],
[{ foo: 'null' }, '?foo=%22null%22'],
[{ foo: 'undefined' }, '?foo=undefined'],
[{ foo: {} }, '?foo=%7B%7D'],
[{ foo: '{}' }, '?foo=%22%7B%7D%22'],
[{ foo: [] }, '?foo=%5B%5D'],
[{ foo: '[]' }, '?foo=%22%5B%5D%22'],
[{ foo: [1, 2, 3] }, '?foo=%5B1%2C2%2C3%5D'],
[{ foo: '1,2,3' }, '?foo=1%2C2%2C3'],
[{ foo: { bar: 'baz' } }, '?foo=%7B%22bar%22%3A%22baz%22%7D'],
[{ 0: 1 }, '?0=1'],
[{ 'foo=bar': 1 }, '?foo%3Dbar=1'],
[{ '{}': 1 }, '?%7B%7D=1'],
[{ '': 1 }, '?=1'],
[{ '=': '=' }, '?%3D=%3D'],
[{ '=': '', '': '=' }, '?%3D=&=%3D'],
[{ 'foo=2&bar': 3 }, '?foo%3D2%26bar=3'],
[{ 'foo?': 1 }, '?foo%3F=1'],
[{ foo: 'bar=' }, '?foo=bar%3D'],
[{ foo: '2&bar=3' }, '?foo=2%26bar%3D3'],
])('isomorphism %j', (input, expected) => {
const str = defaultStringifySearch(input)
expect(str).toEqual(expected)
expect(defaultParseSearch(str)).toEqual(input)
['', {}],
['?foo=', { foo: '' }],
['?foo=bar', { foo: 'bar' }],
['?foo=%22bar%22', { foo: '"bar"' }],
['?foo=bar+baz', { foo: 'bar baz' }],
['?foo=%22bar+baz%22', { foo: '"bar baz"' }],
['?foo=123', { foo: '123' }],
['?foo=%22123%22', { foo: '"123"' }],
['?foo=true', { foo: 'true' }],
['?foo=%22true%22', { foo: '"true"' }],
['?foo=null', { foo: 'null' }],
['?foo=%22null%22', { foo: '"null"' }],
['?foo=undefined', { foo: 'undefined' }],
['?foo=%22undefined%22', { foo: '"undefined"' }],
['?foo=%7B%7D', { foo: {} }],
['?foo=%22%7B%7D%22', { foo: '"{}"' }],
['?foo=%5B%5D', { foo: [] }],
['?foo=%22%5B%5D%22', { foo: '"[]"' }],
['?foo=%5B1%2C2%2C3%5D', { foo: [1, 2, 3] }],
['?foo=1%2C2%2C3', { foo: '1,2,3' }],
['?foo=%7B%22bar%22%3A%22baz%22%7D', { foo: { bar: 'baz' } }],
['?0=1', { 0: '1' }],
['?foo%3Dbar=1', { 'foo=bar': '1' }],
['?%7B%7D=1', { '{}': '1' }],
['?=1', { '': '1' }],
['?%3D=%3D', { '=': '=' }],
['?%3D=&=%3D', { '=': '', '': '=' }],
['?foo%3D2%26bar=3', { 'foo=2&bar': '3' }],
['?foo%3F=1', { 'foo?': '1' }],
['?foo=bar%3D', { foo: 'bar=' }],
['?foo=2%26bar%3D3', { foo: '2&bar=3' }],
])('isomorphism %j', (input, expectedObj) => {
const parsed = defaultParseSearch(input)
expect(parsed).toEqual(expectedObj)
const str = defaultStringifySearch(parsed)
expect(str).toEqual(input)
})

test('undefined values are removed during stringification', () => {
Expand Down Expand Up @@ -67,8 +67,8 @@ describe('Search Params serialization and deserialization', () => {
['?foo=[]', { foo: [] }],
['?foo=1,2,3', { foo: '1,2,3' }],
['?foo={"bar":"baz"}', { foo: { bar: 'baz' } }],
['?foo=1&foo=2', { foo: [1, 2] }],
['?foo=""', { foo: '' }],
['?foo=1&foo=2', { foo: ['1', '2'] }],
['?foo=""', { foo: '""' }],
['?foo=""""', { foo: '""""' }],
['?foo=()', { foo: '()' }],
['?foo=[{}]', { foo: [{}] }],
Expand Down