From fe18b8d98833f8bf90534de6f27ca0a920521991 Mon Sep 17 00:00:00 2001 From: Colin Casey Date: Sat, 5 Feb 2022 10:01:56 -0400 Subject: [PATCH] Typescript Support --- .eslintignore | 1 + .gitignore | 1 + .prettierignore | 1 + jest.config.js | 5 + lib/__tests__/api.test.ts | 226 +++++++++++++++++++++ lib/__tests__/date.test.ts | 176 ++++++++++++++++ lib/__tests__/ietf.test.ts | 85 ++++++++ lib/{cookie.js => cookie.ts} | 314 ++++++++++++++++++++++------- lib/{pathMatch.js => pathMatch.ts} | 10 +- lib/{store.js => store.ts} | 8 +- lib/validators.js | 19 +- package.json | 4 + test/api_test.js | 2 +- test/cookie_jar_test.js | 10 +- test/cookie_prefixes_test.js | 2 +- test/cookie_sorting_test.js | 2 +- test/cookie_to_json_test.js | 2 +- test/cookie_to_string_test.js | 2 +- test/date_test.js | 2 +- test/domain_and_path_test.js | 2 +- test/ietf_test.js | 2 +- test/jar_serialization_test.js | 2 +- test/lifetime_test.js | 2 +- test/parsing_test.js | 2 +- test/regression_test.js | 2 +- test/remove_all_test.js | 2 +- test/same_site_test.js | 2 +- tsconfig.json | 11 + 28 files changed, 796 insertions(+), 103 deletions(-) create mode 100644 .eslintignore create mode 100644 jest.config.js create mode 100644 lib/__tests__/api.test.ts create mode 100644 lib/__tests__/date.test.ts create mode 100644 lib/__tests__/ietf.test.ts rename lib/{cookie.js => cookie.ts} (87%) rename lib/{pathMatch.js => pathMatch.ts} (91%) rename lib/{store.js => store.ts} (95%) create mode 100644 tsconfig.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/.gitignore b/.gitignore index 3dbda85b..df02b1cc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ package-lock.json .npm .config .bash_history +dist/ diff --git a/.prettierignore b/.prettierignore index 89ed1e87..c3062b7c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ lib/version.js +dist/*.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..9e5dccd2 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: "ts-jest", + testEnvironment: "node" +}; diff --git a/lib/__tests__/api.test.ts b/lib/__tests__/api.test.ts new file mode 100644 index 00000000..6e67cb99 --- /dev/null +++ b/lib/__tests__/api.test.ts @@ -0,0 +1,226 @@ +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +import {Cookie, CookieJar, SerializedCookieJar, version} from '../cookie' +import packageJson from '../../package.json' + +jest.useFakeTimers() + +describe('API', () => { + it('should define Cookie', () => { + expect(Cookie).not.toBeUndefined() + }) + + it('should define CookieJar', () => { + expect(CookieJar).not.toBeUndefined() + }) + + it('should define the version that matches the package metadata', () => { + expect(version).toBe(packageJson.version) + }) + + describe('Cookie', () => { + let cookie: Cookie + + describe('constructor', () => { + beforeEach(() => { + cookie = new Cookie({ + key: "test", + value: "b", + maxAge: 60 + }) + }) + + it("should check for key property", () => { + expect(cookie.key).toEqual('test') + }) + + it('should check for value property', () => { + expect(cookie.value).toBe("b"); + }) + + it("should check for maxAge", () => { + expect(cookie.maxAge).toBe(60); + }) + + it("should check for default values for unspecified properties", () => { + expect(cookie.expires).toBe('Infinity') + expect(cookie.secure).toBe(false) + expect(cookie.httpOnly).toBe(false) + }) + }) + }) + + describe('CookieJar Promises', () => { + let cookieJar: CookieJar + + beforeEach(() => { + cookieJar = new CookieJar() + }) + + describe('setCookie', () => { + it('should resolve to a Cookie', async () => { + const cookie = await cookieJar.setCookie("foo=bar", "http://example.com") + expect(cookie).toBeInstanceOf(Cookie) + expect(cookie.key).toBe('foo') + expect(cookie.value).toBe('bar') + }) + + it('supports the "expiry" option', async () => { + const cookie = await cookieJar.setCookie( + "near=expiry; Domain=example.com; Path=/; Max-Age=1", + "http://www.example.com", + { now: new Date(Date.now() - 1) } + ) + expect(cookie).toEqual(expect.objectContaining({ + key: 'near', + value: 'expiry' + })) + jest.advanceTimersByTime(1) + const cookies = await cookieJar.getCookies("http://www.example.com", { + http: true, + expire: false + }) + expect(cookies).toHaveLength(1) + expect(cookies[0]).toEqual(expect.objectContaining({ + key: 'near', + value: 'expiry' + })) + }) + + it('supports the "allPaths" option', async () => { + const cookiesByUrl = { + "http://example.com": [ + "nopath_dom=qq; Path=/; Domain=example.com", + "path_dom=qq; Path=/foo; Domain=example.com" + ], + "http://www.example.com": [ + "nopath_host=qq; Path=/", + "path_host=qq; Path=/foo" + ], + "http://other.example.com": [ + "other=qq; Path=/" + ], + "http://other.example.com/foo": [ + "other2=qq; Path=/foo" + ] + } + const allCookiesSet = [] + for await (let [url, cookies] of Object.entries(cookiesByUrl)) { + for await (let cookie of cookies) { + const setCookie = await cookieJar.setCookie(cookie, url) + if (setCookie) { + allCookiesSet.push(setCookie) + } + } + } + expect(allCookiesSet).toHaveLength(6) + + const cookies = await cookieJar.getCookies("http://www.example.com/") + expect(cookies).toHaveLength(2) + expect(cookies.every(cookie => cookie.path === '/')).toBe(true) + expect(cookies.every(cookie => !/^other/.test(cookie.key))).toBe(true) + + const fooCookies = await cookieJar.getCookies("http://www.example.com/foo") + expect(fooCookies).toHaveLength(4) + expect(fooCookies.every(cookie => !/^other/.test(cookie.key))).toBe(true) + }) + }) + + describe('getCookies', () => { + it('resolves to an array of cookies', async () => { + await cookieJar.setCookie("foo=bar", "http://example.com") + const cookies = await cookieJar.getCookies("http://example.com") + expect(cookies).toEqual([ + expect.objectContaining({ + key: 'foo', + value: 'bar' + }) + ]) + }) + }) + + describe('getCookieString', () => { + it('resolves to a string', async () => { + await cookieJar.setCookie("foo=bar", "http://example.com") + const cookieString = await cookieJar.getCookieString("http://example.com") + expect(cookieString).toBe('foo=bar') + }) + }) + + describe('getSetCookieStrings', () => { + it('resolves to an array of strings', async () => { + await cookieJar.setCookie("foo=bar", "http://example.com") + const cookieString = await cookieJar.getSetCookieStrings("http://example.com") + expect(cookieString).toEqual(['foo=bar; Path=/']) + }) + }) + + describe('removeAllCookies', () => { + it('resolves to an array of strings', async () => { + await cookieJar.setCookie("foo=bar", "http://example.com") + expect(await cookieJar.getCookies("http://example.com")).toHaveLength(1) + await cookieJar.removeAllCookies() + expect(await cookieJar.getCookies("http://example.com")).toHaveLength(0) + }) + }) + + describe('serialize', () => { + it('resolves to an array of strings', async () => { + const now = new Date().toISOString() + + await cookieJar.setCookie("foo=bar", "http://example.com") + const data = await cookieJar.serialize() + const expected: SerializedCookieJar = { + "allowSpecialUseDomain": false, + "cookies": [ + { + "creation": now, + "domain": "example.com", + "hostOnly": true, + "key": "foo", + "lastAccessed": now, + "path": "/", + "pathIsDefault": true, + "value": "bar" + } + ], + "enableLooseMode": false, + "prefixSecurity": "silent", + "rejectPublicSuffixes": true, + "storeType": "MemoryCookieStore", + "version": "tough-cookie@4.0.0" + } + expect(data).toEqual(expected) + }) + }) + }) +}) diff --git a/lib/__tests__/date.test.ts b/lib/__tests__/date.test.ts new file mode 100644 index 00000000..b8fdc10f --- /dev/null +++ b/lib/__tests__/date.test.ts @@ -0,0 +1,176 @@ +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +type DateParsingTestCase = { + [key: string]: boolean +} + +type EquivalenceDateParsingTestCase = { + [key: string]: string +} + +const {parseDate} = require("../cookie"); + +const dateTests: DateParsingTestCase = { + "Wed, 09 Jun 2021 10:18:14 GMT": true, + "Wed, 09 JUN 2021 10:18:14 GMT": true, + "Wed, 09 Jun 2021 22:18:14 GMT": true, + "Tue, 18 Oct 2011 07:42:42.123 GMT": true, + "18 Oct 2011 07:42:42 GMT": true, + "8 Oct 2011 7:42:42 GMT": true, + "8 Oct 2011 7:2:42 GMT": true, + "8 Oct 2011 7:2:2 GMT": true, + "Oct 18 2011 07:42:42 GMT": true, + "Tue Oct 18 2011 07:05:03 GMT+0000 (GMT)": true, + "09 Jun 2021 10:18:14 GMT": true, + "99 Jix 3038 48:86:72 ZMT": false, + "01 Jan 1970 00:00:00 GMT": true, + "01 Jan 1600 00:00:00 GMT": false, // before 1601 + "01 Jan 1601 00:00:00 GMT": true, + "10 Feb 81 13:00:00 GMT": true, // implicit year + "Thu, 17-Apr-2014 02:12:29 GMT": true, // dashes + "Thu, 17-Apr-2014 02:12:29 UTC": true, // dashes and UTC + + // garbage after parts: + "Wedxxx, 09 Jun 2021 10:18:14 GMT": true, // day of week doesn't matter + "Wed, 09e9 Jun 2021 10:18:14 GMT": true, // garbage after day ignored + "Wed, 09 Junxxx 2021 10:18:14 GMT": true, // prefix match on month + "Wed, 09 Jun 2021e9 10:18:14 GMT": true, // garbage after year OK + "Wed, 09 Jun 2021 10e9:18:14 GMT": false, // can't have garbage after HH + "Wed, 09 Jun 2021 10:18e9:14 GMT": false, // can't have garbage after MM + "Wed, 09 Jun 2021 10:18:14e9 GMT": true, // garbage after SS ignored + + // extra digit in time parts: + "Thu, 01 Jan 1970 000:00:01 GMT": false, + "Thu, 01 Jan 1970 00:000:01 GMT": false, + "Thu, 01 Jan 1970 00:00:010 GMT": false, + + // hex in time + "Wed, 09 Jun 2021 1a:33:44 GMT": false, + "Wed, 09 Jun 2021 a1:33:44 GMT": false, + "Wed, 09 Jun 2021 11:f3:44 GMT": false, + "Wed, 09 Jun 2021 11:3f:44 GMT": false, + "Wed, 09 Jun 2021 11:33:e4 GMT": false, + "Wed, 09 Jun 2021 11:33:4e GMT": true, // garbage after seconds is OK + + // negatives in time + "Wed, 09 Jun 2021 -1:33:44 GMT": true, // parses as 1:33; - is a delimiter + "Wed, 09 Jun 2021 11:-3:44 GMT": false, + "Wed, 09 Jun 2021 11:33:-4 GMT": false, + + "": false +} + +const equivalenceTests: EquivalenceDateParsingTestCase = { + // milliseconds ignored + "Tue, 18 Oct 2011 07:42:42.123 GMT": "Tue, 18 Oct 2011 07:42:42 GMT", + + // shorter HH:MM:SS works how you'd expect: + "8 Oct 2011 7:32:42 GMT": "8 Oct 2011 07:32:42 GMT", + "8 Oct 2011 7:2:42 GMT": "8 Oct 2011 07:02:42 GMT", + "8 Oct 2011 7:2:2 GMT": "8 Oct 2011 07:02:02 GMT", + + // MDY versus DMY: + "Oct 18 2011 07:42:42 GMT": "18 Oct 2011 07:42:42 GMT", + + // some other messy auto format + "Tue Oct 18 2011 07:05:03 GMT+0000 (GMT)": + "Tue, 18 Oct 2011 07:05:03 GMT", + + // short year + "10 Feb 81 13:00:00 GMT": "10 Feb 1981 13:00:00 GMT", + "10 Feb 17 13:00:00 GMT": "10 Feb 2017 13:00:00 GMT", + + // dashes + "Thu, 17-Apr-2014 02:12:29 GMT": "Thu, 17 Apr 2014 02:12:29 GMT", + // dashes and "UTC" (timezone is always ignored) + "Thu, 17-Apr-2014 02:12:29 UTC": "Thu, 17 Apr 2014 02:12:29 GMT", + + // no weekday + "09 Jun 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT", + + // garbage after seconds is OK + "Wed, 09 Jun 2021 11:33:4e GMT": "Wed, 09 Jun 2021 11:33:04 GMT", + + // - is delimiter in this position + "Wed, 09 Jun 2021 -1:33:44 GMT": "Wed, 09 Jun 2021 01:33:44 GMT", + + // prefix match on month + "Wed, 09 Junxxx 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT", + "09 November 2021 10:18:14 GMT": "09 Nov 2021 10:18:14 GMT", + + // case of Month + "Wed, 09 JUN 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT", + "Wed, 09 jUN 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT", + + // test the framework :wink: + "Wed, 09 Jun 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT" +} + +describe("Dates", () => { + describe('parsing', () => { + const validDateTestCases = Object.entries(dateTests).filter(testCase => testCase[1]) + const invalidDateTestCases = Object.entries(dateTests).filter(testCase => !testCase[1]) + const equivalenceTestCases = Object.entries(equivalenceTests) + + it.each(validDateTestCases) + (`'%s' is valid`, (date: string) => { + expect(parseDate(date)).toBeInstanceOf(Date) + }) + + it.each(invalidDateTestCases) + (`'%s' is not valid`, (date: string) => { + expect(parseDate(date)).toBeUndefined(); + }) + + it.each(equivalenceTestCases) + (`'%s' parses the same as '%s'`, (date: string, equivalentDate: string) => { + expect(parseDate(date)).toStrictEqual(parseDate(equivalentDate)) + }) + }) + + describe('regexp denial of service attack vectors', () => { + const TOO_MANY_XS = String("x").repeat(65535); + + it('should avoid unbounded regexps when parsing the hour from a date', () => { + expect(parseDate(`Wed, 09 Jun 2021 10${TOO_MANY_XS}:18:14 GMT`)).toBeUndefined() + }) + + it('should avoid unbounded regexps when parsing the minute from a date', () => { + expect(parseDate(`Wed, 09 Jun 2021 10:18${TOO_MANY_XS}:14 GMT`)).toBeUndefined() + }) + + it('should avoid unbounded regexps when parsing the seconds from a date', () => { + const dateWithMillisIgnored = new Date(Date.parse('2021-06-09T10:18:14.000Z')) + expect(parseDate(`Wed, 09 Jun 2021 10:18:14${TOO_MANY_XS} GMT`)).toStrictEqual(dateWithMillisIgnored) + }) + }) +}) diff --git a/lib/__tests__/ietf.test.ts b/lib/__tests__/ietf.test.ts new file mode 100644 index 00000000..a2703341 --- /dev/null +++ b/lib/__tests__/ietf.test.ts @@ -0,0 +1,85 @@ +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +import {CookieJar, parseDate} from '../cookie' +import url from 'url' +import parserData from '../../test/ietf_data/parser.json' +import bsdExampleDates from '../../test/ietf_data/dates/bsd-examples.json' +import exampleDates from '../../test/ietf_data/dates/examples.json' + +describe('IETF http state tests', () => { + describe('Set/get cookie tests', () => { + it.each(parserData) + (`$test`, (testCase) => { + const jar = new CookieJar(); + const expected = testCase.sent + const sentFrom = `http://home.example.org/cookie-parser?${testCase.test}`; + const sentTo = testCase["sent-to"] + ? url.resolve("http://home.example.org", testCase["sent-to"]) + : `http://home.example.org/cookie-parser-result?${testCase.test}`; + + testCase["received"].forEach(cookieStr => { + // @ts-ignore + jar.setCookieSync(cookieStr, sentFrom, {ignoreError: true}); + }); + + // @ts-ignore + const actual = jar.getCookiesSync(sentTo, {sort: true}) as Array<{ key: string, value: string }>; + + expect(actual.length).toBe(expected.length) + actual.forEach((actualCookie, idx) => { + const expectedCookie = expected[idx]; + expect(actualCookie.key).toBe(expectedCookie.name) + expect(actualCookie.value).toBe(expectedCookie.value) + }); + }) + }) + + describe('Date handling', () => { + it.each(exampleDates) + (`ietf_data/dates/examples: $test`, ({test, expected}) => { + if (expected) { + expect(parseDate(test).toUTCString()).toBe(expected) + } else { + expect(parseDate(test)).toBeUndefined() + } + }) + + it.each(bsdExampleDates) + (`ietf_data/dates/bsd_examples: $test`, ({test, expected}) => { + if (expected) { + expect(parseDate(test).toUTCString()).toBe(expected) + } else { + expect(parseDate(test)).toBeUndefined() + } + }) + }) +}) diff --git a/lib/cookie.js b/lib/cookie.ts similarity index 87% rename from lib/cookie.js rename to lib/cookie.ts index f6ba38fd..e9a1da8e 100644 --- a/lib/cookie.js +++ b/lib/cookie.ts @@ -28,17 +28,18 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -"use strict"; -const punycode = require("punycode"); -const urlParse = require("url").parse; -const util = require("util"); -const pubsuffix = require("./pubsuffix-psl"); -const Store = require("./store").Store; -const MemoryCookieStore = require("./memstore").MemoryCookieStore; -const pathMatch = require("./pathMatch").pathMatch; -const validators = require("./validators.js"); -const VERSION = require("./version"); -const { fromCallback } = require("universalify"); + +import * as punycode from "punycode"; +import { parse as urlParse } from 'url' +import * as pubsuffix from './pubsuffix-psl' +import util from 'util' +import {Store} from './store' +import {MemoryCookieStore} from './memstore' +import {pathMatch} from "./pathMatch"; +import * as validators from './validators' +import VERSION from './version' +import {fromCallback} from 'universalify' +import {permuteDomain} from "./permuteDomain" // From RFC6265 S4.1.1 // note that it excludes \x3B ";" @@ -808,7 +809,26 @@ const cookieDefaults = { sameSite: "none" }; -class Cookie { +export class Cookie { + key: string; + value: string; + expires: Date | 'Infinity'; + maxAge: number | 'Infinity' | '-Infinity'; + domain: string | null; + path: string | null; + secure: boolean; + httpOnly: boolean; + extensions: string[] | null; + creation: Date | 'Infinity'; + creationIndex: number; + hostOnly: boolean | null; + pathIsDefault: boolean | null; + lastAccessed: Date | null | 'Infinity'; + sameSite: string | undefined; + + static serializableProperties: string[] + static cookiesCreated: number + constructor(options = {}) { if (util.inspect.custom) { this[util.inspect.custom] = this.inspect; @@ -830,9 +850,11 @@ class Cookie { const now = Date.now(); const hostOnly = this.hostOnly != null ? this.hostOnly : "?"; const createAge = this.creation + // @ts-ignore ? `${now - this.creation.getTime()}ms` : "?"; const accessAge = this.lastAccessed + // @ts-ignore ? `${now - this.lastAccessed.getTime()}ms` : "?"; return `Cookie="${this.toString()}; hostOnly=${hostOnly}; aAge=${accessAge}; cAge=${createAge}"`; @@ -857,6 +879,7 @@ class Cookie { obj[prop] = this[prop] == "Infinity" // intentionally not === ? "Infinity" + // @ts-ignore : this[prop].toISOString(); } } else if (prop === "maxAge") { @@ -886,6 +909,7 @@ class Cookie { return false; } if ( + // @ts-ignore this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires) @@ -944,6 +968,7 @@ class Cookie { toString() { let str = this.cookieString(); + // @ts-ignore if (this.expires != Infinity) { if (this.expires instanceof Date) { str += `; Expires=${formatDate(this.expires)}`; @@ -970,6 +995,7 @@ class Cookie { str += "; HttpOnly"; } if (this.sameSite && this.sameSite !== "none") { + // @ts-ignore const ssCanon = Cookie.sameSiteCanonical[this.sameSite.toLowerCase()]; str += `; SameSite=${ssCanon ? ssCanon : this.sameSite}`; } @@ -993,19 +1019,24 @@ class Cookie { * (Concurs with S5.3 step 3) */ if (this.maxAge != null) { + // @ts-ignore return this.maxAge <= 0 ? 0 : this.maxAge * 1000; } let expires = this.expires; + // @ts-ignore if (expires != Infinity) { if (!(expires instanceof Date)) { + // @ts-ignore expires = parseDate(expires) || Infinity; } + // @ts-ignore if (expires == Infinity) { return Infinity; } + // @ts-ignore return expires.getTime() - (now || Date.now()); } @@ -1017,13 +1048,16 @@ class Cookie { expiryTime(now) { if (this.maxAge != null) { const relativeTo = now || this.creation || new Date(); + // @ts-ignore const age = this.maxAge <= 0 ? -Infinity : this.maxAge * 1000; return relativeTo.getTime() + age; } + // @ts-ignore if (this.expires == Infinity) { return Infinity; } + // @ts-ignore return this.expires.getTime(); } @@ -1042,6 +1076,7 @@ class Cookie { // This replaces the "persistent-flag" parts of S5.3 step 3 isPersistent() { + // @ts-ignore return this.maxAge != null || this.expires != Infinity; } @@ -1059,15 +1094,19 @@ class Cookie { } Cookie.cookiesCreated = 0; +// @ts-ignore Cookie.parse = parse; +// @ts-ignore Cookie.fromJSON = fromJSON; Cookie.serializableProperties = Object.keys(cookieDefaults); +// @ts-ignore Cookie.sameSiteLevel = { strict: 3, lax: 2, none: 1 }; +// @ts-ignore Cookie.sameSiteCanonical = { strict: "Strict", lax: "Lax" @@ -1088,49 +1127,136 @@ function getNormalizedPrefixSecurity(prefixSecurity) { return PrefixSecurityEnum.SILENT; } -class CookieJar { - constructor(store, options = { rejectPublicSuffixes: true }) { +type SetCookieOptions = { + loose?: boolean; + sameSiteContext?: boolean; + ignoreError?: boolean; + http?: boolean; + now?: Date; +} + +const defaultSetCookieOptions: SetCookieOptions = { + loose: false, + sameSiteContext: false, + ignoreError: false, + http: false +} + +interface PromiseCallback { + promise: Promise; + callback: (error?: Error, result?: T) => Promise; +} + +export interface SerializedCookieJar { + version: string; + storeType: string; + rejectPublicSuffixes: boolean; + [key: string]: any; + cookies: SerializedCookie[]; +} + +export interface SerializedCookie { + key: string; + value: string; + [key: string]: any; +} + +function createPromiseCallback(args: IArguments): PromiseCallback { + let callback: (error: Error, result: T) => Promise + let resolve: (result: T) => void + let reject: (error: Error) => void + + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve + reject = _reject + }) + + if (typeof args[args.length - 1] === 'function') { + const cb = args[args.length - 1] + callback = (err, result) => { + try { + cb(err, result) + } catch(e) { + reject(e) + } + return promise + } + } else { + callback = (err, result) => { + try { + err ? reject(err) : resolve(result) + } catch (e) { + reject(e) + } + return promise + } + } + + return { + promise, + callback + } +} + +export class CookieJar { + store: Store; + + constructor(store?: any, options: any = { rejectPublicSuffixes: true }) { if (typeof options === "boolean") { options = { rejectPublicSuffixes: options }; } validators.validate(validators.isObject(options), options); + // @ts-ignore this.rejectPublicSuffixes = options.rejectPublicSuffixes; + // @ts-ignore this.enableLooseMode = !!options.looseMode; + // @ts-ignore this.allowSpecialUseDomain = !!options.allowSpecialUseDomain; this.store = store || new MemoryCookieStore(); + // @ts-ignore this.prefixSecurity = getNormalizedPrefixSecurity(options.prefixSecurity); + // @ts-ignore this._cloneSync = syncWrap("clone"); + // @ts-ignore this._importCookiesSync = syncWrap("_importCookies"); + // @ts-ignore this.getCookiesSync = syncWrap("getCookies"); + // @ts-ignore this.getCookieStringSync = syncWrap("getCookieString"); + // @ts-ignore this.getSetCookieStringsSync = syncWrap("getSetCookieStrings"); + // @ts-ignore this.removeAllCookiesSync = syncWrap("removeAllCookies"); + // @ts-ignore this.setCookieSync = syncWrap("setCookie"); + // @ts-ignore this.serializeSync = syncWrap("serialize"); } - setCookie(cookie, url, options, cb) { - validators.validate(validators.isNonEmptyString(url), cb, options); + setCookie(cookie, url, options: SetCookieOptions = defaultSetCookieOptions, callback?: (error: Error, result: Cookie) => void): Promise { + const promiseCallback = createPromiseCallback(arguments) + const cb = promiseCallback.callback + + validators.validate(validators.isNonEmptyString(url), callback, options); let err; if (validators.isFunction(url)) { - cb = url; return cb(new Error("No URL was specified")); } const context = getCookieContext(url); if (validators.isFunction(options)) { - cb = options; - options = {}; + options = defaultSetCookieOptions; } validators.validate(validators.isFunction(cb), cb); - if(!validators.isNonEmptyString(cookie) && !validators.isObject(cookie) && ( cookie instanceof String && cookie.length == 0)) { + if (!validators.isNonEmptyString(cookie) && !validators.isObject(cookie) && (cookie instanceof String && cookie.length == 0)) { return cb(null); } const host = canonicalDomain(context.hostname); + // @ts-ignore const loose = options.loose || this.enableLooseMode; let sameSiteContext = null; @@ -1143,7 +1269,8 @@ class CookieJar { // S5.3 step 1 if (typeof cookie === "string" || cookie instanceof String) { - cookie = Cookie.parse(cookie, { loose: loose }); + // @ts-ignore + cookie = Cookie.parse(cookie, {loose: loose}); if (!cookie) { err = new Error("Cookie failed to parse"); return cb(options.ignoreError ? null : err); @@ -1165,8 +1292,10 @@ class CookieJar { // S5.3 step 4: NOOP; domain is null by default // S5.3 step 5: public suffixes + // @ts-ignore if (this.rejectPublicSuffixes && cookie.domain) { const suffix = pubsuffix.getPublicSuffix(cookie.cdomain(), { + // @ts-ignore allowSpecialUseDomain: this.allowSpecialUseDomain, ignoreError: options.ignoreError }); @@ -1228,8 +1357,10 @@ class CookieJar { /* 6265bis-02 S5.4 Steps 15 & 16 */ const ignoreErrorForPrefixSecurity = + // @ts-ignore this.prefixSecurity === PrefixSecurityEnum.SILENT; const prefixSecurityDisabled = + // @ts-ignore this.prefixSecurity === PrefixSecurityEnum.DISABLED; /* If prefix checking is not disabled ...*/ if (!prefixSecurityDisabled) { @@ -1257,7 +1388,7 @@ class CookieJar { const store = this.store; if (!store.updateCookie) { - store.updateCookie = function(oldCookie, newCookie, cb) { + store.updateCookie = function (oldCookie, newCookie, cb) { this.putCookie(newCookie, cb); }; } @@ -1267,7 +1398,7 @@ class CookieJar { return cb(err); } - const next = function(err) { + const next = function (err) { if (err) { return cb(err); } else { @@ -1278,9 +1409,11 @@ class CookieJar { if (oldCookie) { // S5.3 step 11 - "If the cookie store contains a cookie with the same name, // domain, and path as the newly created cookie:" + // @ts-ignore if (options.http === false && oldCookie.httpOnly) { // step 11.2 err = new Error("old Cookie is HttpOnly and this isn't an HTTP API"); + // @ts-ignore return cb(options.ignoreError ? null : err); } cookie.creation = oldCookie.creation; // step 11.3 @@ -1295,14 +1428,18 @@ class CookieJar { } store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie); + return promiseCallback.promise + // } } // RFC6365 S5.4 - getCookies(url, options, cb) { + getCookies(url: string, options: any = {}, callback?: (error: Error, result: Cookie[]) => void): Promise { + const promiseCallback = createPromiseCallback(arguments) + const cb = promiseCallback.callback + validators.validate(validators.isNonEmptyString(url), cb, url); const context = getCookieContext(url); if (validators.isFunction(options)) { - cb = options; options = {}; } validators.validate(validators.isObject(options), cb, options); @@ -1323,6 +1460,7 @@ class CookieJar { let sameSiteLevel = 0; if (options.sameSiteContext) { const sameSiteContext = checkSameSiteContext(options.sameSiteContext); + // @ts-ignore sameSiteLevel = Cookie.sameSiteLevel[sameSiteContext]; if (!sameSiteLevel) { return cb(new Error(SAME_SITE_CONTEXT_VAL_ERR)); @@ -1337,6 +1475,7 @@ class CookieJar { const now = options.now || Date.now(); const expireCheck = options.expire !== false; const allPaths = !!options.allPaths; + // @ts-ignore const store = this.store; function matchingCookie(c) { @@ -1375,6 +1514,7 @@ class CookieJar { // RFC6265bis-02 S5.3.7 if (sameSiteLevel) { + // @ts-ignore const cookieLevel = Cookie.sameSiteLevel[c.sameSite || "none"]; if (cookieLevel > sameSiteLevel) { // only allow cookies at or below the request level @@ -1395,6 +1535,7 @@ class CookieJar { store.findCookies( host, allPaths ? null : path, + // @ts-ignore this.allowSpecialUseDomain, (err, cookies) => { if (err) { @@ -1418,16 +1559,18 @@ class CookieJar { cb(null, cookies); } ); + + return promiseCallback.promise } - getCookieString(...args) { - const cb = args.pop(); - validators.validate(validators.isFunction(cb), cb); - const next = function(err, cookies) { + getCookieString(url: string, options: any = {}, callback?: (error: Error, result: string) => void): Promise { + const promiseCallback = createPromiseCallback(arguments) + + const next = function(err: Error, cookies: Cookie[]) { if (err) { - cb(err); + promiseCallback.callback(err); } else { - cb( + promiseCallback.callback( null, cookies .sort(cookieCompare) @@ -1436,18 +1579,19 @@ class CookieJar { ); } }; - args.push(next); - this.getCookies.apply(this, args); + + this.getCookies(url, options, next) + return promiseCallback.promise } - getSetCookieStrings(...args) { - const cb = args.pop(); - validators.validate(validators.isFunction(cb), cb); - const next = function(err, cookies) { + getSetCookieStrings (url: string, options: any = {}, callback?: (error: Error, result: string[]) => void): Promise { + const promiseCallback = createPromiseCallback(arguments) + + const next = function(err: Error, cookies: Cookie[]) { if (err) { - cb(err); + promiseCallback.callback(err); } else { - cb( + promiseCallback.callback( null, cookies.map(c => { return c.toString(); @@ -1455,19 +1599,24 @@ class CookieJar { ); } }; - args.push(next); - this.getCookies.apply(this, args); + + this.getCookies(url, options, next); + return promiseCallback.promise } - serialize(cb) { + serialize(callback?: (error: Error, data: SerializedCookieJar) => void): Promise { + const promiseCallback = createPromiseCallback(arguments) + const cb = promiseCallback.callback + validators.validate(validators.isFunction(cb), cb); + // @ts-ignore let type = this.store.constructor.name; if (validators.isObject(type)) { type = null; } // update README.md "Serialization Format" if you change this, please! - const serialized = { + const serialized: SerializedCookieJar = { // The version of tough-cookie that serialized this jar. Generally a good // practice since future versions can make data import decisions based on // known past behavior. When/if this matters, use `semver`. @@ -1477,9 +1626,13 @@ class CookieJar { storeType: type, // CookieJar configuration: + // @ts-ignore rejectPublicSuffixes: !!this.rejectPublicSuffixes, + // @ts-ignore enableLooseMode: !!this.enableLooseMode, + // @ts-ignore allowSpecialUseDomain: !!this.allowSpecialUseDomain, + // @ts-ignore prefixSecurity: getNormalizedPrefixSecurity(this.prefixSecurity), // this gets filled from getAllCookies: @@ -1488,7 +1641,9 @@ class CookieJar { if ( !( + // @ts-ignore this.store.getAllCookies && + // @ts-ignore typeof this.store.getAllCookies === "function" ) ) { @@ -1499,6 +1654,7 @@ class CookieJar { ); } + // @ts-ignore this.store.getAllCookies((err, cookies) => { if (err) { return cb(err); @@ -1516,9 +1672,12 @@ class CookieJar { return cb(null, serialized); }); + + return promiseCallback.promise } toJSON() { + // @ts-ignore return this.serializeSync(); } @@ -1550,9 +1709,11 @@ class CookieJar { return putNext(null); // skip this cookie } + // @ts-ignore this.store.putCookie(cookie, putNext); }; + // @ts-ignore putNext(); } @@ -1572,6 +1733,7 @@ class CookieJar { cloneSync(newStore) { if (arguments.length === 0) { + // @ts-ignore return this._cloneSync(); } if (!newStore.synchronous) { @@ -1579,11 +1741,15 @@ class CookieJar { "CookieJar clone destination store is not synchronous; use async API instead." ); } + // @ts-ignore return this._cloneSync(newStore); } - removeAllCookies(cb) { - validators.validate(validators.isFunction(cb), cb); + removeAllCookies(callback?: (error?: Error) => void): Promise { + const promiseCallback = createPromiseCallback(arguments) + const cb = promiseCallback.callback + + // @ts-ignore const store = this.store; // Check that the store implements its own removeAllCookies(). The default @@ -1593,7 +1759,8 @@ class CookieJar { typeof store.removeAllCookies === "function" && store.removeAllCookies !== Store.prototype.removeAllCookies ) { - return store.removeAllCookies(cb); + store.removeAllCookies(cb); + return promiseCallback.promise } store.getAllCookies((err, cookies) => { @@ -1629,6 +1796,8 @@ class CookieJar { ); }); }); + + return promiseCallback.promise } static deserialize(strOrObj, store, cb) { @@ -1649,8 +1818,9 @@ class CookieJar { serialized = strOrObj; } - const jar = new CookieJar(store, { + const jar = new CookieJar(store, { rejectPublicSuffixes: serialized.rejectPublicSuffixes, + // @ts-ignore looseMode: serialized.enableLooseMode, allowSpecialUseDomain: serialized.allowSpecialUseDomain, prefixSecurity: serialized.prefixSecurity @@ -1666,22 +1836,26 @@ class CookieJar { static deserializeSync(strOrObj, store) { const serialized = typeof strOrObj === "string" ? JSON.parse(strOrObj) : strOrObj; - const jar = new CookieJar(store, { + const jar = new CookieJar(store, { rejectPublicSuffixes: serialized.rejectPublicSuffixes, - looseMode: serialized.enableLooseMode + // @ts-ignore + looseMode: serialized.enableLooseMode }); // catch this mistake early: + // @ts-ignore if (!jar.store.synchronous) { throw new Error( "CookieJar store is not synchronous; use async API instead." ); } + // @ts-ignore jar._importCookiesSync(serialized); return jar; } } +// @ts-ignore CookieJar.fromJSON = CookieJar.deserializeSync; [ @@ -1692,10 +1866,11 @@ CookieJar.fromJSON = CookieJar.deserializeSync; "getSetCookieStrings", "removeAllCookies", "serialize", - "setCookie" + // "setCookie" ].forEach(name => { CookieJar.prototype[name] = fromCallback(CookieJar.prototype[name]); }); +// CookieJar.prototype.setCookie = fromCallback(CookieJar.prototype.setCookie) CookieJar.deserialize = fromCallback(CookieJar.deserialize); // Use a closure to provide a true imperative API for synchronous stores. @@ -1720,22 +1895,23 @@ function syncWrap(method) { }; } -exports.version = VERSION; -exports.CookieJar = CookieJar; -exports.Cookie = Cookie; -exports.Store = Store; -exports.MemoryCookieStore = MemoryCookieStore; -exports.parseDate = parseDate; -exports.formatDate = formatDate; -exports.parse = parse; -exports.fromJSON = fromJSON; -exports.domainMatch = domainMatch; -exports.defaultPath = defaultPath; -exports.pathMatch = pathMatch; -exports.getPublicSuffix = pubsuffix.getPublicSuffix; -exports.cookieCompare = cookieCompare; -exports.permuteDomain = require("./permuteDomain").permuteDomain; -exports.permutePath = permutePath; -exports.canonicalDomain = canonicalDomain; -exports.PrefixSecurityEnum = PrefixSecurityEnum; -exports.ParameterError = validators.ParameterError; +const getPublicSuffix = pubsuffix.getPublicSuffix +const ParameterError = validators.ParameterError + +export { VERSION as version } +export { Store as Store } +export { MemoryCookieStore as MemoryCookieStore } +export { parseDate as parseDate } +export { formatDate as formatDate } +export { parse as parse } +export { fromJSON as fromJSON } +export { domainMatch as domainMatch } +export { defaultPath as defaultPath } +export { pathMatch as pathMatch } +export { getPublicSuffix as getPublicSuffix } +export { cookieCompare as cookieCompare } +export { permuteDomain as permuteDomain } +export { permutePath as permutePath } +export { canonicalDomain as canonicalDomain } +export { PrefixSecurityEnum as PrefixSecurityEnum } +export { ParameterError as ParameterError } diff --git a/lib/pathMatch.js b/lib/pathMatch.ts similarity index 91% rename from lib/pathMatch.js rename to lib/pathMatch.ts index 16ff63ee..926c885c 100644 --- a/lib/pathMatch.js +++ b/lib/pathMatch.ts @@ -28,12 +28,12 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -"use strict"; + /* * "A request-path path-matches a given cookie-path if at least one of the * following conditions holds:" */ -function pathMatch(reqPath, cookiePath) { +export function pathMatch(reqPath: string, cookiePath: string): boolean { // "o The cookie-path and the request-path are identical." if (cookiePath === reqPath) { return true; @@ -43,19 +43,17 @@ function pathMatch(reqPath, cookiePath) { if (idx === 0) { // "o The cookie-path is a prefix of the request-path, and the last // character of the cookie-path is %x2F ("/")." - if (cookiePath.substr(-1) === "/") { + if (cookiePath[cookiePath.length - 1] === "/") { return true; } // " o The cookie-path is a prefix of the request-path, and the first // character of the request-path that is not included in the cookie- path // is a %x2F ("/") character." - if (reqPath.substr(cookiePath.length, 1) === "/") { + if (new RegExp(`^${cookiePath}`).test(reqPath) && reqPath[cookiePath.length] === "/") { return true; } } return false; } - -exports.pathMatch = pathMatch; diff --git a/lib/store.js b/lib/store.ts similarity index 95% rename from lib/store.js rename to lib/store.ts index 2ed0259e..4c5bc666 100644 --- a/lib/store.js +++ b/lib/store.ts @@ -31,12 +31,16 @@ "use strict"; /*jshint unused:false */ -class Store { +import {Cookie} from "./cookie"; + +export class Store { + synchronous: boolean; + constructor() { this.synchronous = false; } - findCookie(domain, path, key, cb) { + findCookie(domain, path, key, cb): Cookie { throw new Error("findCookie is not implemented"); } diff --git a/lib/validators.js b/lib/validators.js index d2f39deb..b75c3905 100644 --- a/lib/validators.js +++ b/lib/validators.js @@ -1,21 +1,21 @@ /* ************************************************************************************ Extracted from check-types.js https://gitlab.com/philbooth/check-types.js - + MIT License - + Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Phil Booth - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,7 +23,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + ************************************************************************************ */ "use strict"; @@ -82,6 +82,11 @@ function validate(bool, cb, options) { class ParameterError extends Error { constructor(...params) { super(...params); + if (Object.setPrototypeOf) { + Object.setPrototypeOf(this, ParameterError.prototype); + } else { + this.__proto__ = new.target.prototype; + } } }; @@ -92,4 +97,4 @@ exports.isDate = isDate; exports.isEmptyString = isEmptyString; exports.isString = isString; exports.isObject = isObject; -exports.validate = validate; \ No newline at end of file +exports.validate = validate; diff --git a/package.json b/package.json index e07dcb7c..8c536698 100644 --- a/package.json +++ b/package.json @@ -92,13 +92,17 @@ "node": ">=6" }, "devDependencies": { + "@types/jest": "^27.4.0", "async": "^2.6.2", "eslint": "^5.16.0", "eslint-config-prettier": "^4.2.0", "eslint-plugin-prettier": "^3.0.1", "genversion": "^2.1.0", + "jest": "^27.4.7", "nyc": "^14.0.0", "prettier": "^1.17.0", + "ts-jest": "^27.1.3", + "typescript": "^4.5.5", "vows": "^0.8.2" }, "dependencies": { diff --git a/test/api_test.js b/test/api_test.js index 1144cf34..81fbbc68 100644 --- a/test/api_test.js +++ b/test/api_test.js @@ -32,7 +32,7 @@ const vows = require("vows"); const assert = require("assert"); const async = require("async"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; const CookieJar = tough.CookieJar; diff --git a/test/cookie_jar_test.js b/test/cookie_jar_test.js index 328a639b..e409f8aa 100644 --- a/test/cookie_jar_test.js +++ b/test/cookie_jar_test.js @@ -32,7 +32,7 @@ const vows = require("vows"); const assert = require("assert"); const async = require("async"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; const CookieJar = tough.CookieJar; @@ -750,12 +750,12 @@ vows "of undefined": { topic: function() { const jar = new tough.CookieJar(); - const cookieString = `AWSELB=69b2c0038b16e8e27056d1178e0d556c; - Path=/, jses_WS41=5f8dc2f6-ea37-49de-8dfa-b58336c2d9ce; path=/; + const cookieString = `AWSELB=69b2c0038b16e8e27056d1178e0d556c; + Path=/, jses_WS41=5f8dc2f6-ea37-49de-8dfa-b58336c2d9ce; path=/; Secure; HttpOnly, AuthToken=EFKFFFCH@K@GHIHEJCJMMGJM>CDHDEK>CFGK?MHJ >>JI@B??@CAEHBJH@H@A@GCFDLIMLJEEJEIFGALA?BIM?@G@DEDI@JE?I?HKJBIDDHJMEFEFM - >G@J?I??B@C>>LAH?GCGJ@FMEGHBGAF; expires=Sun, 31-Jan-9021 02:39:04 GMT; - path=/; Secure; HttpOnly, FirstReferrer=; expires=Fri, 31-Jan-9020 20:50:44 + >G@J?I??B@C>>LAH?GCGJ@FMEGHBGAF; expires=Sun, 31-Jan-9021 02:39:04 GMT; + path=/; Secure; HttpOnly, FirstReferrer=; expires=Fri, 31-Jan-9020 20:50:44 GMT; path=/`; jar.setCookieSync(cookieString, "https://google.com"); diff --git a/test/cookie_prefixes_test.js b/test/cookie_prefixes_test.js index 5a21be3f..81ef4a1b 100644 --- a/test/cookie_prefixes_test.js +++ b/test/cookie_prefixes_test.js @@ -31,7 +31,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const CookieJar = tough.CookieJar; const PrefixSecurityEnum = tough.PrefixSecurityEnum; diff --git a/test/cookie_sorting_test.js b/test/cookie_sorting_test.js index 23ddeee4..0e50685c 100644 --- a/test/cookie_sorting_test.js +++ b/test/cookie_sorting_test.js @@ -31,7 +31,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; const CookieJar = tough.CookieJar; diff --git a/test/cookie_to_json_test.js b/test/cookie_to_json_test.js index 5becedb2..8506cedc 100644 --- a/test/cookie_to_json_test.js +++ b/test/cookie_to_json_test.js @@ -32,7 +32,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; vows diff --git a/test/cookie_to_string_test.js b/test/cookie_to_string_test.js index b531646a..2a853efe 100644 --- a/test/cookie_to_string_test.js +++ b/test/cookie_to_string_test.js @@ -32,7 +32,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; vows diff --git a/test/date_test.js b/test/date_test.js index 2965ac11..5b81003c 100644 --- a/test/date_test.js +++ b/test/date_test.js @@ -32,7 +32,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); function dateVows(table) { const theVows = {}; diff --git a/test/domain_and_path_test.js b/test/domain_and_path_test.js index de15f317..e85ba70e 100644 --- a/test/domain_and_path_test.js +++ b/test/domain_and_path_test.js @@ -32,7 +32,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; function matchVows(func, table) { diff --git a/test/ietf_test.js b/test/ietf_test.js index 1529ea08..a15d5fda 100644 --- a/test/ietf_test.js +++ b/test/ietf_test.js @@ -35,7 +35,7 @@ const assert = require("assert"); const fs = require("fs"); const path = require("path"); const url = require("url"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const CookieJar = tough.CookieJar; function readJson(filePath) { diff --git a/test/jar_serialization_test.js b/test/jar_serialization_test.js index dfa2fb39..5bbb9758 100644 --- a/test/jar_serialization_test.js +++ b/test/jar_serialization_test.js @@ -32,7 +32,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; const CookieJar = tough.CookieJar; const Store = tough.Store; diff --git a/test/lifetime_test.js b/test/lifetime_test.js index 0954d6a5..9a5c0357 100644 --- a/test/lifetime_test.js +++ b/test/lifetime_test.js @@ -32,7 +32,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; vows diff --git a/test/parsing_test.js b/test/parsing_test.js index b26d4cfb..82aff72d 100644 --- a/test/parsing_test.js +++ b/test/parsing_test.js @@ -32,7 +32,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; const LOTS_OF_SEMICOLONS = ";".repeat(65535); diff --git a/test/regression_test.js b/test/regression_test.js index 29d21c0e..8f88a83a 100644 --- a/test/regression_test.js +++ b/test/regression_test.js @@ -33,7 +33,7 @@ const vows = require("vows"); const assert = require("assert"); const async = require("async"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; const CookieJar = tough.CookieJar; const MemoryCookieStore = tough.MemoryCookieStore; diff --git a/test/remove_all_test.js b/test/remove_all_test.js index fa19f506..bed18b18 100644 --- a/test/remove_all_test.js +++ b/test/remove_all_test.js @@ -33,7 +33,7 @@ const util = require("util"); const vows = require("vows"); const assert = require("assert"); const async = require("async"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; const CookieJar = tough.CookieJar; const Store = tough.Store; diff --git a/test/same_site_test.js b/test/same_site_test.js index c43b3f4e..785c61e3 100644 --- a/test/same_site_test.js +++ b/test/same_site_test.js @@ -32,7 +32,7 @@ "use strict"; const vows = require("vows"); const assert = require("assert"); -const tough = require("../lib/cookie"); +const tough = require("../dist/lib/cookie"); const Cookie = tough.Cookie; const CookieJar = tough.CookieJar; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..ccd5d5b7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "outDir": "./dist", + "allowJs": true, + "target": "es5", + "resolveJsonModule": true, + "esModuleInterop": true + }, + "include": ["./lib/**/*"] +}