Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -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",
},
};
1 change: 0 additions & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/node_modules/
/coverage/
/dist/set-cookie.cjs
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npm test
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
83 changes: 25 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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
});

Expand Down Expand Up @@ -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);
});
```
Expand Down Expand Up @@ -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
// ...
Expand All @@ -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

Expand Down
16 changes: 16 additions & 0 deletions build-cjs.js
Original file line number Diff line number Diff line change
@@ -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}`);
2 changes: 1 addition & 1 deletion .eslintrc.js → dist/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
43 changes: 33 additions & 10 deletions lib/set-cookie.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 };
Loading