-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Enhance URL query parsing functions (#80)
* feat: enhance query string parsing with new functions - Updated the parseQueryString function to return undefined instead of null for null inputs, improving consistency in return values. - Introduced parseQueryStringArray function to convert URL query parameter values into an array of strings, with error handling for invalid inputs. - Added parseQueryPositiveInts function to convert query parameter values into an array of positive integers, including robust error handling and type safety. - Enhanced documentation with detailed JSDoc comments for new functions, clarifying parameters, return values, and error handling scenarios. * feat: enhance URL query parameter parsing with detailed documentation - Updated parseQueryString, parseQueryStringArray, parseQueryNumber, parseQueryInt, and parseQueryPositiveInt functions to improve handling of null, undefined, and invalid inputs. - Added comprehensive JSDoc comments in both English and Chinese for all functions, clarifying parameters, return values, and error handling scenarios. - Introduced examples for each function to demonstrate usage and expected outcomes, enhancing overall documentation quality. * feat: add url-parse module for enhanced query parameter handling - Introduced a new module `url-parse.ts` with functions for parsing URL query parameters, including `parseQueryString`, `parseQueryStringArray`, `parseQueryNumber`, `parseQueryInt`, and `parseQueryPositiveInt`. - Added comprehensive JSDoc documentation in both English and Chinese, detailing function parameters, return values, and error handling. - Created unit tests in `url-parse.test.ts` to validate the functionality of the new parsing methods, ensuring robust handling of various input scenarios. - Updated `deno.json` to include the new `url-parse` module, enhancing the project's overall functionality. * feat: expand URL query parameter parsing with new functions and tests - Added new parsing functions: `parseQueryInt`, `parseQueryNumber`, `parseQueryPositiveInts`, and `parseQueryStringArray` to enhance query parameter handling. - Updated existing tests for `parseQueryPositiveInt` to improve error handling and consistency in return values. - Introduced comprehensive unit tests for the new functions, ensuring robust validation of various input scenarios. - Enhanced overall test coverage for URL query parameter parsing functionality.
- Loading branch information
Showing
5 changed files
with
376 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import { assertEquals, assertThrows } from '@std/assert'; | ||
import { | ||
parseQueryInt, | ||
parseQueryNumber, | ||
parseQueryPositiveInt, | ||
parseQueryPositiveInts, | ||
parseQueryString, | ||
parseQueryStringArray, | ||
} from './url-parse.ts'; | ||
|
||
Deno.test('parseQueryString', () => { | ||
const url = new URL( | ||
'https://example.com/path?a=1&b=2&c=&d=d&e=null&f=undefined', | ||
); | ||
url.searchParams.set('b', '中'); | ||
url.searchParams.set('g', ' '); | ||
url.searchParams.set('h', ';abc'); | ||
const a = url.searchParams.get('a'); | ||
const b = url.searchParams.get('b'); | ||
const c = url.searchParams.get('c'); | ||
const d = url.searchParams.get('d'); | ||
const e = url.searchParams.get('e'); | ||
const f = url.searchParams.get('f'); | ||
const g = url.searchParams.get('g'); | ||
console.log({ a, b, c, d, e, f, g }); | ||
assertEquals(parseQueryString(a), '1'); | ||
assertEquals(parseQueryString(b), '中'); | ||
assertEquals(parseQueryString(c), undefined); | ||
assertEquals(parseQueryString(d), 'd'); | ||
assertEquals(parseQueryString(e), 'null'); | ||
assertEquals(parseQueryString(f), undefined); | ||
assertEquals(parseQueryString(g), undefined); | ||
assertThrows(() => parseQueryString(';abc'), TypeError); | ||
}); | ||
|
||
Deno.test('parseQueryPositiveInt', () => { | ||
const url = new URL('https://example.com/path'); | ||
url.searchParams.set('a', '1'); | ||
url.searchParams.set('b', '2'); | ||
url.searchParams.set('c', ''); | ||
url.searchParams.set('d', '0'); | ||
url.searchParams.set('e', 'null'); | ||
url.searchParams.set('g', '-1'); | ||
url.searchParams.set('h', '1.5'); | ||
url.searchParams.set('i', 'abc'); | ||
const a = url.searchParams.get('a'); | ||
const b = url.searchParams.get('b'); | ||
const c = url.searchParams.get('c'); | ||
const d = url.searchParams.get('d'); | ||
const e = url.searchParams.get('e'); | ||
const g = url.searchParams.get('g'); | ||
const h = url.searchParams.get('h'); | ||
const i = url.searchParams.get('i'); | ||
|
||
assertEquals(parseQueryPositiveInt(a), 1); | ||
assertEquals(parseQueryPositiveInt(b), 2); | ||
assertEquals(parseQueryPositiveInt(c), undefined); | ||
assertThrows(() => parseQueryPositiveInt(d), TypeError); // 0 不是正整数 | ||
assertThrows(() => parseQueryPositiveInt(e), TypeError); | ||
assertThrows(() => parseQueryPositiveInt(g), TypeError); // 负数不是正整数 | ||
assertThrows(() => parseQueryPositiveInt(h), TypeError); // 小数不是整数 | ||
assertThrows(() => parseQueryPositiveInt(i), TypeError); // 非数字字符串 | ||
}); | ||
|
||
Deno.test('parseQueryStringArray', () => { | ||
const url = new URL('https://example.com/path'); | ||
url.searchParams.set('a', 'a,b,c'); | ||
url.searchParams.set('b', 'x|y|z'); | ||
url.searchParams.set('c', ''); | ||
url.searchParams.set('d', 'a,,c'); | ||
url.searchParams.set('e', '<script>,b,c'); | ||
|
||
assertEquals(parseQueryStringArray(url.searchParams.get('a')), [ | ||
'a', | ||
'b', | ||
'c', | ||
]); | ||
assertEquals( | ||
parseQueryStringArray(url.searchParams.get('b'), { separator: '|' }), | ||
['x', 'y', 'z'], | ||
); | ||
assertEquals(parseQueryStringArray(url.searchParams.get('c')), undefined); | ||
assertEquals(parseQueryStringArray(url.searchParams.get('d')), ['a', 'c']); | ||
assertThrows( | ||
() => parseQueryStringArray(url.searchParams.get('e')), | ||
TypeError, | ||
); | ||
}); | ||
|
||
Deno.test('parseQueryNumber', () => { | ||
const url = new URL('https://example.com/path'); | ||
url.searchParams.set('a', '123'); | ||
url.searchParams.set('b', '12.34'); | ||
url.searchParams.set('c', ''); | ||
url.searchParams.set('d', 'abc'); | ||
url.searchParams.set('e', 'Infinity'); | ||
url.searchParams.set('f', 'NaN'); | ||
|
||
assertEquals(parseQueryNumber(url.searchParams.get('a')), 123); | ||
assertEquals(parseQueryNumber(url.searchParams.get('b')), 12.34); | ||
assertEquals(parseQueryNumber(url.searchParams.get('c')), undefined); | ||
assertThrows(() => parseQueryNumber(url.searchParams.get('d')), TypeError); | ||
assertThrows(() => parseQueryNumber(url.searchParams.get('e')), TypeError); | ||
assertThrows(() => parseQueryNumber(url.searchParams.get('f')), TypeError); | ||
}); | ||
|
||
Deno.test('parseQueryInt', () => { | ||
const url = new URL('https://example.com/path'); | ||
url.searchParams.set('a', '123'); | ||
url.searchParams.set('b', '12.34'); | ||
url.searchParams.set('c', ''); | ||
url.searchParams.set('d', 'abc'); | ||
url.searchParams.set('e', '-123'); | ||
url.searchParams.set('f', '0'); | ||
|
||
assertEquals(parseQueryInt(url.searchParams.get('a')), 123); | ||
assertThrows(() => parseQueryInt(url.searchParams.get('b')), TypeError); | ||
assertEquals(parseQueryInt(url.searchParams.get('c')), undefined); | ||
assertThrows(() => parseQueryInt(url.searchParams.get('d')), TypeError); | ||
assertEquals(parseQueryInt(url.searchParams.get('e')), -123); | ||
assertEquals(parseQueryInt(url.searchParams.get('f')), 0); | ||
}); | ||
|
||
Deno.test('parseQueryPositiveInts', () => { | ||
const url = new URL('https://example.com/path'); | ||
url.searchParams.set('a', '1,2,3'); | ||
url.searchParams.set('b', ''); | ||
url.searchParams.set('c', '1,0,3'); | ||
url.searchParams.set('d', '1,-2,3'); | ||
url.searchParams.set('e', '1,abc,3'); | ||
|
||
assertEquals(parseQueryPositiveInts(url.searchParams.get('a')), [1, 2, 3]); | ||
assertEquals(parseQueryPositiveInts(url.searchParams.get('b')), undefined); | ||
assertThrows( | ||
() => parseQueryPositiveInts(url.searchParams.get('c')), | ||
TypeError, | ||
); | ||
assertThrows( | ||
() => parseQueryPositiveInts(url.searchParams.get('d')), | ||
TypeError, | ||
); | ||
assertThrows( | ||
() => parseQueryPositiveInts(url.searchParams.get('e')), | ||
TypeError, | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
import { toInt, toPositiveInt } from '../ts/number-type-convert.ts'; | ||
import { isSafeString, SafeString } from '../ts/string.ts'; | ||
|
||
/** | ||
* Convert URL query parameter value to string | ||
* 将 URL 查询参数值转换为字符串 | ||
* | ||
* @param query - URL query parameter value (typically from url.searchParams.get()) | ||
* URL 查询参数值(通常来自 url.searchParams.get()) | ||
* 可以为 null 或字符串类型 | ||
* @returns A SafeString or undefined | ||
* 返回安全字符串或 undefined | ||
* - Returns undefined if input is null, "undefined" or empty string | ||
* 当输入为 null、"undefined" 或空字符串时返回 undefined | ||
* - Returns trimmed SafeString for valid input | ||
* 对于有效输入返回经过去空格处理的安全字符串 | ||
* - Throws TypeError for invalid input containing unsafe characters | ||
* 当输入包含不安全字符时抛出 TypeError | ||
* | ||
* @example | ||
* parseQueryString('hello') // returns 'hello' | ||
* parseQueryString(null) // returns undefined | ||
* parseQueryString('') // returns undefined | ||
* parseQueryString('<script>') // throws TypeError | ||
* | ||
* @author iugo <[email protected]> | ||
*/ | ||
export function parseQueryString( | ||
query: string | null, | ||
): SafeString | undefined { | ||
if (query === null) { | ||
return undefined; | ||
} | ||
const trimmedQuery = query.trim(); | ||
if (trimmedQuery === 'undefined' || trimmedQuery === '') { | ||
return undefined; | ||
} | ||
if (!isSafeString(trimmedQuery)) { | ||
throw new TypeError(`invalid query string: ${query}`); | ||
} | ||
return trimmedQuery; | ||
} | ||
|
||
/** | ||
* Convert URL query parameter value to array of strings | ||
* 将 URL 查询参数值转换为字符串数组 | ||
* | ||
* @param query - URL query parameter value (typically from url.searchParams.get()) | ||
* URL 查询参数值(通常来自 url.searchParams.get()) | ||
* 可以为 null 或字符串类型 | ||
* @param options - Configuration options | ||
* 配置选项 | ||
* @param options.separator - Delimiter used to split the string (默认为逗号 ',') | ||
* 用于分割字符串的分隔符 | ||
* @returns An array of strings or undefined | ||
* 返回字符串数组或 undefined | ||
* - Returns undefined if input is null, "undefined", empty string or results in empty array | ||
* 当输入为 null、"undefined"、空字符串或结果为空数组时返回 undefined | ||
* - Returns array of non-empty strings for valid input | ||
* 对于有效输入返回非空字符串数组 | ||
* - Throws TypeError if any element contains unsafe characters | ||
* 当任何元素包含不安全字符时抛出 TypeError | ||
* | ||
* @example | ||
* parseQueryStringArray('a,b,c') // returns ['a', 'b', 'c'] | ||
* parseQueryStringArray('a|b|c', { separator: '|' }) // returns ['a', 'b', 'c'] | ||
* parseQueryStringArray('') // returns undefined | ||
* | ||
* @author iugo <[email protected]> | ||
*/ | ||
export function parseQueryStringArray( | ||
query: string | null, | ||
{ separator = ',' }: { separator?: string } = {}, | ||
): string[] | undefined { | ||
if (query === null || query === 'undefined' || query === '') { | ||
return undefined; | ||
} | ||
|
||
try { | ||
const arr = query | ||
.split(separator) | ||
.map((v) => parseQueryString(v) ?? '') | ||
.filter(Boolean); | ||
|
||
return arr.length === 0 ? undefined : arr; | ||
} catch (_err) { | ||
throw new TypeError(`invalid query string array: ${query}`); | ||
} | ||
} | ||
|
||
/** | ||
* Convert URL query parameter value to number | ||
* 将 URL 查询参数值转换为数字 | ||
* | ||
* @param query - URL query parameter value (typically from url.searchParams.get()) | ||
* URL 查询参数值(通常来自 url.searchParams.get()) | ||
* 可以为 null 或字符串类型 | ||
* @returns A number or undefined | ||
* 返回数字或 undefined | ||
* - Returns undefined if input is null, "undefined" or empty string | ||
* 当输入为 null、"undefined" 或空字符串时返回 undefined | ||
* - Returns parsed number for valid numeric input (包括整数和浮点数) | ||
* 对于有效的数字输入返回解析后的数字(包括整数和浮点数) | ||
* - Throws TypeError for non-numeric input or NaN/Infinity values | ||
* 当输入非数字或为 NaN/Infinity 时抛出 TypeError | ||
* | ||
* @example | ||
* parseQueryNumber('123') // returns 123 | ||
* parseQueryNumber('12.34') // returns 12.34 | ||
* parseQueryNumber('abc') // throws TypeError | ||
* | ||
* @author iugo <[email protected]> | ||
*/ | ||
export function parseQueryNumber( | ||
query: string | null, | ||
): number | undefined { | ||
if (query === null || query === 'undefined' || query === '') { | ||
return undefined; | ||
} | ||
const trimmedQuery = query.trim(); | ||
const num = Number(trimmedQuery); | ||
if (!Number.isFinite(num)) { | ||
throw new TypeError(`invalid query number: ${query}`); | ||
} | ||
return num; | ||
} | ||
|
||
/** | ||
* Convert URL query parameter value to integer | ||
* 将 URL 查询参数值转换为整数 | ||
* | ||
* @param query - URL query parameter value (typically from url.searchParams.get()) | ||
* URL 查询参数值(通常来自 url.searchParams.get()) | ||
* @returns A number or undefined | ||
* 返回整数或 undefined | ||
* - Returns undefined if input is null, "undefined" or empty string | ||
* 当输入为 null、"undefined" 或空字符串时返回 undefined | ||
* - Returns integer for valid numeric input | ||
* 对于有效的数字输入返回整数 | ||
* - Throws TypeError for invalid input | ||
* 当输入无效时抛出 TypeError | ||
* | ||
* @example | ||
* parseQueryInt('123') // returns 123 | ||
* parseQueryInt('12.34') // returns 12 | ||
* parseQueryInt('abc') // throws TypeError | ||
* | ||
* @author iugo <[email protected]> | ||
*/ | ||
export function parseQueryInt(query: string | null): number | undefined { | ||
if (query === null || query === 'undefined' || query === '') { | ||
return undefined; | ||
} | ||
const num = parseQueryNumber(query); | ||
if (num === undefined) { | ||
return undefined; | ||
} | ||
return toInt(num); | ||
} | ||
|
||
/** | ||
* Convert URL query parameter value to positive integer | ||
* 将 URL 查询参数值转换为正整数 | ||
* | ||
* @param query - URL query parameter value (typically from url.searchParams.get()) | ||
* URL 查询参数值(通常来自 url.searchParams.get()) | ||
* @returns A number or undefined | ||
* 返回正整数或 undefined | ||
* - Returns undefined if input is null, "undefined" or empty string | ||
* 当输入为 null、"undefined" 或空字符串时返回 undefined | ||
* - Returns positive integer for valid numeric input | ||
* 对于有效的数字输入返回正整数 | ||
* - Throws TypeError for invalid input or non-positive numbers | ||
* 当输入无效或非正数时抛出 TypeError | ||
* | ||
* @example | ||
* parseQueryPositiveInt('123') // returns 123 | ||
* parseQueryPositiveInt('-1') // throws TypeError | ||
* parseQueryPositiveInt('0') // throws TypeError | ||
* | ||
* @author iugo <[email protected]> | ||
*/ | ||
export function parseQueryPositiveInt( | ||
query: string | null, | ||
): number | undefined { | ||
if (query === null || query === 'undefined' || query === '') { | ||
return undefined; | ||
} | ||
const num = parseQueryNumber(query); | ||
if (num === undefined) { | ||
return undefined; | ||
} | ||
return toPositiveInt(num); | ||
} | ||
|
||
/** | ||
* Convert URL query parameter value to array of positive integers | ||
* 将 URL 查询参数值转换为正整数数组 | ||
* | ||
* @param query - URL query parameter value (typically from url.searchParams.get()) | ||
* URL 查询参数值(通常来自 url.searchParams.get()) | ||
* @returns An array of positive integers or undefined | ||
* 返回正整数数组或 undefined | ||
* - Returns undefined if input is null, "undefined" or empty string | ||
* 当输入为 null、"undefined" 或空字符串时返回 undefined | ||
* - Returns array of positive integers for valid input | ||
* 对于有效的数字输入返回正整数数组 | ||
* - Throws TypeError for invalid input | ||
* 当输入无效时抛出 TypeError | ||
* | ||
* @example | ||
* parseQueryPositiveInts('1,2,3') // returns [1, 2, 3] | ||
* parseQueryPositiveInts('') // returns undefined | ||
* parseQueryPositiveInts('1|2|3', { separator: '|' }) // returns [1, 2, 3] | ||
* | ||
* @author iugo <[email protected]> | ||
*/ | ||
export function parseQueryPositiveInts( | ||
query: string | null, | ||
): number[] | undefined { | ||
if (query === null || query === 'undefined' || query === '') { | ||
return undefined; | ||
} | ||
try { | ||
return query.split(',').map(toPositiveInt); | ||
} catch (_err) { | ||
throw new TypeError(`invalid query positive int array: ${query}`); | ||
} | ||
} |
Oops, something went wrong.