diff --git a/packages/router-core/src/qss.ts b/packages/router-core/src/qss.ts index df6e24a7c3..303c83c595 100644 --- a/packages/router-core/src/qss.ts +++ b/packages/router-core/src/qss.ts @@ -38,21 +38,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 - */ -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. @@ -69,11 +54,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] } } diff --git a/packages/router-core/src/searchParams.ts b/packages/router-core/src/searchParams.ts index 740d36441c..cf70dc68bb 100644 --- a/packages/router-core/src/searchParams.ts +++ b/packages/router-core/src/searchParams.ts @@ -32,7 +32,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 } @@ -71,8 +74,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 } diff --git a/packages/router-core/tests/qss.test.ts b/packages/router-core/tests/qss.test.ts index ce92857ab5..fc228c779a 100644 --- a/packages/router-core/tests/qss.test.ts +++ b/packages/router-core/tests/qss.test.ts @@ -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', () => { diff --git a/packages/router-core/tests/searchParams.test.ts b/packages/router-core/tests/searchParams.test.ts index 006e119be5..9286331b53 100644 --- a/packages/router-core/tests/searchParams.test.ts +++ b/packages/router-core/tests/searchParams.test.ts @@ -2,43 +2,44 @@ 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"' }, '?foo=%22bar%22'], [{ 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: '"bar baz"' }, '?foo=%22bar+baz%22'], + [{ 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: '"undefined"' }, '?foo=%22undefined%22'], [{ foo: {} }, '?foo=%7B%7D'], - [{ foo: '{}' }, '?foo=%22%7B%7D%22'], + [{ foo: '"{}"' }, '?foo=%22%7B%7D%22'], [{ foo: [] }, '?foo=%5B%5D'], - [{ foo: '[]' }, '?foo=%22%5B%5D%22'], + [{ 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'], + [{ 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=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'], + [{ foo: '1000000000000001110' }, '?foo=1000000000000001110'], ])('isomorphism %j', (input, expected) => { const str = defaultStringifySearch(input) expect(str).toEqual(expected) - expect(defaultParseSearch(str)).toEqual(input) + const parsed = defaultParseSearch(str) + expect(parsed).toEqual(input) }) test('undefined values are removed during stringification', () => { @@ -67,8 +68,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: [{}] }],