diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..fb3d960 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,16 @@ +"use strict"; +module.exports = { + // We tried to stick to ES5 compat for browsers (#44), but eslint can't handle that with modules. + // However, it is enforced for the dist/ directory, which just gets a cjs-ify'd version of the lib. + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + env: { + node: true, + browser: true, + }, + extends: ["eslint:recommended", "plugin:prettier/recommended"], + rules: { + "prefer-const": "error", + strict: "error", + eqeqeq: "error", + }, +}; diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index fa1b084..ce613c4 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -20,7 +20,6 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'npm' - run: npm ci - - run: npm run build --if-present - run: npm test publish: name: Publish diff --git a/.gitignore b/.gitignore index e61051f..b55927f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules/ /coverage/ +/dist/set-cookie.cjs \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..18de984 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npm test \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ca94cfd..6299b6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Change log +## [v3.0.0](https://github.com/nfriedly/set-cookie-parser/tree/v3.0.0) - 2025-12-17 + +### Summary of v3 changes +* Library now supports both ESM and CJS +* Combined cookie headers are now split automatically +* API has been simplified to a single named export: `parseSetCookie` (with other exports for backwards compatibility) + +Changed: +* Library is now written as an ES module, with CJS automatically built for backwards-compatibility +* `parse` function renamed to `parseSetCookie` (with alias for backwards compatibility) + +Added: +* Library now splits combined cookies automatically based on input type. +* The new `split` option overrides this behavior, set to `true` to always split or `false` for the previous behavior of never plitting automatically. + +Removed / Soft-deprecated: +* default export, `parse()`, `parseString()`, and `splitCookieString()` methods are no longer documented, but are still present for backwards compatibility. + ## [v2.7.2](https://github.com/nfriedly/set-cookie-parser/tree/v2.7.2) - 2025-10-27 Fixed: diff --git a/README.md b/README.md index 4430e15..d277299 100644 --- a/README.md +++ b/README.md @@ -6,25 +6,11 @@ --- -ℹ️ **Note for current users:** I'm considering some changes for the next major version and would appreciate your feedback: https://github.com/nfriedly/set-cookie-parser/discussions/68 - ---- - Parses set-cookie headers into JavaScript objects Accepts a single `set-cookie` header value, an array of `set-cookie` header values, a Node.js response object, or a `fetch()` `Response` object that may have 0 or more `set-cookie` headers. -Also accepts an optional options object. Defaults: - -```js -{ - decodeValues: true, // Calls decodeURIComponent on each value - default: true - map: false, // Return an object instead of an array - default: false - silent: false, // Suppress the warning that is logged when called on a request instead of a response - default: false -} -``` - -Returns either an array of cookie objects or a map of name => cookie object with `{map: true}`. Each cookie object will have, at a minimum `name` and `value` properties, and may have additional properties depending on the set-cookie header: +Returns either an array of cookie objects or a map of name => cookie object with options set `{map: true}`. Each cookie object will have, at a minimum `name` and `value` properties, and may have additional properties depending on the set-cookie header: * `name` - cookie name (string) * `value` - cookie value (string) @@ -36,7 +22,7 @@ Returns either an array of cookie objects or a map of name => cookie object with * `secure` - indicates cookie should only be sent over HTTPs (true or undefined) * `httpOnly` - indicates cookie should *not* be accessible to client-side JavaScript (true or undefined) * `sameSite` - indicates if cookie should be included in cross-site requests ([more info](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value)) (string or undefined) - * Note: valid values are `"Strict"`, `"Lax"`, and `"None"`, but set-cookie-parser coppies the value verbatim and does *not* perform any validation. + * Note: valid values are `"Strict"`, `"Lax"`, and `"None"`, but set-cookie-parser copies the value verbatim and does *not* perform any validation. * `partitioned` - indicates cookie should be scoped to the combination of 3rd party domain + top page domain ([more info](https://developer.mozilla.org/en-US/docs/Web/Privacy/Privacy_sandbox/Partitioned_cookies)) (true or undefined) (The output format is loosely based on the input format of https://www.npmjs.com/package/cookie) @@ -53,11 +39,12 @@ $ npm install --save set-cookie-parser ### Get array of cookie objects ```js -var http = require('http'); -var setCookie = require('set-cookie-parser'); +import * as http from 'node:http'; +import { parseSetCookie } from 'set-cookie-parser'; +// or const { parseSetCookie } = require('set-cookie-parser'); http.get('http://example.com', function(res) { - var cookies = setCookie.parse(res, { + const cookies = parseSetCookie(res, { decodeValues: true // default: true }); @@ -90,16 +77,17 @@ Example output: ### Get map of cookie objects ```js -var http = require('http'); -var setCookie = require('set-cookie-parser'); +import * as http from 'node:http'; +import { parseSetCookie } from 'set-cookie-parser'; +// or const { parseSetCookie } = require('set-cookie-parser'); http.get('http://example.com', function(res) { - var cookies = setCookie.parse(res, { + const cookies = parseSetCookie(res, { decodeValues: true, // default: true map: true // default: false }); - var desiredCookie = cookies['session']; + const desiredCookie = cookies['session']; console.log(desiredCookie); }); ``` @@ -129,12 +117,13 @@ Example output: This library can be used in conjunction with the [cookie](https://www.npmjs.com/package/cookie) library to modify and replace set-cookie headers: ```js -const libCookie = require('cookie'); -const setCookie = require('set-cookie-parser'); +import * as libCookie from 'cookie'; +import { parseSetCookie } from 'set-cookie-parser'; +// or const { parseSetCookie } = require('set-cookie-parser'); function modifySetCookie(res){ // parse the set-cookie headers with this library - let cookies = setCookie.parse(res); + const cookies = parseSetCookie(res); // modify the cookies here // ... @@ -148,45 +137,23 @@ function modifySetCookie(res){ See a real-world example of this in [unblocker](https://github.com/nfriedly/node-unblocker/blob/08a89ec27274b46dcd80d0a324a59406f2bdad3d/lib/cookies.js#L67-L85) -## Usage in React Native (and with some other fetch implementations) - -React Native follows the Fetch spec more closely and combines all of the Set-Cookie header values into a single string. -The `splitCookiesString` method reverses this. - -```js -var setCookie = require('set-cookie-parser'); - -var response = fetch(/*...*/); - -// This is mainly for React Native; Node.js does not combine set-cookie headers. -var combinedCookieHeader = response.headers.get('Set-Cookie'); -var splitCookieHeaders = setCookie.splitCookiesString(combinedCookieHeader) -var cookies = setCookie.parse(splitCookieHeaders); - -console.log(cookies); // should be an array of cookies -``` - -This behavior may become a default part of parse in the next major release, but requires the extra step for now. - -Note that the `fetch()` spec now includes a `getSetCookie()` method that provides un-combined `Set-Cookie` headers. This library will automatically use that method if it is present. - ## API -### parse(input, [options]) +### parseSetCookie(input, [options]) Parses cookies from a string, array of strings, or a http response object. Always returns an array, regardless of input format. (Unless the `map` option is set, in which case it always returns an object.) -### parseString(individualSetCookieHeader, [options]) - -Parses a single set-cookie header value string. Options default is `{decodeValues: true}`. Used under-the-hood by `parse()`. -Returns an object. - -### splitCookiesString(combinedSetCookieHeader) +Also accepts an optional options object. Defaults: -It's uncommon, but the HTTP spec does allow for multiple of the same header to have their values combined (comma-separated) into a single header. -This method splits apart a combined header without choking on commas that appear within a cookie's value (or expiration date). -Returns an array of strings that may be passed to `parse()`. +```js +{ + decodeValues: true, // Calls decodeURIComponent on each value - default: true + map: false, // Return an object instead of an array - default: false + silent: false, // Suppress the warning that is logged when called on a request instead of a response - default: false + split: 'auto', // Separate combined cookie headers. Valid options are true/false/'auto'. 'auto' splits strings but not arrays. +} +``` ## References diff --git a/build-cjs.js b/build-cjs.js new file mode 100644 index 0000000..78e4b53 --- /dev/null +++ b/build-cjs.js @@ -0,0 +1,16 @@ +// Converts the ESM library file to commonJS for backwards compatibility with older node.js versions and projects +// kind of dumb, but works better than all the "smarter" options I tried + +import { readFileSync, writeFileSync } from "node:fs"; + +const inFile = "lib/set-cookie.js"; +const outFile = "dist/set-cookie.cjs"; + +const header = `// Generated automatically from ${inFile}; see build-cjs.js\n\n`; +const cjsExports = `module.exports = parseSetCookie;\n`; // the other exports are properties on parseSetCookie + +const input = readFileSync(inFile, { encoding: "utf8" }); +const output = header + input.split("// EXPORTS")[0] + cjsExports; +writeFileSync(outFile, output); + +console.log(`Wrote ${output.length} bytes to ${outFile}`); diff --git a/.eslintrc.js b/dist/.eslintrc.cjs similarity index 82% rename from .eslintrc.js rename to dist/.eslintrc.cjs index 899c92b..bf4d23b 100644 --- a/.eslintrc.js +++ b/dist/.eslintrc.cjs @@ -1,7 +1,7 @@ "use strict"; module.exports = { // This isn't really meant for use in browsers, but some dependents such as nookie are. - // So, stick with ES5 to be nice. See #44 + // So, stick with ES5 (at least for the CJS version) to be nice. See #44 parserOptions: { ecmaVersion: 5 }, env: { node: true, diff --git a/lib/set-cookie.js b/lib/set-cookie.js index 2d74ca5..2c7b9b0 100644 --- a/lib/set-cookie.js +++ b/lib/set-cookie.js @@ -1,9 +1,8 @@ -"use strict"; - var defaultParseOptions = { decodeValues: true, map: false, silent: false, + split: "auto", // auto = split strings but not arrays }; function isForbiddenKey(key) { @@ -91,7 +90,7 @@ function parseNameValuePair(nameValuePairStr) { return { name: name, value: value }; } -function parse(input, options) { +function parseSetCookie(input, options) { options = options ? Object.assign({}, defaultParseOptions, options) : defaultParseOptions; @@ -129,20 +128,33 @@ function parse(input, options) { input = sch; } } - if (!Array.isArray(input)) { + + var split = options.split; + var isArray = Array.isArray(input); + + if (split === "auto") { + split = !isArray; + } + + if (!isArray) { input = [input]; } + input = input.filter(isNonEmptyString); + + if (split) { + input = input.map(splitCookiesString).flat(); + } + if (!options.map) { return input - .filter(isNonEmptyString) .map(function (str) { return parseString(str, options); }) .filter(Boolean); } else { var cookies = createNullObj(); - return input.filter(isNonEmptyString).reduce(function (cookies, str) { + return input.reduce(function (cookies, str) { var cookie = parseString(str, options); if (cookie && !isForbiddenKey(cookie.name)) { cookies[cookie.name] = cookie; @@ -236,7 +248,18 @@ function splitCookiesString(cookiesString) { return cookiesStrings; } -module.exports = parse; -module.exports.parse = parse; -module.exports.parseString = parseString; -module.exports.splitCookiesString = splitCookiesString; +// named export for CJS +parseSetCookie.parseSetCookie = parseSetCookie; +// for backwards compatibility +parseSetCookie.parse = parseSetCookie; +parseSetCookie.parseString = parseString; +parseSetCookie.splitCookiesString = splitCookiesString; + +// EXPORTS +// (this section is replaced by build-cjs.js) + +// named export for ESM +export { parseSetCookie }; +// for backwards compatibility +export default parseSetCookie; +export { parseSetCookie as parse, parseString, splitCookiesString }; diff --git a/package-lock.json b/package-lock.json index 25e47b3..c3cc49e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "husky": "^9.0.11", + "husky": "^9.1.7", "mocha": "^10.3.0", "prettier": "^3.2.5", "pretty-quick": "^4.0.0", @@ -305,9 +305,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", @@ -410,9 +410,9 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -999,12 +999,12 @@ } }, "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, "bin": { - "husky": "bin.mjs" + "husky": "bin.js" }, "engines": { "node": ">=18" @@ -1382,6 +1382,15 @@ "node": ">= 14.0.0" } }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1435,15 +1444,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -1626,9 +1626,9 @@ } }, "node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", "dev": true }, "node_modules/picocolors": { diff --git a/package.json b/package.json index e5b6893..beb1003 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,19 @@ "url": "http://nfriedly.com/" }, "files": [ - "lib" + "lib", + "dist" ], - "main": "./lib/set-cookie.js", + "main": "./dist/set-cookie.cjs", + "module": "./lib/set-cookie.js", + "type": "module", + "exports": { + ".": { + "module-sync": "./lib/set-cookie.js", + "import": "./lib/set-cookie.js", + "require": "./dist/set-cookie.cjs" + } + }, "sideEffects": false, "keywords": [ "set-cookie", @@ -26,7 +36,7 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "husky": "^9.0.11", + "husky": "^9.1.7", "mocha": "^10.3.0", "prettier": "^3.2.5", "pretty-quick": "^4.0.0", @@ -34,9 +44,11 @@ }, "scripts": { "lint": "eslint . --ignore-pattern '!.eslintrc.js'", - "test": "npm run lint && mocha", + "test": "npm run build && npm run lint && mocha", "autofix": "npm run lint -- --fix", - "precommit": "npm test" + "format": "npm run lint -- --fix", + "build": "node ./build-cjs.js", + "prepare": "husky" }, "license": "MIT", "prettier": { diff --git a/test/.eslintrc.js b/test/.eslintrc.cjs similarity index 100% rename from test/.eslintrc.js rename to test/.eslintrc.cjs diff --git a/test/cjs.cjs b/test/cjs.cjs new file mode 100644 index 0000000..e15df98 --- /dev/null +++ b/test/cjs.cjs @@ -0,0 +1,25 @@ +const assert = require("node:assert"); +const parseSetCookie = require("../dist/set-cookie.cjs"); + +describe("commonJS (CJS)", function () { + + it('should export all methods', function() { + assert(typeof parseSetCookie === 'function'); + assert(typeof parseSetCookie.parseSetCookie === 'function'); + assert(typeof parseSetCookie.parse === 'function'); + assert(typeof parseSetCookie.parseString === 'function'); + assert(typeof parseSetCookie.splitCookiesString === 'function'); + }); + + it('default export should work', function() { + var actual = parseSetCookie("foo=bar;"); + var expected = [{ name: "foo", value: "bar" }]; + assert.deepEqual(actual, expected); + }); + + it('named export should work', function() { + var actual = parseSetCookie.parse("foo=bar;"); + var expected = [{ name: "foo", value: "bar" }]; + assert.deepEqual(actual, expected); + }); +}); \ No newline at end of file diff --git a/test/fetch.js b/test/fetch.js index 0f610d0..43961cf 100644 --- a/test/fetch.js +++ b/test/fetch.js @@ -1,6 +1,5 @@ -"use strict"; -var assert = require("assert"); -var setCookie = require("../lib/set-cookie.js"); +import assert from "node:assert"; +import setCookie from "../lib/set-cookie.js"; describe("fetch", () => { before(() => { diff --git a/test/set-cookie-parser.js b/test/set-cookie-parser.js index 1e1e360..980de6e 100644 --- a/test/set-cookie-parser.js +++ b/test/set-cookie-parser.js @@ -1,27 +1,26 @@ -"use strict"; -var assert = require("assert"); -var setCookie = require("../lib/set-cookie.js"); +import assert from "node:assert"; +import { parseSetCookie } from "../lib/set-cookie.js"; describe("set-cookie-parser", function () { it("should parse a simple set-cookie header", function () { - var actual = setCookie.parse("foo=bar;"); + var actual = parseSetCookie("foo=bar;"); var expected = [{ name: "foo", value: "bar" }]; assert.deepEqual(actual, expected); }); it("should return empty array on falsy input", function () { var cookieStr = ""; - var actual = setCookie.parse(cookieStr); + var actual = parseSetCookie(cookieStr); var expected = []; assert.deepEqual(actual, expected); cookieStr = null; - actual = setCookie.parse(cookieStr); + actual = parseSetCookie(cookieStr); expected = []; assert.deepEqual(actual, expected); cookieStr = undefined; - actual = setCookie.parse(cookieStr); + actual = parseSetCookie(cookieStr); expected = []; assert.deepEqual(actual, expected); }); @@ -29,7 +28,7 @@ describe("set-cookie-parser", function () { it("should parse a complex set-cookie header", function () { var cookieStr = "foo=bar; Max-Age=1000; Domain=.example.com; Path=/; Expires=Tue, 01 Jul 2025 10:01:11 GMT; HttpOnly; Secure; Partitioned"; - var actual = setCookie.parse(cookieStr); + var actual = parseSetCookie(cookieStr); var expected = [ { name: "foo", @@ -49,7 +48,7 @@ describe("set-cookie-parser", function () { it("should parse a weird but valid cookie", function () { var cookieStr = "foo=bar=bar&foo=foo&John=Doe&Doe=John; Max-Age=1000; Domain=.example.com; Path=/; HttpOnly; Secure"; - var actual = setCookie.parse(cookieStr); + var actual = parseSetCookie(cookieStr); var expected = [ { name: "foo", @@ -66,13 +65,13 @@ describe("set-cookie-parser", function () { it("should parse a cookie with percent-encoding in the data", function () { var cookieStr = "foo=asdf%3Basdf%3Dtrue%3Basdf%3Dasdf%3Basdf%3Dtrue%40asdf"; - var actual = setCookie.parse(cookieStr); + var actual = parseSetCookie(cookieStr); var expected = [ { name: "foo", value: "asdf;asdf=true;asdf=asdf;asdf=true@asdf" }, ]; assert.deepEqual(actual, expected); - actual = setCookie.parse(cookieStr, { decodeValues: false }); + actual = parseSetCookie(cookieStr, { decodeValues: false }); expected = [ { name: "foo", @@ -81,7 +80,7 @@ describe("set-cookie-parser", function () { ]; assert.deepEqual(actual, expected); - actual = setCookie.parse(cookieStr, { decodeValues: true }); + actual = parseSetCookie(cookieStr, { decodeValues: true }); expected = [ { name: "foo", value: "asdf;asdf=true;asdf=asdf;asdf=true@asdf" }, ]; @@ -91,7 +90,7 @@ describe("set-cookie-parser", function () { it("should handle the case when value is not UTF-8 encoded", function () { var cookieStr = "foo=R%F3r%EB%80%8DP%FF%3B%2C%23%9A%0CU%8E%A2C8%D7%3C%3C%B0%DF%17%60%F7Y%DB%16%8BQ%D6%1A"; - var actual = setCookie.parse(cookieStr, { decodeValues: true }); + var actual = parseSetCookie(cookieStr, { decodeValues: true }); var expected = [ { name: "foo", @@ -107,7 +106,7 @@ describe("set-cookie-parser", function () { "bam=baz", "foo=bar; Max-Age=1000; Domain=.example.com; Path=/; Expires=Tue, 01 Jul 2025 10:01:11 GMT; HttpOnly; Secure", ]; - var actual = setCookie.parse(cookieStrs); + var actual = parseSetCookie(cookieStrs); var expected = [ { name: "bam", value: "baz" }, { @@ -133,7 +132,7 @@ describe("set-cookie-parser", function () { ], }, }; - var actual = setCookie.parse(mockResponse); + var actual = parseSetCookie(mockResponse); var expected = [ { name: "bam", value: "baz" }, { @@ -160,7 +159,7 @@ describe("set-cookie-parser", function () { ], }, }; - var actual = setCookie.parse(mockResponse); + var actual = parseSetCookie(mockResponse); var expected = [ { name: "bam", value: "baz" }, { @@ -182,7 +181,7 @@ describe("set-cookie-parser", function () { var mockResponse = { headers: {}, }; - var actual = setCookie.parse(mockResponse); + var actual = parseSetCookie(mockResponse); var expected = []; assert.deepEqual(actual, expected); }); @@ -190,7 +189,7 @@ describe("set-cookie-parser", function () { it("should return object of cookies when result option is set to map", function () { var cookieStr = "foo=bar; Max-Age=1000; Domain=.example.com; Path=/; Expires=Tue, 01 Jul 2025 10:01:11 GMT; HttpOnly; Secure"; - var actual = setCookie.parse(cookieStr, { map: true }); + var actual = parseSetCookie(cookieStr, { map: true }); var expected = { foo: { name: "foo", @@ -208,43 +207,93 @@ describe("set-cookie-parser", function () { it("should return empty object on falsy input when result options is set to map", function () { var cookieStr = ""; - var actual = setCookie.parse(cookieStr, { map: true }); + var actual = parseSetCookie(cookieStr, { map: true }); var expected = {}; assert.deepEqual(actual, expected); cookieStr = null; - actual = setCookie.parse(cookieStr, { map: true }); + actual = parseSetCookie(cookieStr, { map: true }); expected = {}; assert.deepEqual(actual, expected); cookieStr = undefined; - actual = setCookie.parse(cookieStr, { map: true }); + actual = parseSetCookie(cookieStr, { map: true }); expected = {}; assert.deepEqual(actual, expected); }); it("should have empty name string, and value is the name-value-pair if the name-value-pair string lacks a = character", function () { - var actual = setCookie.parse("foo;"); + var actual = parseSetCookie("foo;"); var expected = [{ name: "", value: "foo" }]; assert.deepEqual(actual, expected); - actual = setCookie.parse("foo;SameSite=None;Secure"); + actual = parseSetCookie("foo;SameSite=None;Secure"); expected = [{ name: "", value: "foo", sameSite: "None", secure: true }]; assert.deepEqual(actual, expected); }); it("should skip cookies that could pollute the object prototype", function () { - var actual = setCookie.parse("__proto__=test;"); + var actual = parseSetCookie("__proto__=test;"); var expected = []; assert.deepEqual(actual, expected); - actual = setCookie.parse("foo;__proto__=None;Secure"); + actual = parseSetCookie("foo;__proto__=None;Secure"); expected = [{ name: "", value: "foo", secure: true }]; assert.deepEqual(actual, expected); - actual = setCookie.parse("__proto__=test;", { map: true }); + actual = parseSetCookie("__proto__=test;", { map: true }); expected = {}; assert.deepEqual(actual, expected); }); + + describe("split option", function () { + const cookieA = "a=b"; + const cookieB = "b=c"; + const cookieC = "c=d"; + const combinedCookies = `${cookieA}, ${cookieB}`; + + it("should split when true", function () { + var actual = parseSetCookie(combinedCookies, { split: true }); + var expected = [ + { name: "a", value: "b" }, + { name: "b", value: "c" }, + ]; + assert.deepEqual(actual, expected); + }); + + it("should not split when false", function () { + var actual = parseSetCookie(combinedCookies, { split: false }); + var expected = [{ name: "a", value: "b, b=c" }]; + assert.deepEqual(actual, expected); + }); + + it("should split strings by default", function () { + var actual = parseSetCookie(combinedCookies); + var expected = [ + { name: "a", value: "b" }, + { name: "b", value: "c" }, + ]; + assert.deepEqual(actual, expected); + }); + + it("should not split arrays by default", function () { + var actual = parseSetCookie([combinedCookies, cookieC]); + var expected = [ + { name: "a", value: "b, b=c" }, + { name: "c", value: "d" }, + ]; + assert.deepEqual(actual, expected); + }); + + it("should split arrays when true", function () { + var actual = parseSetCookie([combinedCookies, cookieC], { split: true }); + var expected = [ + { name: "a", value: "b" }, + { name: "b", value: "c" }, + { name: "c", value: "d" }, + ]; + assert.deepEqual(actual, expected); + }); + }); }); diff --git a/test/split-cookies-string.js b/test/split-cookies-string.js index 1b044d7..73032a4 100644 --- a/test/split-cookies-string.js +++ b/test/split-cookies-string.js @@ -1,8 +1,5 @@ -"use strict"; -var assert = require("assert"); -var setCookie = require("../lib/set-cookie.js"); - -const splitCookiesString = setCookie.splitCookiesString; +import assert from "node:assert"; +import { splitCookiesString } from "../lib/set-cookie.js"; const array = ["a", "b"]; diff --git a/test/warnings.js b/test/warnings.js index e2781d5..3c9ced0 100644 --- a/test/warnings.js +++ b/test/warnings.js @@ -1,8 +1,7 @@ -"use strict"; -var sinon = require("sinon"); -var setCookie = require("../lib/set-cookie.js"); +import sinon from "sinon"; +import setCookie from "../lib/set-cookie.js"; -describe("set-cookie-parser", function () { +describe("warnings", function () { var sandbox = sinon.createSandbox(); afterEach(function () {