From 538682edf817424da67e3ae35f6268e659dbf073 Mon Sep 17 00:00:00 2001 From: Martin Klepsch Date: Wed, 13 Mar 2024 22:16:32 +0200 Subject: [PATCH] use unicode-resilient approach to exclude sub delimiters --- src/encoding.ts | 22 ++++++++++++++++++---- test/main.test.ts | 11 +++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/encoding.ts b/src/encoding.ts index 8f27702..9df22fc 100644 --- a/src/encoding.ts +++ b/src/encoding.ts @@ -13,8 +13,6 @@ / "*" / "+" / "," / ";" / "=" */ -const excludeSubDelimiters = /[^!$'()*+,;|:]/g - export type URLParamsEncodingType = | 'default' | 'uri' @@ -22,8 +20,24 @@ export type URLParamsEncodingType = | 'none' | 'legacy' -export const encodeURIComponentExcludingSubDelims = (segment: string): string => - segment.replace(excludeSubDelimiters, match => encodeURIComponent(match)) +export const encodeURIComponentExcludingSubDelims = ( + segment: string +): string => { + // Define sub-delimiters to exclude from encoding + const subDelimiters = /[!$'()*+,;|:]/g + + // Use Array.from to correctly handle characters represented by surrogate pairs + return Array.from(segment) + .map(char => { + // Check if the character is a sub-delimiter + if (subDelimiters.test(char)) { + return char // Return the character as is if it's a sub-delimiter + } else { + return encodeURIComponent(char) // Otherwise, encode it + } + }) + .join('') // Join the array back into a string +} const encodingMethods: Record< URLParamsEncodingType, diff --git a/test/main.test.ts b/test/main.test.ts index c997e06..de8a1fc 100644 --- a/test/main.test.ts +++ b/test/main.test.ts @@ -50,6 +50,17 @@ describe('Path', () => { ) }) + it('should match and build paths with emojis in query parameters', () => { + const pathA = new Path('/home?emoji') + + expect(pathA.build({ emoji: '🤗' })).toEqual('/home?emoji=%F0%9F%A4%97') + + const path = new Path('/home?emoji') + expect(path.test('/home?emoji=%F0%9F%99%8C')).toEqual({ + emoji: '🙌' + }) + }) + it('should match and build paths with query parameters', () => { const path = new Path('/users?offset&limit', { queryParams: { booleanFormat: 'string' }