diff --git a/lib/__tests__/cookieJar.spec.ts b/lib/__tests__/cookieJar.spec.ts index d3f591d2..2a37d0b8 100644 --- a/lib/__tests__/cookieJar.spec.ts +++ b/lib/__tests__/cookieJar.spec.ts @@ -34,7 +34,6 @@ import { CookieJar } from '../cookie/cookieJar' import type { SerializedCookieJar } from '../cookie/constants' import { MemoryCookieStore } from '../memstore' import { Store } from '../store' -import { ParameterError } from '../validators' // ported from: // - test/api_test.js (cookie jar tests) @@ -1173,7 +1172,7 @@ it('should fix issue #145 - missing 2nd url parameter', () => { expect( // @ts-expect-error test case explicitly violates the expected function signature () => cookieJar.setCookie('x=y; Domain=example.com; Path=/'), - ).toThrow(ParameterError) + ).toThrowError('`url` argument is not a string or URL.') }) it('should fix issue #197 - CookieJar().setCookie throws an error when empty cookie is passed', async () => { @@ -1241,6 +1240,21 @@ it('should fix issue #154 - Expiry should not be affected by creation date', asy expect(updatedCookies[0]?.expiryTime()).toBe(now + 60 * 1000 + 1000) }) +it('should fix issue #261 - URL objects should be accepted in setCookie', async () => { + const jar = new CookieJar() + const url = new URL('https://example.com') + await jar.setCookie('foo=bar; Max-Age=60;', url) + const cookies = await jar.getCookies(url) + expect(cookies).toEqual([ + expect.objectContaining({ + key: 'foo', + value: 'bar', + path: '/', + domain: 'example.com', + }), + ]) +}) + // special use domains under a sub-domain describe.each(['local', 'example', 'invalid', 'localhost', 'test'])( 'when special use domain is dev.%s', diff --git a/lib/cookie/cookieJar.ts b/lib/cookie/cookieJar.ts index 6b60e099..685f6ba0 100644 --- a/lib/cookie/cookieJar.ts +++ b/lib/cookie/cookieJar.ts @@ -2,14 +2,15 @@ import urlParse from 'url-parse' import { getPublicSuffix } from '../getPublicSuffix' import * as validators from '../validators' +import { ParameterError } from '../validators' import { Store } from '../store' import { MemoryCookieStore } from '../memstore' import { pathMatch } from '../pathMatch' import { Cookie } from './cookie' import { Callback, - ErrorCallback, createPromiseCallback, + ErrorCallback, inOperator, safeToString, } from '../utils' @@ -68,20 +69,18 @@ type CreateCookieJarOptions = { const SAME_SITE_CONTEXT_VAL_ERR = 'Invalid sameSiteContext option for getCookies(); expected one of "strict", "lax", or "none"' -function getCookieContext(url: string | URL) { - if (url instanceof URL && 'query' in url) { +function getCookieContext(url: unknown) { + if (url instanceof URL) { return url - } - - if (typeof url === 'string') { + } else if (typeof url === 'string') { try { return urlParse(decodeURI(url)) } catch { return urlParse(url) } + } else { + throw new ParameterError('`url` argument is not a string or URL.') } - - throw new Error('`url` argument is invalid') } function checkSameSiteContext(value: string) { @@ -197,29 +196,29 @@ export class CookieJar { // return `undefined` when `ignoreError` is true. But would that be excessive overloading? setCookie( cookie: string | Cookie, - url: string, + url: string | URL, callback: Callback, ): void setCookie( cookie: string | Cookie, - url: string, + url: string | URL, options: SetCookieOptions, callback: Callback, ): void setCookie( cookie: string | Cookie, - url: string, + url: string | URL, options?: SetCookieOptions, ): Promise setCookie( cookie: string | Cookie, - url: string, + url: string | URL, options: SetCookieOptions | Callback, callback?: Callback, ): unknown setCookie( cookie: string | Cookie, - url: string, + url: string | URL, options?: SetCookieOptions | Callback, callback?: Callback, ): unknown { @@ -230,18 +229,22 @@ export class CookieJar { const promiseCallback = createPromiseCallback(callback) const cb = promiseCallback.callback - validators.validate( - validators.isNonEmptyString(url), - callback, - safeToString(options), - ) + if (typeof url === 'string') { + validators.validate( + validators.isNonEmptyString(url), + callback, + safeToString(options), + ) + } + + const context = getCookieContext(url) + let err if (typeof url === 'function') { return promiseCallback.reject(new Error('No URL was specified')) } - const context = getCookieContext(url) if (typeof options === 'function') { options = defaultSetCookieOptions } @@ -498,21 +501,21 @@ export class CookieJar { // RFC6365 S5.4 getCookies(url: string, callback: Callback): void getCookies( - url: string, + url: string | URL, options: GetCookiesOptions | undefined, callback: Callback, ): void getCookies( - url: string, + url: string | URL, options?: GetCookiesOptions | undefined, ): Promise getCookies( - url: string, + url: string | URL, options: GetCookiesOptions | undefined | Callback, callback?: Callback, ): unknown getCookies( - url: string, + url: string | URL, options?: GetCookiesOptions | Callback, callback?: Callback, ): unknown { @@ -525,10 +528,12 @@ export class CookieJar { const promiseCallback = createPromiseCallback(callback) const cb = promiseCallback.callback - validators.validate(validators.isNonEmptyString(url), cb, url) + if (typeof url === 'string') { + validators.validate(validators.isNonEmptyString(url), cb, url) + } + const context = getCookieContext(url) validators.validate(validators.isObject(options), cb, safeToString(options)) validators.validate(typeof cb === 'function', cb) - const context = getCookieContext(url) const host = canonicalDomain(context.hostname) const path = context.pathname || '/'