Skip to content

Commit

Permalink
feat: Add support for additional options in generateCookie function
Browse files Browse the repository at this point in the history
  • Loading branch information
KiraLT committed May 9, 2024
1 parent 6530373 commit 16dc60e
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 28 deletions.
56 changes: 45 additions & 11 deletions spec/http.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,21 @@ describe('HttpError', () => {

it('has expose', () => {
expect(
new HttpError(404, undefined, { expose: false }).expose
new HttpError(404, undefined, { expose: false }).expose,
).toBeFalsy()
expect(
new HttpError(500, undefined, { expose: true }).expose
new HttpError(500, undefined, { expose: true }).expose,
).toBeTruthy()
})

it('has public message', () => {
expect(new HttpError(404).publicMessage).toBe('Not Found')
expect(new HttpError(404, '404').publicMessage).toBe('404')
expect(new HttpError(404, '404', { expose: false }).publicMessage).toBe(
'Not Found'
'Not Found',
)
expect(new HttpError(500, '500').publicMessage).toBe(
'Internal Server Error'
'Internal Server Error',
)
})

Expand All @@ -83,7 +83,41 @@ describe('generateCookie', () => {

it('supports expiration', () => {
expect(generateCookie('a', 'b', { expires: 1 })).toMatch(
/^a=b;expires=.*GMT$/
/^a=b;expires=.*GMT$/,
)
})

it('supports path', () => {
expect(generateCookie('a', 'b', { path: '/' })).toBe('a=b;path=/')
})

it('supports domain', () => {
expect(generateCookie('a', 'b', { domain: 'domain.com' })).toBe(
'a=b;domain=domain.com',
)
})

it('supports secure', () => {
expect(generateCookie('a', 'b', { secure: true })).toBe('a=b;secure')
})

it('supports samesite', () => {
expect(generateCookie('a', 'b', { sameSite: 'strict' })).toBe(
'a=b;samesite=strict',
)
})

it('supports multiple options', () => {
expect(
generateCookie('a', 'b', {
expires: 1,
path: '/',
domain: 'domain.com',
secure: true,
sameSite: 'strict',
}),
).toMatch(
/^a=b;expires=.*GMT;path=\/;domain=domain.com;secure;samesite=strict$/,
)
})
})
Expand All @@ -109,13 +143,13 @@ describe('decodeHtml', () => {
describe('urlToRelative', () => {
it('converts absolute URL to relative', () => {
expect(urlToRelative('https://domain.com/index.html')).toBe(
'/index.html'
'/index.html',
)
})

it('supports subdomains', () => {
expect(urlToRelative('https://my-sub.domain.com/index.html')).toBe(
'/index.html'
'/index.html',
)
})
})
Expand Down Expand Up @@ -146,7 +180,7 @@ describe('parseQueryString', () => {
expect(
parseQueryString('page=1;limit=20', {
separator: ';',
})
}),
).toEqual({ page: ['1'], limit: ['20'] })
})

Expand All @@ -160,7 +194,7 @@ describe('parseQueryString', () => {
describe('generateQueryString', () => {
it('parses string', () => {
expect(generateQueryString({ page: [1], limit: 20 })).toBe(
'page=1&limit=20'
'page=1&limit=20',
)
})

Expand All @@ -170,8 +204,8 @@ describe('generateQueryString', () => {
{ page: [1], limit: 20 },
{
separator: ';',
}
)
},
),
).toBe('page=1;limit=20')
})
})
70 changes: 53 additions & 17 deletions src/http/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { flatMap, ensureArray, Primitive, isNullOrUndefined, isNot } from '../'
*
* // Create a cookie that expires 7 days from now, valid across the entire site:
* document.cookie = generateCookie('name', 'value', { expires: 7 })
*
* // Delete a cookie by setting the expires parameter to zero:
* document.cookie = generateCookie('name', 'value', { expires: 0 })
* ```
* @param name cookie name
* @param value cookie value
Expand All @@ -20,15 +23,45 @@ export function generateCookie(
options?: {
/**
* Expire cookie after x days (negative to remove cookie)
*
* @default undefined Cookie is removed when the user closes the browser
*/
expires?: number
}
/**
* A String indicating the path where the cookie is visible.
*
* @default undefined
*/
path?: string
/**
* A String indicating a valid domain where the cookie should be visible. The cookie will also be visible to all subdomains.
*
* @default undefined Cookie is visible only to the domain or subdomain of the page where the cookie was created
*/
domain?: string
/**
* A Boolean indicating if the cookie transmission requires a secure protocol (https).
*
* @default undefined No secure protocol requirement
*/
secure?: boolean
/**
* A String, allowing to control whether the browser is sending a cookie along with cross-site requests.
*
* @default undefined No SameSite attribute set
*/
sameSite?: 'strict' | 'lax' | 'none'
},
): string {
const { expires } = options ?? {}

return [
`${encodeURIComponent(name)}=${encodeURIComponent(value)}`,
expires ? `expires=${new Date(expires * 864e5).toUTCString()}` : '',
options?.path ? `path=${options.path}` : '',
options?.domain ? `domain=${options.domain}` : '',
options?.secure ? 'secure' : '',
options?.sameSite ? `samesite=${options.sameSite}` : '',
]
.filter((v) => !!v)
.join(';')
Expand All @@ -46,15 +79,18 @@ export function generateCookie(
* @param cookieString `document.cookie` value
*/
export function parseCookies(cookieString: string): Record<string, string> {
return cookieString.split(/; /).reduce((prev, cur) => {
const [name, value] = cur.split('=', 2)
if (name != null && value != null) {
prev[decodeURIComponent(name)] = decodeURIComponent(
value[0] === '"' ? value.slice(1, -1) : value
)
}
return prev
}, {} as Record<string, string>)
return cookieString.split(/; /).reduce(
(prev, cur) => {
const [name, value] = cur.split('=', 2)
if (name != null && value != null) {
prev[decodeURIComponent(name)] = decodeURIComponent(
value[0] === '"' ? value.slice(1, -1) : value,
)
}
return prev
},
{} as Record<string, string>,
)
}

/**
Expand All @@ -77,7 +113,7 @@ export function parseQueryString(
* @default `&`
*/
separator?: string
}
},
): Record<string, string[]> {
const { separator = '&' } = options ?? {}

Expand All @@ -98,13 +134,13 @@ export function parseQueryString(
}

return []
}
},
).reduce<Record<string, string[]>>(
(prev, [key, value]) => ({
...prev,
[key]: [...(prev[key] || []), ...value],
}),
{}
{},
)
}

Expand All @@ -128,7 +164,7 @@ export function generateQueryString(
* @default `&`
*/
separator?: string
}
},
): string {
const { separator = '&' } = options ?? {}
return flatMap(Object.entries(query), ([key, values]) =>
Expand All @@ -137,9 +173,9 @@ export function generateQueryString(
.map(
(v) =>
`${encodeURIComponent(key.toString())}=${encodeURIComponent(
v.toString()
).replace(/%20/g, '+')}`
)
v.toString(),
).replace(/%20/g, '+')}`,
),
).join(separator)
}

Expand Down

0 comments on commit 16dc60e

Please sign in to comment.