diff --git a/src/index.ts b/src/index.ts
index a95a2e9..7add4e8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -68,8 +68,6 @@ const pathValueRegExp = /^[\u0020-\u003A\u003D-\u007E]*$/;
*/
const maxAgeRegExp = /^-?\d+$/;
-const __toString = Object.prototype.toString;
-
const NullObject = /* @__PURE__ */ (() => {
const C = function () {};
C.prototype = Object.create(null);
@@ -276,28 +274,13 @@ export type SerializeOptions = StringifyOptions &
* Serialize a name value pair into a cookie string suitable for
* http headers. An optional options object specifies cookie parameters.
*
- * serialize('foo', 'bar', { httpOnly: true })
- * => "foo=bar; httpOnly"
+ * stringifySetCookie({ name: 'foo', value: 'bar', httpOnly: true })
+ * => "foo=bar; HttpOnly"
*/
export function stringifySetCookie(
cookie: SetCookie,
options?: StringifyOptions,
-): string;
-export function stringifySetCookie(
- name: string,
- val: string,
- options?: SerializeOptions,
-): string;
-export function stringifySetCookie(
- _name: string | SetCookie,
- _val?: string | StringifyOptions,
- _opts?: SerializeOptions,
): string {
- const cookie =
- typeof _name === "object"
- ? _name
- : { ..._opts, name: _name, value: String(_val) };
- const options = typeof _val === "object" ? _val : _opts;
const enc = options?.encode || encodeURIComponent;
if (!cookieNameRegExp.test(cookie.name)) {
@@ -337,7 +320,7 @@ export function stringifySetCookie(
}
if (cookie.expires) {
- if (!isDate(cookie.expires) || !Number.isFinite(cookie.expires.valueOf())) {
+ if (!Number.isFinite(cookie.expires.valueOf())) {
throw new TypeError(`option expires is invalid: ${cookie.expires}`);
}
@@ -403,7 +386,7 @@ export function stringifySetCookie(
/**
* Deserialize a `Set-Cookie` header into an object.
*
- * deserialize('foo=bar; httpOnly')
+ * parseSetCookie('foo=bar; HttpOnly')
* => { name: 'foo', value: 'bar', httpOnly: true }
*/
export function parseSetCookie(str: string, options?: ParseOptions): SetCookie {
@@ -533,15 +516,3 @@ function decode(str: string): string {
return str;
}
}
-
-/**
- * Determine if value is a Date.
- */
-function isDate(val: any): val is Date {
- return __toString.call(val) === "[object Date]";
-}
-
-/**
- * Backward compatibility exports.
- */
-export { stringifySetCookie as serialize, parseCookie as parse };
diff --git a/src/parse-cookie.spec.ts b/src/parse-cookie.spec.ts
index 2444909..82a042a 100644
--- a/src/parse-cookie.spec.ts
+++ b/src/parse-cookie.spec.ts
@@ -11,10 +11,6 @@ describe("cookie.parseCookie", function () {
});
});
- it("should have backward compatible export", function () {
- expect(cookie.parse).toBe(cookie.parseCookie);
- });
-
it("should parse cookie string to object", function () {
expect(cookie.parseCookie("foo=bar")).toEqual({ foo: "bar" });
expect(cookie.parseCookie("foo=123")).toEqual({ foo: "123" });
diff --git a/src/stringify-set-cookie.bench.ts b/src/stringify-set-cookie.bench.ts
index 281656e..3d983b9 100644
--- a/src/stringify-set-cookie.bench.ts
+++ b/src/stringify-set-cookie.bench.ts
@@ -3,16 +3,24 @@ import * as cookie from "./index.js";
describe("cookie.stringifySetCookie", () => {
bench("simple", () => {
- cookie.stringifySetCookie("foo", "bar");
+ cookie.stringifySetCookie({
+ name: "foo",
+ value: "bar",
+ });
});
bench("encode", () => {
- cookie.stringifySetCookie("foo", "hello there!");
+ cookie.stringifySetCookie({
+ name: "foo",
+ value: "hello there!",
+ });
});
const expires = new Date("Wed, 21 Oct 2015 07:28:00 GMT");
- bench("attributes", () => {
- cookie.stringifySetCookie("foo", "bar", {
+ bench("all attributes", () => {
+ cookie.stringifySetCookie({
+ name: "foo",
+ value: "bar",
path: "/",
domain: "example.com",
maxAge: 3600,
@@ -25,7 +33,7 @@ describe("cookie.stringifySetCookie", () => {
});
});
- bench("object input", () => {
+ bench("typical object", () => {
cookie.stringifySetCookie({
name: "foo",
value: "bar",
@@ -36,36 +44,4 @@ describe("cookie.stringifySetCookie", () => {
sameSite: "strict",
});
});
-
- const setCookies10 = genSetCookies(10);
- bench("10 set-cookies", () => {
- for (const setCookie of setCookies10) {
- cookie.stringifySetCookie(setCookie);
- }
- });
-
- const setCookies100 = genSetCookies(100);
- bench("100 set-cookies", () => {
- for (const setCookie of setCookies100) {
- cookie.stringifySetCookie(setCookie);
- }
- });
});
-
-function genSetCookies(num: number): cookie.SetCookie[] {
- const cookies: cookie.SetCookie[] = [];
-
- for (let i = 0; i < num; i++) {
- cookies.push({
- name: "foo" + i,
- value: "bar " + i,
- path: "/foo" + i,
- maxAge: 3600,
- httpOnly: true,
- secure: true,
- sameSite: "lax",
- });
- }
-
- return cookies;
-}
diff --git a/src/stringify-set-cookie.spec.ts b/src/stringify-set-cookie.spec.ts
index 92a56ea..d944e1d 100644
--- a/src/stringify-set-cookie.spec.ts
+++ b/src/stringify-set-cookie.spec.ts
@@ -1,46 +1,32 @@
-import { describe, it, expect } from "vitest";
-import * as cookie from "./index.js";
+import { describe, expect, it } from "vitest";
+import { stringifySetCookie } from "./index.js";
-describe("cookie.stringifySetCookie", function () {
- it("should have backward compatible export", function () {
- expect(cookie.serialize).toBe(cookie.stringifySetCookie);
- });
-
- it("should serialize name and value", function () {
- expect(cookie.stringifySetCookie("foo", "bar")).toEqual("foo=bar");
+describe("cookie.stringifySetCookie", () => {
+ it("should serialize name and value", () => {
+ expect(stringifySetCookie({ name: "foo", value: "bar" })).toEqual(
+ "foo=bar",
+ );
});
- it("should URL-encode value", function () {
- expect(cookie.stringifySetCookie("foo", "bar +baz")).toEqual(
+ it("should URL-encode value", () => {
+ expect(stringifySetCookie({ name: "foo", value: "bar +baz" })).toEqual(
"foo=bar%20%2Bbaz",
);
});
- it("should serialize empty value", function () {
- expect(cookie.stringifySetCookie("foo", "")).toEqual("foo=");
- });
-
- it("should serialize an object", function () {
- expect(
- cookie.stringifySetCookie({ name: "foo", value: "bar +baz" }),
- ).toEqual("foo=bar%20%2Bbaz");
+ it("should serialize empty value", () => {
+ expect(stringifySetCookie({ name: "foo", value: "" })).toEqual("foo=");
});
- it("should serialize an object with options", function () {
+ it("should serialize with options", () => {
expect(
- cookie.stringifySetCookie(
+ stringifySetCookie(
{ name: "foo", value: "bar+baz" },
{ encode: (x) => x },
),
).toEqual("foo=bar+baz");
});
- it("should support three argument mode with value in options", function () {
- expect(
- cookie.stringifySetCookie("foo", "test", { value: "ignored" } as any),
- ).toEqual("foo=test");
- });
-
it.each([
["foo"],
["foo,bar"],
@@ -71,7 +57,7 @@ describe("cookie.stringifySetCookie", function () {
["foo?bar"],
["foo\\bar"],
])("should serialize name: %s", (name) => {
- expect(cookie.stringifySetCookie(name, "baz")).toEqual(`${name}=baz`);
+ expect(stringifySetCookie({ name, value: "baz" })).toEqual(`${name}=baz`);
});
it.each([
@@ -82,12 +68,12 @@ describe("cookie.stringifySetCookie", function () {
["foo bar"],
["foo\tbar"],
])("should throw for invalid name: %s", (name) => {
- expect(() => cookie.stringifySetCookie(name, "bar")).toThrow(
+ expect(() => stringifySetCookie({ name, value: "bar" })).toThrow(
/argument name is invalid/,
);
});
- describe('with "domain" option', function () {
+ describe('with "domain" option', () => {
it.each([
["example.com"],
["sub.example.com"],
@@ -95,9 +81,8 @@ describe("cookie.stringifySetCookie", function () {
["localhost"],
[".localhost"],
["my-site.org"],
- ["localhost"],
])("should serialize domain: %s", (domain) => {
- expect(cookie.stringifySetCookie("foo", "bar", { domain })).toEqual(
+ expect(stringifySetCookie({ name: "foo", value: "bar", domain })).toEqual(
`foo=bar; Domain=${domain}`,
);
});
@@ -110,20 +95,21 @@ describe("cookie.stringifySetCookie", function () {
["example.com; Path=/"],
["example.com /* inject a comment */"],
])("should throw for invalid domain: %s", (domain) => {
- expect(() => cookie.stringifySetCookie("foo", "bar", { domain })).toThrow(
- /option domain is invalid/,
- );
+ expect(() =>
+ stringifySetCookie({ name: "foo", value: "bar", domain }),
+ ).toThrow(/option domain is invalid/);
});
});
- describe('with "encode" option', function () {
- it("should specify alternative value encoder", function () {
+ describe('with "encode" option', () => {
+ it("should specify alternative value encoder", () => {
expect(
- cookie.stringifySetCookie("foo", "bar", {
- encode: function (v) {
- return Buffer.from(v, "utf8").toString("base64");
+ stringifySetCookie(
+ { name: "foo", value: "bar" },
+ {
+ encode: (v) => Buffer.from(v, "utf8").toString("base64"),
},
- }),
+ ),
).toEqual("foo=YmFy");
});
@@ -131,7 +117,7 @@ describe("cookie.stringifySetCookie", function () {
"should serialize value: %s",
(value) => {
expect(
- cookie.stringifySetCookie("foo", value, { encode: (x) => x }),
+ stringifySetCookie({ name: "foo", value }, { encode: (x) => x }),
).toEqual(`foo=${value}`);
},
);
@@ -140,228 +126,146 @@ describe("cookie.stringifySetCookie", function () {
"should throw for invalid value: %s",
(value) => {
expect(() =>
- cookie.stringifySetCookie("foo", value, { encode: (x) => x }),
+ stringifySetCookie({ name: "foo", value }, { encode: (x) => x }),
).toThrow(/argument val is invalid/);
},
);
});
- describe('with "expires" option', function () {
- it("should throw on invalid date", function () {
- expect(
- cookie.stringifySetCookie.bind(cookie, "foo", "bar", {
+ describe('with "expires" option', () => {
+ it("should throw on invalid date", () => {
+ expect(() =>
+ stringifySetCookie({
+ name: "foo",
+ value: "bar",
expires: new Date(NaN),
}),
).toThrow(/option expires is invalid/);
});
- it("should set expires to given date", function () {
+ it("should set expires to given date", () => {
expect(
- cookie.stringifySetCookie("foo", "bar", {
+ stringifySetCookie({
+ name: "foo",
+ value: "bar",
expires: new Date(Date.UTC(2000, 11, 24, 10, 30, 59, 900)),
}),
).toEqual("foo=bar; Expires=Sun, 24 Dec 2000 10:30:59 GMT");
});
});
- describe('with "httpOnly" option', function () {
- it("should include httpOnly flag when true", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { httpOnly: true }),
- ).toEqual("foo=bar; HttpOnly");
- });
-
- it("should not include httpOnly flag when false", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { httpOnly: false }),
- ).toEqual("foo=bar");
- });
- });
-
- describe('with "maxAge" option', function () {
- it("should throw when not a number", function () {
- expect(function () {
- cookie.stringifySetCookie("foo", "bar", { maxAge: "buzz" as any });
- }).toThrow(/option maxAge is invalid/);
- });
-
- it("should throw when Infinity", function () {
- expect(function () {
- cookie.stringifySetCookie("foo", "bar", { maxAge: Infinity });
- }).toThrow(/option maxAge is invalid/);
- });
-
- it("should throw when max-age is not an integer", function () {
- expect(function () {
- cookie.stringifySetCookie("foo", "bar", { maxAge: 3.14 });
- }).toThrow(/option maxAge is invalid/);
- });
-
- it("should set max-age to value", function () {
- expect(cookie.stringifySetCookie("foo", "bar", { maxAge: 1000 })).toEqual(
- "foo=bar; Max-Age=1000",
- );
- expect(cookie.stringifySetCookie("foo", "bar", { maxAge: 0 })).toEqual(
- "foo=bar; Max-Age=0",
- );
- });
-
- it("should not set when undefined", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { maxAge: undefined }),
- ).toEqual("foo=bar");
+ describe('with "maxAge" option', () => {
+ it.each([
+ ["non-number", "buzz"],
+ ["Infinity", Infinity],
+ ["non-integer", 3.14],
+ ])("should throw when maxAge is %s", (_label, maxAge) => {
+ expect(() =>
+ stringifySetCookie({ name: "foo", value: "bar", maxAge } as any),
+ ).toThrow(/option maxAge is invalid/);
});
- });
- describe('with "partitioned" option', function () {
- it("should include partitioned flag when true", function () {
+ it("should set max-age to value", () => {
expect(
- cookie.stringifySetCookie("foo", "bar", { partitioned: true }),
- ).toEqual("foo=bar; Partitioned");
- });
-
- it("should not include partitioned flag when false", function () {
+ stringifySetCookie({ name: "foo", value: "bar", maxAge: 1000 }),
+ ).toEqual("foo=bar; Max-Age=1000");
expect(
- cookie.stringifySetCookie("foo", "bar", { partitioned: false }),
- ).toEqual("foo=bar");
- });
-
- it("should not include partitioned flag when not defined", function () {
- expect(cookie.stringifySetCookie("foo", "bar", {})).toEqual("foo=bar");
+ stringifySetCookie({ name: "foo", value: "bar", maxAge: 0 }),
+ ).toEqual("foo=bar; Max-Age=0");
});
});
- describe('with "path" option', function () {
- it("should serialize path", function () {
- var validPaths = [
- "/",
- "/login",
- "/foo.bar/baz",
- "/foo-bar",
- "/foo=bar?baz",
- '/foo"bar"',
- "/../foo/bar",
- "../foo/",
- "./",
- ];
-
- validPaths.forEach(function (path) {
- expect(cookie.stringifySetCookie("foo", "bar", { path: path })).toEqual(
- "foo=bar; Path=" + path,
- );
- });
+ describe('with "path" option', () => {
+ it.each([
+ ["/"],
+ ["/login"],
+ ["/foo.bar/baz"],
+ ["/foo-bar"],
+ ["/foo=bar?baz"],
+ ['/foo"bar"'],
+ ["/../foo/bar"],
+ ["../foo/"],
+ ["./"],
+ ])("should serialize path: %s", (path) => {
+ expect(stringifySetCookie({ name: "foo", value: "bar", path })).toEqual(
+ `foo=bar; Path=${path}`,
+ );
});
- it("should throw for invalid value", function () {
- var invalidPaths = [
- "/\n",
- "/foo\u0000",
- "/path/with\rnewline",
- "/; Path=/sensitive-data",
- '/login">',
- ];
-
- invalidPaths.forEach(function (path) {
- expect(
- cookie.stringifySetCookie.bind(cookie, "foo", "bar", { path: path }),
- ).toThrow(/option path is invalid/);
- });
+ it.each([
+ ["/\n"],
+ ["/foo\u0000"],
+ ["/path/with\rnewline"],
+ ["/; Path=/sensitive-data"],
+ ['/login">'],
+ ])("should throw for invalid path: %s", (path) => {
+ expect(() =>
+ stringifySetCookie({ name: "foo", value: "bar", path }),
+ ).toThrow(/option path is invalid/);
});
});
- describe('with "priority" option', function () {
- it("should throw on invalid priority", function () {
- expect(function () {
- cookie.stringifySetCookie("foo", "bar", { priority: "foo" as any });
- }).toThrow(/option priority is invalid/);
- });
-
- it("should throw on non-string", function () {
- expect(function () {
- cookie.stringifySetCookie("foo", "bar", { priority: 42 as any });
- }).toThrow(/option priority is invalid/);
- });
-
- it("should set priority low", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { priority: "low" }),
- ).toEqual("foo=bar; Priority=Low");
- });
-
- it("should set priority medium", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { priority: "medium" }),
- ).toEqual("foo=bar; Priority=Medium");
- });
-
- it("should set priority high", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { priority: "high" }),
- ).toEqual("foo=bar; Priority=High");
- });
-
- it("should set priority case insensitive", function () {
+ describe('with "boolean attributes"', () => {
+ it("should include enabled flags", () => {
expect(
- cookie.stringifySetCookie("foo", "bar", { priority: "High" as any }),
- ).toEqual("foo=bar; Priority=High");
+ stringifySetCookie({
+ name: "foo",
+ value: "bar",
+ httpOnly: true,
+ secure: true,
+ partitioned: true,
+ }),
+ ).toEqual("foo=bar; HttpOnly; Secure; Partitioned");
});
});
- describe('with "sameSite" option', function () {
- it("should throw on invalid sameSite", function () {
- expect(() => {
- cookie.stringifySetCookie("foo", "bar", { sameSite: "foo" as any });
- }).toThrow(/option sameSite is invalid/);
- });
-
- it("should set sameSite strict", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { sameSite: "strict" }),
- ).toEqual("foo=bar; SameSite=Strict");
- });
-
- it("should set sameSite lax", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { sameSite: "lax" }),
- ).toEqual("foo=bar; SameSite=Lax");
- });
-
- it("should set sameSite none", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { sameSite: "none" }),
- ).toEqual("foo=bar; SameSite=None");
- });
-
- it("should set sameSite strict when true", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { sameSite: true }),
- ).toEqual("foo=bar; SameSite=Strict");
- });
-
- it("should not set sameSite when false", function () {
- expect(
- cookie.stringifySetCookie("foo", "bar", { sameSite: false }),
- ).toEqual("foo=bar");
+ describe('with "priority" option', () => {
+ it.each([
+ ["invalid priority", "foo"],
+ ["non-string", 42],
+ ])("should throw on %s", (_label, priority) => {
+ expect(() =>
+ stringifySetCookie({ name: "foo", value: "bar", priority } as any),
+ ).toThrow(/option priority is invalid/);
});
- it("should set sameSite case insensitive", function () {
+ it.each([
+ ["low", "Low"],
+ ["medium", "Medium"],
+ ["high", "High"],
+ ["High", "High"],
+ ])("should set priority %s", (priority, expected) => {
expect(
- cookie.stringifySetCookie("foo", "bar", { sameSite: "Lax" as any }),
- ).toEqual("foo=bar; SameSite=Lax");
+ stringifySetCookie({
+ name: "foo",
+ value: "bar",
+ priority: priority as any,
+ }),
+ ).toEqual(`foo=bar; Priority=${expected}`);
});
});
- describe('with "secure" option', function () {
- it("should include secure flag when true", function () {
- expect(cookie.stringifySetCookie("foo", "bar", { secure: true })).toEqual(
- "foo=bar; Secure",
- );
+ describe('with "sameSite" option', () => {
+ it("should throw on invalid sameSite", () => {
+ expect(() =>
+ stringifySetCookie({
+ name: "foo",
+ value: "bar",
+ sameSite: "foo" as any,
+ }),
+ ).toThrow(/option sameSite is invalid/);
});
- it("should not include secure flag when false", function () {
+ it.each([
+ ["strict", "Strict"],
+ ["lax", "Lax"],
+ ["none", "None"],
+ [true, "Strict"],
+ ["Lax", "Lax"],
+ ])("should set sameSite %s", (sameSite, expected) => {
expect(
- cookie.stringifySetCookie("foo", "bar", { secure: false }),
- ).toEqual("foo=bar");
+ stringifySetCookie({ name: "foo", value: "bar", sameSite } as any),
+ ).toEqual(`foo=bar; SameSite=${expected}`);
});
});
});