diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 62562b7..0000000
--- a/.eslintignore
+++ /dev/null
@@ -1,2 +0,0 @@
-coverage
-node_modules
diff --git a/.eslintrc.yml b/.eslintrc.yml
deleted file mode 100644
index 44febf6..0000000
--- a/.eslintrc.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-extends:
- - plugin:markdown/recommended
-plugins:
- - markdown
-overrides:
- - files: '**/*.md'
- processor: 'markdown/markdown'
-rules:
- eol-last: error
- indent: ["error", 2, { "SwitchCase": 1 }]
- no-trailing-spaces: error
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9da9752..92c5aba 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,253 +1,27 @@
name: ci
on:
-- pull_request
-- push
+ - pull_request
+ - push
jobs:
test:
- runs-on: ubuntu-20.04
+ name: Node.js ${{ matrix.node-version }}
+ runs-on: ubuntu-latest
strategy:
matrix:
- name:
- - Node.js 0.6
- - Node.js 0.8
- - Node.js 0.10
- - Node.js 0.12
- - io.js 1.x
- - io.js 2.x
- - io.js 3.x
- - Node.js 4.x
- - Node.js 5.x
- - Node.js 6.x
- - Node.js 7.x
- - Node.js 8.x
- - Node.js 9.x
- - Node.js 10.x
- - Node.js 11.x
- - Node.js 12.x
- - Node.js 13.x
- - Node.js 14.x
- - Node.js 15.x
- - Node.js 16.x
- - Node.js 17.x
- - Node.js 18.x
- - Node.js 19.x
- - Node.js 20.x
- - Node.js 21.x
-
- include:
- - name: Node.js 0.6
- node-version: "0.6"
- npm-i: mocha@1.21.5
- npm-rm: beautify-benchmark benchmark nyc top-sites
-
- - name: Node.js 0.8
- node-version: "0.8"
- npm-i: mocha@2.5.3
- npm-rm: beautify-benchmark benchmark nyc top-sites
-
- - name: Node.js 0.10
- node-version: "0.10"
- npm-i: mocha@3.5.3 nyc@10.3.2
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 0.12
- node-version: "0.12"
- npm-i: mocha@3.5.3 nyc@10.3.2
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: io.js 1.x
- node-version: "1.8"
- npm-i: mocha@3.5.3 nyc@10.3.2
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: io.js 2.x
- node-version: "2.5"
- npm-i: mocha@3.5.3 nyc@10.3.2
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: io.js 3.x
- node-version: "3.3"
- npm-i: mocha@3.5.3 nyc@10.3.2
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 4.x
- node-version: "4.9"
- npm-i: mocha@5.2.0 nyc@11.9.0
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 5.x
- node-version: "5.12"
- npm-i: mocha@5.2.0 nyc@11.9.0
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 6.x
- node-version: "6.17"
- npm-i: mocha@6.2.2 nyc@14.1.1
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 7.x
- node-version: "7.10"
- npm-i: mocha@6.2.2 nyc@14.1.1
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 8.x
- node-version: "8.17"
- npm-i: mocha@7.1.2 nyc@14.1.1
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 9.x
- node-version: "9.11"
- npm-i: mocha@7.1.2 nyc@14.1.1
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 10.x
- node-version: "10.24"
- npm-i: mocha@8.4.0
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 11.x
- node-version: "11.15"
- npm-i: mocha@8.4.0
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 12.x
- node-version: "12.22"
- npm-i: mocha@9.2.2
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 13.x
- node-version: "13.14"
- npm-i: mocha@9.2.2
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 14.x
- node-version: "14.21"
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 15.x
- node-version: "15.14"
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 16.x
- node-version: "16.20"
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 17.x
- node-version: "17.9"
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 18.x
- node-version: "18.18"
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 19.x
- node-version: "19.9"
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 20.x
- node-version: "20.9"
- npm-rm: beautify-benchmark benchmark top-sites
-
- - name: Node.js 21.x
- node-version: "21.1"
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Install Node.js ${{ matrix.node-version }}
- shell: bash -eo pipefail -l {0}
- run: |
- if [[ "${{ matrix.node-version }}" == 0.6* ]]; then
- sudo sh -c 'echo "deb http://us.archive.ubuntu.com/ubuntu/ bionic universe" >> /etc/apt/sources.list'
- sudo sh -c 'echo "deb http://security.ubuntu.com/ubuntu bionic-security main" >> /etc/apt/sources.list'
- sudo apt-get update
- sudo apt-get install g++-4.8 gcc-4.8 libssl1.0-dev python2 python-is-python2
- export CC=/usr/bin/gcc-4.8
- export CXX=/usr/bin/g++-4.8
- fi
- nvm install --default ${{ matrix.node-version }}
- if [[ "${{ matrix.node-version }}" == 0.* && "$(cut -d. -f2 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then
- nvm install --alias=npm 0.10
- nvm use ${{ matrix.node-version }}
- if [[ "$(npm -v)" == 1.1.* ]]; then
- nvm exec npm npm install -g npm@1.1
- ln -fs "$(which npm)" "$(dirname "$(nvm which npm)")/npm"
- else
- sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")"
- fi
- npm config set strict-ssl false
- fi
- dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
-
- - name: Configure npm
- run: |
- if [[ "$(npm config get package-lock)" == "true" ]]; then
- npm config set package-lock false
- else
- npm config set shrinkwrap false
- fi
-
- - name: Remove npm module(s) ${{ matrix.npm-rm }}
- run: npm rm --silent --save-dev ${{ matrix.npm-rm }}
- if: matrix.npm-rm != ''
-
- - name: Install npm module(s) ${{ matrix.npm-i }}
- run: npm install --save-dev ${{ matrix.npm-i }}
- if: matrix.npm-i != ''
-
- - name: Setup Node.js version-specific dependencies
- shell: bash
- run: |
- # eslint for linting
- # - remove on Node.js < 12
- if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then
- node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
- grep -E '^eslint(-|$)' | \
- sort -r | \
- xargs -n1 npm rm --silent --save-dev
- fi
-
- - name: Install Node.js dependencies
- run: npm install
-
- - name: List environment
- id: list_env
- shell: bash
- run: |
- echo "node@$(node -v)"
- echo "npm@$(npm -v)"
- npm -s ls ||:
- (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT"
-
- - name: Run tests
- shell: bash
- run: |
- if npm -ps ls nyc | grep -q nyc; then
- npm run test-ci
- else
- npm test
- fi
-
- - name: Lint code
- if: steps.list_env.outputs.eslint != ''
- run: npm run lint
-
- - name: Collect code coverage
- uses: coverallsapp/github-action@master
- if: steps.list_env.outputs.nyc != ''
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- flag-name: run-${{ matrix.test_number }}
- parallel: true
-
- coverage:
- needs: test
- runs-on: ubuntu-latest
+ node-version:
+ - "16"
+ - "18"
+ - "20"
+ - "*"
steps:
- - name: Upload code coverage
- uses: coverallsapp/github-action@master
- with:
- github-token: ${{ secrets.github_token }}
- parallel-finished: true
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ - run: npm ci
+ - run: npm test
+ - uses: codecov/codecov-action@v4
+ with:
+ name: Node.js ${{ matrix.node-version }}
diff --git a/.gitignore b/.gitignore
index f15b98e..e9c5519 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@ coverage/
node_modules/
npm-debug.log
package-lock.json
+dist/
+*.tsbuildinfo
diff --git a/README.md b/README.md
index 71fdac1..cfbc5e6 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ $ npm install cookie
## API
```js
-var cookie = require('cookie');
+var cookie = require("cookie");
```
### cookie.parse(str, options)
@@ -31,7 +31,7 @@ The `str` argument is the string representing a `Cookie` header value and `optio
optional object containing additional parsing options.
```js
-var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2');
+var cookies = cookie.parse("foo=bar; equation=E%3Dmc%5E2");
// { foo: 'bar', equation: 'E=mc^2' }
```
@@ -58,7 +58,7 @@ name for the cookie, the `value` argument is the value to set the cookie to, and
argument is an optional object containing additional serialization options.
```js
-var setCookie = cookie.serialize('foo', 'bar');
+var setCookie = cookie.serialize("foo", "bar");
// foo=bar
```
@@ -127,9 +127,9 @@ is considered the ["default path"][rfc-6265-5.1.4].
Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute][rfc-west-cookie-priority-00-4.1].
- - `'low'` will set the `Priority` attribute to `Low`.
- - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
- - `'high'` will set the `Priority` attribute to `High`.
+- `'low'` will set the `Priority` attribute to `Low`.
+- `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
+- `'high'` will set the `Priority` attribute to `High`.
More information about the different priority levels can be found in
[the specification][rfc-west-cookie-priority-00-4.1].
@@ -141,11 +141,11 @@ This also means many clients may ignore this attribute until they understand it.
Specifies the `boolean` or `string` to be the value for the [`SameSite` `Set-Cookie` attribute][rfc-6265bis-09-5.4.7].
- - `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
- - `false` will not set the `SameSite` attribute.
- - `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
- - `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.
- - `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
+- `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
+- `false` will not set the `SameSite` attribute.
+- `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
+- `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.
+- `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
More information about the different enforcement levels can be found in
[the specification][rfc-6265bis-09-5.4.7].
@@ -167,10 +167,10 @@ The following example uses this module in conjunction with the Node.js core HTTP
to prompt a user for their name and display it back on future visits.
```js
-var cookie = require('cookie');
-var escapeHtml = require('escape-html');
-var http = require('http');
-var url = require('url');
+var cookie = require("cookie");
+var escapeHtml = require("escape-html");
+var http = require("http");
+var url = require("url");
function onRequest(req, res) {
// Parse the query string
@@ -178,35 +178,40 @@ function onRequest(req, res) {
if (query && query.name) {
// Set a new cookie with the name
- res.setHeader('Set-Cookie', cookie.serialize('name', String(query.name), {
- httpOnly: true,
- maxAge: 60 * 60 * 24 * 7 // 1 week
- }));
+ res.setHeader(
+ "Set-Cookie",
+ cookie.serialize("name", String(query.name), {
+ httpOnly: true,
+ maxAge: 60 * 60 * 24 * 7, // 1 week
+ }),
+ );
// Redirect back after setting cookie
res.statusCode = 302;
- res.setHeader('Location', req.headers.referer || '/');
+ res.setHeader("Location", req.headers.referer || "/");
res.end();
return;
}
// Parse the cookies on the request
- var cookies = cookie.parse(req.headers.cookie || '');
+ var cookies = cookie.parse(req.headers.cookie || "");
// Get the visitor name set in the cookie
var name = cookies.name;
- res.setHeader('Content-Type', 'text/html; charset=UTF-8');
+ res.setHeader("Content-Type", "text/html; charset=UTF-8");
if (name) {
- res.write('
Welcome back, ' + escapeHtml(name) + '!
');
+ res.write("Welcome back, " + escapeHtml(name) + "!
");
} else {
- res.write('Hello, new visitor!
');
+ res.write("Hello, new visitor!
");
}
res.write('');
+ res.write(
+ ' ',
+ );
+ res.end("");
}
http.createServer(onRequest).listen(3000);
diff --git a/benchmark/index.js b/benchmark/index.js
deleted file mode 100644
index 8a39f3a..0000000
--- a/benchmark/index.js
+++ /dev/null
@@ -1,34 +0,0 @@
-var fs = require('fs')
-var path = require('path')
-var spawn = require('child_process').spawn
-
-var exe = process.argv[0]
-var cwd = process.cwd()
-
-for (var dep in process.versions) {
- console.log(' %s@%s', dep, process.versions[dep])
-}
-
-console.log('')
-
-runScripts(fs.readdirSync(__dirname))
-
-function runScripts (fileNames) {
- var fileName = fileNames.shift()
-
- if (!fileName) return
- if (!/\.js$/i.test(fileName)) return runScripts(fileNames)
- if (fileName.toLowerCase() === 'index.js') return runScripts(fileNames)
-
- var fullPath = path.join(__dirname, fileName)
-
- console.log('> %s %s', exe, path.relative(cwd, fullPath))
-
- var proc = spawn(exe, [fullPath], {
- 'stdio': 'inherit'
- })
-
- proc.on('exit', function () {
- runScripts(fileNames)
- })
-}
diff --git a/benchmark/parse-top.js b/benchmark/parse-top.js
deleted file mode 100644
index 4cbfd00..0000000
--- a/benchmark/parse-top.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Module dependencies.
- */
-
-var benchmark = require('benchmark')
-var benchmarks = require('beautify-benchmark')
-var top = require('./parse-top.json')
-
-/**
- * Globals for benchmark.js
- */
-
-global.cookie = require('..')
-
-var suite = new benchmark.Suite()
-
-Object.keys(top).forEach(function (domain) {
- suite.add({
- name: 'parse ' + domain,
- minSamples: 100,
- fn: 'var val = cookie.parse(' + JSON.stringify(top[domain]) + ')'
- })
-})
-
-suite.on('start', function () {
- process.stdout.write(' cookie.parse - top sites\n\n')
-})
-
-suite.on('cycle', function (event) {
- benchmarks.add(event.target)
-})
-
-suite.on('complete', function () {
- benchmarks.log()
-})
-
-suite.run({ async: false })
diff --git a/benchmark/parse.js b/benchmark/parse.js
deleted file mode 100644
index 567f4b6..0000000
--- a/benchmark/parse.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * Module dependencies.
- */
-
-var benchmark = require('benchmark')
-var benchmarks = require('beautify-benchmark')
-
-/**
- * Globals for benchmark.js
- */
-
-global.cookie = require('..')
-
-var suite = new benchmark.Suite()
-
-suite.add({
- name: 'simple',
- minSamples: 100,
- fn: 'var val = cookie.parse("foo=bar")'
-})
-
-suite.add({
- name: 'decode',
- minSamples: 100,
- fn: 'var val = cookie.parse("foo=hello%20there!")'
-})
-
-suite.add({
- name: 'unquote',
- minSamples: 100,
- fn: 'var val = cookie.parse("foo=\\"foo bar\\"")'
-})
-
-suite.add({
- name: 'duplicates',
- minSamples: 100,
- fn: 'var val = cookie.parse(' + JSON.stringify(gencookies(2) + '; ' + gencookies(2)) + ')'
-})
-
-suite.add({
- name: '10 cookies',
- minSamples: 100,
- fn: 'var val = cookie.parse(' + JSON.stringify(gencookies(10)) + ')'
-})
-
-suite.add({
- name: '100 cookies',
- minSamples: 100,
- fn: 'var val = cookie.parse(' + JSON.stringify(gencookies(100)) + ')'
-})
-
-suite.on('start', function () {
- process.stdout.write(' cookie.parse - generic\n\n')
-})
-
-suite.on('cycle', function (event) {
- benchmarks.add(event.target)
-})
-
-suite.on('complete', function onComplete () {
- benchmarks.log()
-})
-
-suite.run({ async: false })
-
-function gencookies (num) {
- var str = ''
-
- for (var i = 0; i < num; i++) {
- str += '; foo' + i + '=bar'
- }
-
- return str.slice(2)
-}
diff --git a/package.json b/package.json
index f498ea7..ed648a8 100644
--- a/package.json
+++ b/package.json
@@ -1,44 +1,42 @@
{
"name": "cookie",
- "description": "HTTP server cookie parsing and serialization",
"version": "0.7.1",
- "author": "Roman Shtylman ",
- "contributors": [
- "Douglas Christopher Wilson "
- ],
- "license": "MIT",
+ "description": "HTTP server cookie parsing and serialization",
"keywords": [
"cookie",
"cookies"
],
"repository": "jshttp/cookie",
- "devDependencies": {
- "beautify-benchmark": "0.2.4",
- "benchmark": "2.1.4",
- "eslint": "8.53.0",
- "eslint-plugin-markdown": "3.0.1",
- "mocha": "10.2.0",
- "nyc": "15.1.0",
- "safe-buffer": "5.2.1",
- "top-sites": "1.1.194"
- },
+ "license": "MIT",
+ "author": "Roman Shtylman ",
+ "contributors": [
+ "Douglas Christopher Wilson "
+ ],
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
"files": [
- "HISTORY.md",
- "LICENSE",
- "README.md",
- "SECURITY.md",
- "index.js"
+ "dist/"
],
- "main": "index.js",
+ "scripts": {
+ "bench": "vitest bench",
+ "build": "ts-scripts build",
+ "format": "ts-scripts format",
+ "prepare": "ts-scripts install",
+ "prepublishOnly": "npm run build",
+ "specs": "ts-scripts specs",
+ "test": "ts-scripts test"
+ },
+ "devDependencies": {
+ "@borderless/ts-scripts": "^0.15.0",
+ "@vitest/coverage-v8": "^2.1.2",
+ "top-sites": "1.1.194",
+ "typescript": "^5.6.2",
+ "vitest": "^2.1.2"
+ },
"engines": {
- "node": ">= 0.6"
+ "node": ">=16"
},
- "scripts": {
- "bench": "node benchmark/index.js",
- "lint": "eslint .",
- "test": "mocha --reporter spec --bail --check-leaks test/",
- "test-ci": "nyc --reporter=lcov --reporter=text npm test",
- "test-cov": "nyc --reporter=html --reporter=text npm test",
- "update-bench": "node scripts/update-benchmark.js"
+ "ts-scripts": {
+ "project": "tsconfig.build.json"
}
}
diff --git a/benchmark/parse-top.json b/scripts/parse-top.json
similarity index 100%
rename from benchmark/parse-top.json
rename to scripts/parse-top.json
diff --git a/scripts/update-benchmark.js b/scripts/update-benchmark.js
index 23cdcfe..ebe032d 100644
--- a/scripts/update-benchmark.js
+++ b/scripts/update-benchmark.js
@@ -1,69 +1,90 @@
-'use strict'
+"use strict";
-var fs = require('fs')
-var http = require('http')
-var https = require('https')
-var path = require('path')
-var topSites = require('top-sites')
-var url = require('url')
+var fs = require("fs");
+var http = require("http");
+var https = require("https");
+var path = require("path");
+var topSites = require("top-sites");
+var url = require("url");
-var BENCH_COOKIES_FILE = path.join(__dirname, '..', 'benchmark', 'parse-top.json')
+var BENCH_COOKIES_FILE = path.join(__dirname, "parse-top.json");
getAllCookies(topSites.slice(0, 20), function (err, cookies) {
- if (err) throw err
- var str = '{\n' +
- Object.keys(cookies).sort().map(function (key) {
- return ' ' + JSON.stringify(key) + ': ' + JSON.stringify(cookies[key])
- }).join(',\n') +
- '\n}\n'
- fs.writeFileSync(BENCH_COOKIES_FILE, str)
-})
+ if (err) throw err;
+ var str =
+ "{\n" +
+ Object.keys(cookies)
+ .sort()
+ .map(function (key) {
+ return " " + JSON.stringify(key) + ": " + JSON.stringify(cookies[key]);
+ })
+ .join(",\n") +
+ "\n}\n";
+ fs.writeFileSync(BENCH_COOKIES_FILE, str);
+});
-function get (href, callback) {
- var protocol = url.parse(href, false, true).protocol
- var proto = protocol === 'https:' ? https : http
+function get(href, callback) {
+ var protocol = url.parse(href, false, true).protocol;
+ var proto = protocol === "https:" ? https : http;
- proto.get(href)
- .on('error', callback)
- .on('response', function (res) {
- if (res.headers.location && res.statusCode >= 300 && res.statusCode < 400) {
- get(url.resolve(href, res.headers.location), callback)
+ proto
+ .get(href)
+ .on("error", callback)
+ .on("response", function (res) {
+ if (
+ res.headers.location &&
+ res.statusCode >= 300 &&
+ res.statusCode < 400
+ ) {
+ get(url.resolve(href, res.headers.location), callback);
} else {
- callback(null, res)
+ callback(null, res);
}
- })
+ });
}
-function getAllCookies (sites, callback) {
- var all = Object.create(null)
- var wait = sites.length
+function getAllCookies(sites, callback) {
+ var all = Object.create(null);
+ var wait = sites.length;
sites.forEach(function (site) {
getCookies(site, function (err, cookies) {
if (!err && cookies.length) {
- all[site.rootDomain] = cookies.map(obfuscate).join('; ')
+ all[site.rootDomain] = cookies.map(obfuscate).join("; ");
}
if (!--wait) {
- callback(null, all)
+ callback(null, all);
}
- })
- })
+ });
+ });
}
-function getCookies (site, callback) {
- var href = url.format({ hostname: site.rootDomain, protocol: 'http' })
+function getCookies(site, callback) {
+ var href = url.format({ hostname: site.rootDomain, protocol: "http" });
get(href, function (err, res) {
- if (err) return callback(err)
- var cookies = (res.headers['set-cookie'] || []).map(function (c) { return c.split(';')[0] })
- callback(null, cookies)
- })
+ if (err) return callback(err);
+ var cookies = (res.headers["set-cookie"] || []).map(function (c) {
+ return c.split(";")[0];
+ });
+ callback(null, cookies);
+ });
}
-function obfuscate (str) {
+function obfuscate(str) {
return str
- .replace(/%[0-9a-f]{2}/gi, function () { return '%__' })
- .replace(/[a-z]/g, function () { return 'l' })
- .replace(/[A-Z]/g, function () { return 'U' })
- .replace(/[0-9]/g, function () { return '0' })
- .replace(/%__/g, function () { return '%22' })
+ .replace(/%[0-9a-f]{2}/gi, function () {
+ return "%__";
+ })
+ .replace(/[a-z]/g, function () {
+ return "l";
+ })
+ .replace(/[A-Z]/g, function () {
+ return "U";
+ })
+ .replace(/[0-9]/g, function () {
+ return "0";
+ })
+ .replace(/%__/g, function () {
+ return "%22";
+ });
}
diff --git a/index.js b/src/index.ts
similarity index 50%
rename from index.js
rename to src/index.ts
index 51a58cb..d887761 100644
--- a/index.js
+++ b/src/index.ts
@@ -1,27 +1,3 @@
-/*!
- * cookie
- * Copyright(c) 2012-2014 Roman Shtylman
- * Copyright(c) 2015 Douglas Christopher Wilson
- * MIT Licensed
- */
-
-'use strict';
-
-/**
- * Module exports.
- * @public
- */
-
-exports.parse = parse;
-exports.serialize = serialize;
-
-/**
- * Module variables.
- * @private
- */
-
-var __toString = Object.prototype.toString
-
/**
* RegExp to match cookie-name in RFC 6265 sec 4.1.1
* This refers out to the obsoleted definition of token in RFC 2616 sec 2.2
@@ -33,8 +9,7 @@ var __toString = Object.prototype.toString
* "*" / "+" / "-" / "." / "^" / "_" /
* "`" / "|" / "~" / DIGIT / ALPHA
*/
-
-var cookieNameRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
+const cookieNameRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
/**
* RegExp to match cookie-value in RFC 6265 sec 4.1.1
@@ -45,8 +20,8 @@ var cookieNameRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
* ; whitespace DQUOTE, comma, semicolon,
* ; and backslash
*/
-
-var cookieValueRegExp = /^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u005D-\u007E]*\1$/;
+const cookieValueRegExp =
+ /^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u005D-\u007E]*\1$/;
/**
* RegExp to match domain-value in RFC 6265 sec 4.1.1
@@ -71,8 +46,8 @@ var cookieValueRegExp = /^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u00
* character is not permitted, but a trailing %x2E ("."), if present, will
* cause the user agent to ignore the attribute.)
*/
-
-var domainValueRegExp = /^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
+const domainValueRegExp =
+ /^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
/**
* RegExp to match path-value in RFC 6265 sec 4.1.1
@@ -81,90 +56,105 @@ var domainValueRegExp = /^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([
* CHAR = %x01-7F
* ; defined in RFC 5234 appendix B.1
*/
+const pathValueRegExp = /^[\u0020-\u003A\u003D-\u007E]*$/;
-var pathValueRegExp = /^[\u0020-\u003A\u003D-\u007E]*$/;
+/**
+ * Parse options.
+ */
+export interface ParseOptions {
+ decode?: (str: string) => string;
+}
/**
* Parse a cookie header.
*
* Parse the given cookie header string into an object
* The object has the various cookies as keys(names) => values
- *
- * @param {string} str
- * @param {object} [opt]
- * @return {object}
- * @public
*/
-
-function parse(str, opt) {
- if (typeof str !== 'string') {
- throw new TypeError('argument str must be a string');
- }
-
- var obj = {};
- var len = str.length;
+export function parse(
+ str: string,
+ options?: ParseOptions,
+): Record {
+ const obj: Record = {};
+ const len = str.length;
// RFC 6265 sec 4.1.1, RFC 2616 2.2 defines a cookie name consists of one char minimum, plus '='.
if (len < 2) return obj;
- var dec = (opt && opt.decode) || decode;
- var index = 0;
- var eqIdx = 0;
- var endIdx = 0;
+ const dec = options?.decode || decode;
+ let index = 0;
do {
- eqIdx = str.indexOf('=', index);
+ const eqIdx = str.indexOf("=", index);
if (eqIdx === -1) break; // No more cookie pairs.
- endIdx = str.indexOf(';', index);
+ const colonIdx = str.indexOf(";", index);
+ const endIdx = colonIdx === -1 ? len : colonIdx;
- if (endIdx === -1) {
- endIdx = len;
- } else if (eqIdx > endIdx) {
+ if (eqIdx > endIdx) {
// backtrack on prior semicolon
- index = str.lastIndexOf(';', eqIdx - 1) + 1;
+ index = str.lastIndexOf(";", eqIdx - 1) + 1;
continue;
}
- var keyStartIdx = startIndex(str, index, eqIdx);
- var keyEndIdx = endIndex(str, eqIdx, keyStartIdx);
- var key = str.slice(keyStartIdx, keyEndIdx);
+ const keyStartIdx = startIndex(str, index, eqIdx);
+ const keyEndIdx = endIndex(str, eqIdx, keyStartIdx);
+ const key = str.slice(keyStartIdx, keyEndIdx);
// only assign once
if (!obj.hasOwnProperty(key)) {
- var valStartIdx = startIndex(str, eqIdx + 1, endIdx);
- var valEndIdx = endIndex(str, endIdx, valStartIdx);
+ let valStartIdx = startIndex(str, eqIdx + 1, endIdx);
+ let valEndIdx = endIndex(str, endIdx, valStartIdx);
- if (str.charCodeAt(valStartIdx) === 0x22 /* " */ && str.charCodeAt(valEndIdx - 1) === 0x22 /* " */) {
+ if (
+ str.charCodeAt(valStartIdx) === 0x22 /* " */ &&
+ str.charCodeAt(valEndIdx - 1) === 0x22 /* " */
+ ) {
valStartIdx++;
valEndIdx--;
}
- var val = str.slice(valStartIdx, valEndIdx);
+ const val = str.slice(valStartIdx, valEndIdx);
obj[key] = tryDecode(val, dec);
}
- index = endIdx + 1
+ index = endIdx + 1;
} while (index < len);
return obj;
}
-function startIndex(str, index, max) {
+function startIndex(str: string, index: number, max: number) {
do {
- var code = str.charCodeAt(index);
+ const code = str.charCodeAt(index);
if (code !== 0x20 /* */ && code !== 0x09 /* \t */) return index;
} while (++index < max);
return max;
}
-function endIndex(str, index, min) {
+function endIndex(str: string, index: number, min: number) {
while (index > min) {
- var code = str.charCodeAt(--index);
+ const code = str.charCodeAt(--index);
if (code !== 0x20 /* */ && code !== 0x09 /* \t */) return index + 1;
}
return min;
}
+/**
+ * Serialize options.
+ */
+export interface SerializeOptions {
+ encode?: (str: string) => string;
+ maxAge?: number;
+ domain?: string;
+ path?: string;
+ expires?: Date;
+ httpOnly?: boolean;
+ secure?: boolean;
+ partitioned?: boolean;
+ priority?: "low" | "medium" | "high";
+ sameSite?: boolean | "lax" | "strict" | "none";
+}
+
/**
* Serialize data into a cookie header.
*
@@ -173,120 +163,108 @@ function endIndex(str, index, min) {
*
* serialize('foo', 'bar', { httpOnly: true })
* => "foo=bar; httpOnly"
- *
- * @param {string} name
- * @param {string} val
- * @param {object} [opt]
- * @return {string}
- * @public
*/
-
-function serialize(name, val, opt) {
- var enc = (opt && opt.encode) || encodeURIComponent;
-
- if (typeof enc !== 'function') {
- throw new TypeError('option encode is invalid');
+export function serialize(
+ name: string,
+ val: string,
+ opt?: SerializeOptions,
+): string {
+ const enc = opt?.encode || encodeURIComponent;
+
+ if (typeof enc !== "function") {
+ throw new TypeError("option encode is invalid");
}
if (!cookieNameRegExp.test(name)) {
- throw new TypeError('argument name is invalid');
+ throw new TypeError("argument name is invalid");
}
- var value = enc(val);
+ const value = enc(val);
if (!cookieValueRegExp.test(value)) {
- throw new TypeError('argument val is invalid');
+ throw new TypeError("argument val is invalid");
}
- var str = name + '=' + value;
+ let str = name + "=" + value;
if (!opt) return str;
- if (null != opt.maxAge) {
- var maxAge = Math.floor(opt.maxAge);
-
- if (!isFinite(maxAge)) {
- throw new TypeError('option maxAge is invalid')
+ if (opt.maxAge !== undefined) {
+ if (!Number.isInteger(opt.maxAge)) {
+ throw new TypeError("option maxAge is invalid");
}
- str += '; Max-Age=' + maxAge;
+ str += "; Max-Age=" + opt.maxAge;
}
if (opt.domain) {
if (!domainValueRegExp.test(opt.domain)) {
- throw new TypeError('option domain is invalid');
+ throw new TypeError("option domain is invalid");
}
- str += '; Domain=' + opt.domain;
+ str += "; Domain=" + opt.domain;
}
if (opt.path) {
if (!pathValueRegExp.test(opt.path)) {
- throw new TypeError('option path is invalid');
+ throw new TypeError("option path is invalid");
}
- str += '; Path=' + opt.path;
+ str += "; Path=" + opt.path;
}
if (opt.expires) {
- var expires = opt.expires
-
- if (!isDate(expires) || isNaN(expires.valueOf())) {
- throw new TypeError('option expires is invalid');
+ if (
+ typeof opt.expires.toUTCString !== "function" ||
+ !Number.isFinite(opt.expires.valueOf())
+ ) {
+ throw new TypeError("option expires is invalid");
}
- str += '; Expires=' + expires.toUTCString()
+ str += "; Expires=" + opt.expires.toUTCString();
}
if (opt.httpOnly) {
- str += '; HttpOnly';
+ str += "; HttpOnly";
}
if (opt.secure) {
- str += '; Secure';
+ str += "; Secure";
}
if (opt.partitioned) {
- str += '; Partitioned'
+ str += "; Partitioned";
}
if (opt.priority) {
- var priority = typeof opt.priority === 'string'
- ? opt.priority.toLowerCase() : opt.priority;
-
- switch (priority) {
- case 'low':
- str += '; Priority=Low'
- break
- case 'medium':
- str += '; Priority=Medium'
- break
- case 'high':
- str += '; Priority=High'
- break
+ switch (opt.priority) {
+ case "low":
+ str += "; Priority=Low";
+ break;
+ case "medium":
+ str += "; Priority=Medium";
+ break;
+ case "high":
+ str += "; Priority=High";
+ break;
default:
- throw new TypeError('option priority is invalid')
+ throw new TypeError("option priority is invalid");
}
}
if (opt.sameSite) {
- var sameSite = typeof opt.sameSite === 'string'
- ? opt.sameSite.toLowerCase() : opt.sameSite;
-
- switch (sameSite) {
+ switch (opt.sameSite) {
case true:
- str += '; SameSite=Strict';
+ case "strict":
+ str += "; SameSite=Strict";
break;
- case 'lax':
- str += '; SameSite=Lax';
+ case "lax":
+ str += "; SameSite=Lax";
break;
- case 'strict':
- str += '; SameSite=Strict';
- break;
- case 'none':
- str += '; SameSite=None';
+ case "none":
+ str += "; SameSite=None";
break;
default:
- throw new TypeError('option sameSite is invalid');
+ throw new TypeError("option sameSite is invalid");
}
}
@@ -295,37 +273,15 @@ function serialize(name, val, opt) {
/**
* URL-decode string value. Optimized to skip native call when no %.
- *
- * @param {string} str
- * @returns {string}
*/
-
-function decode (str) {
- return str.indexOf('%') !== -1
- ? decodeURIComponent(str)
- : str
-}
-
-/**
- * Determine if value is a Date.
- *
- * @param {*} val
- * @private
- */
-
-function isDate (val) {
- return __toString.call(val) === '[object Date]';
+function decode(str: string): string {
+ return str.indexOf("%") !== -1 ? decodeURIComponent(str) : str;
}
/**
* Try decoding a string using a decoding function.
- *
- * @param {string} str
- * @param {function} decode
- * @private
*/
-
-function tryDecode(str, decode) {
+function tryDecode(str: string, decode: (str: string) => string): string {
try {
return decode(str);
} catch (e) {
diff --git a/src/parse.bench.ts b/src/parse.bench.ts
new file mode 100644
index 0000000..574f470
--- /dev/null
+++ b/src/parse.bench.ts
@@ -0,0 +1,47 @@
+import { describe, bench } from "vitest";
+import * as cookie from "./index.js";
+import top from "../scripts/parse-top.json";
+
+describe("parse", () => {
+ bench("simple", () => {
+ cookie.parse("foo=bar");
+ });
+
+ bench("decode", () => {
+ cookie.parse("foo=hello%20there!");
+ });
+
+ bench("unquote", () => {
+ cookie.parse('foo="foo bar"');
+ });
+
+ bench("duplicates", () => {
+ cookie.parse(genCookies(2) + "; " + genCookies(2));
+ });
+
+ bench("10 cookies", () => {
+ cookie.parse(genCookies(10));
+ });
+
+ bench("100 cookies", () => {
+ cookie.parse(genCookies(100));
+ });
+});
+
+describe("parse top-sites", () => {
+ Object.entries(top).forEach(function ([domain, value]) {
+ bench("parse " + domain, () => {
+ cookie.parse(value);
+ });
+ });
+});
+
+function genCookies(num: number) {
+ let str = "";
+
+ for (let i = 0; i < num; i++) {
+ str += "; foo" + i + "=bar";
+ }
+
+ return str.slice(2);
+}
diff --git a/src/parse.spec.ts b/src/parse.spec.ts
new file mode 100644
index 0000000..224aa3d
--- /dev/null
+++ b/src/parse.spec.ts
@@ -0,0 +1,96 @@
+import { describe, it, expect } from "vitest";
+import * as cookie from "./index.js";
+
+describe("cookie.parse(str)", function () {
+ it("should parse cookie string to object", function () {
+ expect(cookie.parse("foo=bar")).toEqual({ foo: "bar" });
+ expect(cookie.parse("foo=123")).toEqual({ foo: "123" });
+ });
+
+ it("should ignore OWS", function () {
+ expect(cookie.parse("FOO = bar; baz = raz")).toEqual({
+ FOO: "bar",
+ baz: "raz",
+ });
+ });
+
+ it("should parse cookie with empty value", function () {
+ expect(cookie.parse("foo=; bar=")).toEqual({ foo: "", bar: "" });
+ });
+
+ it("should parse cookie with minimum length", function () {
+ expect(cookie.parse("f=")).toEqual({ f: "" });
+ expect(cookie.parse("f=;b=")).toEqual({ f: "", b: "" });
+ });
+
+ it("should URL-decode values", function () {
+ expect(cookie.parse('foo="bar=123456789&name=Magic+Mouse"')).toEqual({
+ foo: "bar=123456789&name=Magic+Mouse",
+ });
+
+ expect(cookie.parse("email=%20%22%2c%3b%2f")).toEqual({ email: ' ",;/' });
+ });
+
+ it("should parse quoted values", function () {
+ expect(cookie.parse('foo="bar"')).toEqual({ foo: "bar" });
+ expect(cookie.parse('foo=" a b c "')).toEqual({ foo: " a b c " });
+ });
+
+ it("should trim whitespace around key and value", function () {
+ expect(cookie.parse(' foo = "bar" ')).toEqual({ foo: "bar" });
+ expect(cookie.parse(" foo = bar ; fizz = buzz ")).toEqual({
+ foo: "bar",
+ fizz: "buzz",
+ });
+ expect(cookie.parse(' foo = " a b c " ')).toEqual({ foo: " a b c " });
+ expect(cookie.parse(" = bar ")).toEqual({ "": "bar" });
+ expect(cookie.parse(" foo = ")).toEqual({ foo: "" });
+ expect(cookie.parse(" = ")).toEqual({ "": "" });
+ expect(cookie.parse("\tfoo\t=\tbar\t")).toEqual({ foo: "bar" });
+ });
+
+ it("should return original value on escape error", function () {
+ expect(cookie.parse("foo=%1;bar=bar")).toEqual({ foo: "%1", bar: "bar" });
+ });
+
+ it("should ignore cookies without value", function () {
+ expect(cookie.parse("foo=bar;fizz ; buzz")).toEqual({ foo: "bar" });
+ expect(cookie.parse(" fizz; foo= bar")).toEqual({ foo: "bar" });
+ });
+
+ it("should ignore duplicate cookies", function () {
+ expect(cookie.parse("foo=%1;bar=bar;foo=boo")).toEqual({
+ foo: "%1",
+ bar: "bar",
+ });
+ expect(cookie.parse("foo=false;bar=bar;foo=true")).toEqual({
+ foo: "false",
+ bar: "bar",
+ });
+ expect(cookie.parse("foo=;bar=bar;foo=boo")).toEqual({
+ foo: "",
+ bar: "bar",
+ });
+ });
+
+ it("should parse native properties", function () {
+ expect(cookie.parse("toString=foo;valueOf=bar")).toEqual({
+ toString: "foo",
+ valueOf: "bar",
+ });
+ });
+});
+
+describe("cookie.parse(str, options)", function () {
+ describe('with "decode" option', function () {
+ it("should specify alternative value decoder", function () {
+ expect(
+ cookie.parse('foo="YmFy"', {
+ decode: function (v) {
+ return Buffer.from(v, "base64").toString();
+ },
+ }),
+ ).toEqual({ foo: "bar" });
+ });
+ });
+});
diff --git a/src/serialize.spec.ts b/src/serialize.spec.ts
new file mode 100644
index 0000000..e9a6ba8
--- /dev/null
+++ b/src/serialize.spec.ts
@@ -0,0 +1,338 @@
+import { describe, it, expect } from "vitest";
+import * as cookie from "./index.js";
+
+describe("cookie.serialize(name, value)", function () {
+ it("should serialize name and value", function () {
+ expect(cookie.serialize("foo", "bar")).toEqual("foo=bar");
+ });
+
+ it("should URL-encode value", function () {
+ expect(cookie.serialize("foo", "bar +baz")).toEqual("foo=bar%20%2Bbaz");
+ });
+
+ it("should serialize empty value", function () {
+ expect(cookie.serialize("foo", "")).toEqual("foo=");
+ });
+
+ it("should serialize valid name", function () {
+ var validNames = [
+ "foo",
+ "foo!bar",
+ "foo#bar",
+ "foo$bar",
+ "foo'bar",
+ "foo*bar",
+ "foo+bar",
+ "foo-bar",
+ "foo.bar",
+ "foo^bar",
+ "foo_bar",
+ "foo`bar",
+ "foo|bar",
+ "foo~bar",
+ "foo7bar",
+ ];
+
+ validNames.forEach(function (name) {
+ expect(cookie.serialize(name, "baz")).toEqual(name + "=baz");
+ });
+ });
+
+ it("should throw for invalid name", function () {
+ var invalidNames = [
+ "foo\n",
+ "foo\u280a",
+ "foo/foo",
+ "foo,foo",
+ "foo;foo",
+ "foo@foo",
+ "foo[foo]",
+ "foo?foo",
+ "foo:foo",
+ "foo{foo}",
+ "foo foo",
+ "foo\tfoo",
+ 'foo"foo',
+ "foo',
+ ];
+
+ invalidPaths.forEach(function (path) {
+ expect(
+ cookie.serialize.bind(cookie, "foo", "bar", { path: path }),
+ ).toThrow(/option path is invalid/);
+ });
+ });
+ });
+
+ describe('with "priority" option', function () {
+ it("should throw on invalid priority", function () {
+ expect(function () {
+ cookie.serialize("foo", "bar", { priority: "foo" as any });
+ }).toThrow(/option priority is invalid/);
+ });
+
+ it("should throw on non-string", function () {
+ expect(function () {
+ cookie.serialize("foo", "bar", { priority: 42 as any });
+ }).toThrow(/option priority is invalid/);
+ });
+
+ it("should set priority low", function () {
+ expect(cookie.serialize("foo", "bar", { priority: "low" })).toEqual(
+ "foo=bar; Priority=Low",
+ );
+ });
+
+ it("should set priority medium", function () {
+ expect(cookie.serialize("foo", "bar", { priority: "medium" })).toEqual(
+ "foo=bar; Priority=Medium",
+ );
+ });
+
+ it("should set priority high", function () {
+ expect(cookie.serialize("foo", "bar", { priority: "high" })).toEqual(
+ "foo=bar; Priority=High",
+ );
+ });
+ });
+
+ describe('with "sameSite" option', function () {
+ it("should throw on invalid sameSite", function () {
+ expect(() => {
+ cookie.serialize("foo", "bar", { sameSite: "foo" as any });
+ }).toThrow(/option sameSite is invalid/);
+ });
+
+ it("should set sameSite strict", function () {
+ expect(cookie.serialize("foo", "bar", { sameSite: "strict" })).toEqual(
+ "foo=bar; SameSite=Strict",
+ );
+ });
+
+ it("should set sameSite lax", function () {
+ expect(cookie.serialize("foo", "bar", { sameSite: "lax" })).toEqual(
+ "foo=bar; SameSite=Lax",
+ );
+ });
+
+ it("should set sameSite none", function () {
+ expect(cookie.serialize("foo", "bar", { sameSite: "none" })).toEqual(
+ "foo=bar; SameSite=None",
+ );
+ });
+
+ it("should set sameSite strict when true", function () {
+ expect(cookie.serialize("foo", "bar", { sameSite: true })).toEqual(
+ "foo=bar; SameSite=Strict",
+ );
+ });
+
+ it("should not set sameSite when false", function () {
+ expect(cookie.serialize("foo", "bar", { sameSite: false })).toEqual(
+ "foo=bar",
+ );
+ });
+ });
+
+ describe('with "secure" option', function () {
+ it("should include secure flag when true", function () {
+ expect(cookie.serialize("foo", "bar", { secure: true })).toEqual(
+ "foo=bar; Secure",
+ );
+ });
+
+ it("should not include secure flag when false", function () {
+ expect(cookie.serialize("foo", "bar", { secure: false })).toEqual(
+ "foo=bar",
+ );
+ });
+ });
+});
diff --git a/test/parse.js b/test/parse.js
deleted file mode 100644
index b5f65ed..0000000
--- a/test/parse.js
+++ /dev/null
@@ -1,85 +0,0 @@
-
-var assert = require('assert');
-var Buffer = require('safe-buffer').Buffer
-
-var cookie = require('..');
-
-describe('cookie.parse(str)', function () {
- it('should throw with no arguments', function () {
- assert.throws(cookie.parse.bind(), /argument str must be a string/)
- })
-
- it('should throw when not a string', function () {
- assert.throws(cookie.parse.bind(null, 42), /argument str must be a string/)
- })
-
- it('should parse cookie string to object', function () {
- assert.deepEqual(cookie.parse('foo=bar'), { foo: 'bar' })
- assert.deepEqual(cookie.parse('foo=123'), { foo: '123' })
- })
-
- it('should ignore OWS', function () {
- assert.deepEqual(cookie.parse('FOO = bar; baz = raz'),
- { FOO: 'bar', baz: 'raz' })
- })
-
- it('should parse cookie with empty value', function () {
- assert.deepEqual(cookie.parse('foo=; bar='), { foo: '', bar: '' })
- })
-
- it('should parse cookie with minimum length', function () {
- assert.deepEqual(cookie.parse('f='), { f: '' })
- assert.deepEqual(cookie.parse('f=;b='), { f: '', b: '' })
- })
-
- it('should URL-decode values', function () {
- assert.deepEqual(cookie.parse('foo="bar=123456789&name=Magic+Mouse"'),
- { foo: 'bar=123456789&name=Magic+Mouse' })
-
- assert.deepEqual(cookie.parse('email=%20%22%2c%3b%2f'), { email: ' ",;/' })
- })
-
- it('should parse quoted values', function () {
- assert.deepEqual(cookie.parse('foo="bar"'), { foo: 'bar' })
- assert.deepEqual(cookie.parse('foo=" a b c "'), { foo: ' a b c ' })
- })
-
- it('should trim whitespace around key and value', function () {
- assert.deepEqual(cookie.parse(' foo = "bar" '), { foo: 'bar' })
- assert.deepEqual(cookie.parse(' foo = bar ; fizz = buzz '), { foo: 'bar', fizz: 'buzz' })
- assert.deepEqual(cookie.parse(' foo = " a b c " '), { foo: ' a b c ' })
- assert.deepEqual(cookie.parse(' = bar '), { '': 'bar' })
- assert.deepEqual(cookie.parse(' foo = '), { foo: '' })
- assert.deepEqual(cookie.parse(' = '), { '': '' })
- assert.deepEqual(cookie.parse('\tfoo\t=\tbar\t'), { foo: 'bar' })
- })
-
- it('should return original value on escape error', function () {
- assert.deepEqual(cookie.parse('foo=%1;bar=bar'), { foo: '%1', bar: 'bar' })
- })
-
- it('should ignore cookies without value', function () {
- assert.deepEqual(cookie.parse('foo=bar;fizz ; buzz'), { foo: 'bar' })
- assert.deepEqual(cookie.parse(' fizz; foo= bar'), { foo: 'bar' })
- })
-
- it('should ignore duplicate cookies', function () {
- assert.deepEqual(cookie.parse('foo=%1;bar=bar;foo=boo'), { foo: '%1', bar: 'bar' })
- assert.deepEqual(cookie.parse('foo=false;bar=bar;foo=true'), { foo: 'false', bar: 'bar' })
- assert.deepEqual(cookie.parse('foo=;bar=bar;foo=boo'), { foo: '', bar: 'bar' })
- })
-
- it('should parse native properties', function () {
- assert.deepEqual(cookie.parse('toString=foo;valueOf=bar'), { toString: 'foo', valueOf: 'bar' })
- })
-})
-
-describe('cookie.parse(str, options)', function () {
- describe('with "decode" option', function () {
- it('should specify alternative value decoder', function () {
- assert.deepEqual(cookie.parse('foo="YmFy"', {
- decode: function (v) { return Buffer.from(v, 'base64').toString() }
- }), { foo: 'bar' })
- })
- })
-})
diff --git a/test/serialize.js b/test/serialize.js
deleted file mode 100644
index 1ee3235..0000000
--- a/test/serialize.js
+++ /dev/null
@@ -1,319 +0,0 @@
-
-var assert = require('assert');
-var Buffer = require('safe-buffer').Buffer
-
-var cookie = require('..');
-
-describe('cookie.serialize(name, value)', function () {
- it('should serialize name and value', function () {
- assert.equal(cookie.serialize('foo', 'bar'), 'foo=bar')
- })
-
- it('should URL-encode value', function () {
- assert.equal(cookie.serialize('foo', 'bar +baz'), 'foo=bar%20%2Bbaz')
- })
-
- it('should serialize empty value', function () {
- assert.equal(cookie.serialize('foo', ''), 'foo=')
- })
-
- it('should serialize valid name', function () {
- var validNames = [
- 'foo',
- 'foo!bar',
- 'foo#bar',
- 'foo$bar',
- "foo'bar",
- 'foo*bar',
- 'foo+bar',
- 'foo-bar',
- 'foo.bar',
- 'foo^bar',
- 'foo_bar',
- 'foo`bar',
- 'foo|bar',
- 'foo~bar',
- 'foo7bar',
- ];
-
- validNames.forEach(function (name) {
- assert.equal(cookie.serialize(name, 'baz'), name + '=baz');
- });
- });
-
- it('should throw for invalid name', function () {
- var invalidNames = [
- 'foo\n',
- 'foo\u280a',
- 'foo/foo',
- 'foo,foo',
- 'foo;foo',
- 'foo@foo',
- 'foo[foo]',
- 'foo?foo',
- 'foo:foo',
- 'foo{foo}',
- 'foo foo',
- 'foo\tfoo',
- 'foo"foo',
- 'foo'
- ];
-
- invalidPaths.forEach(function (path) {
- assert.throws(
- cookie.serialize.bind(cookie, 'foo', 'bar', { path: path }),
- /option path is invalid/,
- 'Expected an error for invalid path: ' + path
- );
- });
- });
- });
-
- describe('with "priority" option', function () {
- it('should throw on invalid priority', function () {
- assert.throws(function () {
- cookie.serialize('foo', 'bar', { priority: 'foo' })
- }, /option priority is invalid/)
- })
-
- it('should throw on non-string', function () {
- assert.throws(function () {
- cookie.serialize('foo', 'bar', { priority: 42 })
- }, /option priority is invalid/)
- })
-
- it('should set priority low', function () {
- assert.equal(cookie.serialize('foo', 'bar', { priority: 'Low' }), 'foo=bar; Priority=Low')
- assert.equal(cookie.serialize('foo', 'bar', { priority: 'loW' }), 'foo=bar; Priority=Low')
- })
-
- it('should set priority medium', function () {
- assert.equal(cookie.serialize('foo', 'bar', { priority: 'Medium' }), 'foo=bar; Priority=Medium')
- assert.equal(cookie.serialize('foo', 'bar', { priority: 'medium' }), 'foo=bar; Priority=Medium')
- })
-
- it('should set priority high', function () {
- assert.equal(cookie.serialize('foo', 'bar', { priority: 'High' }), 'foo=bar; Priority=High')
- assert.equal(cookie.serialize('foo', 'bar', { priority: 'HIGH' }), 'foo=bar; Priority=High')
- })
- })
-
- describe('with "sameSite" option', function () {
- it('should throw on invalid sameSite', function () {
- assert.throws(function () {
- cookie.serialize('foo', 'bar', { sameSite: 'foo' })
- }, /option sameSite is invalid/)
- })
-
- it('should set sameSite strict', function () {
- assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'Strict' }), 'foo=bar; SameSite=Strict')
- assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'strict' }), 'foo=bar; SameSite=Strict')
- })
-
- it('should set sameSite lax', function () {
- assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'Lax' }), 'foo=bar; SameSite=Lax')
- assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'lax' }), 'foo=bar; SameSite=Lax')
- })
-
- it('should set sameSite none', function () {
- assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'None' }), 'foo=bar; SameSite=None')
- assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'none' }), 'foo=bar; SameSite=None')
- })
-
- it('should set sameSite strict when true', function () {
- assert.equal(cookie.serialize('foo', 'bar', { sameSite: true }), 'foo=bar; SameSite=Strict')
- })
-
- it('should not set sameSite when false', function () {
- assert.equal(cookie.serialize('foo', 'bar', { sameSite: false }), 'foo=bar')
- })
- })
-
- describe('with "secure" option', function () {
- it('should include secure flag when true', function () {
- assert.equal(cookie.serialize('foo', 'bar', { secure: true }), 'foo=bar; Secure')
- })
-
- it('should not include secure flag when false', function () {
- assert.equal(cookie.serialize('foo', 'bar', { secure: false }), 'foo=bar')
- })
- })
-})
diff --git a/tsconfig.build.json b/tsconfig.build.json
new file mode 100644
index 0000000..4b7a898
--- /dev/null
+++ b/tsconfig.build.json
@@ -0,0 +1,4 @@
+{
+ "extends": "./tsconfig.json",
+ "exclude": ["src/**/*.spec.ts", "src/**/*.bench.ts"]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..ef26e83
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "@borderless/ts-scripts/configs/tsconfig.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src",
+ "resolveJsonModule": true
+ },
+ "include": ["src/**/*"]
+}