From 981960dfd8c1c2f221bfe3c03da0c69472971eb9 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Fri, 13 Feb 2026 11:17:28 -0800 Subject: [PATCH] Faster stringify cookie with benchmarks --- src/index.ts | 11 +++-- src/stringify-cookie.bench.ts | 41 ++++++++++++++++++ src/stringify-set-cookie.bench.ts | 71 +++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 src/stringify-cookie.bench.ts create mode 100644 src/stringify-set-cookie.bench.ts diff --git a/src/index.ts b/src/index.ts index 30fd59a..c37b97d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -158,9 +158,11 @@ export function stringifyCookie( options?: StringifyOptions, ): string { const enc = options?.encode || encodeURIComponent; - const cookieStrings: string[] = []; + const keys = Object.keys(cookie); + let str = ""; - for (const name of Object.keys(cookie)) { + for (let i = 0; i < keys.length; i++) { + const name = keys[i]; const val = cookie[name]; if (val === undefined) continue; @@ -174,10 +176,11 @@ export function stringifyCookie( throw new TypeError(`cookie val is invalid: ${val}`); } - cookieStrings.push(`${name}=${value}`); + if (i > 0) str += "; "; + str += name + "=" + value; } - return cookieStrings.join("; "); + return str; } /** diff --git a/src/stringify-cookie.bench.ts b/src/stringify-cookie.bench.ts new file mode 100644 index 0000000..9da3464 --- /dev/null +++ b/src/stringify-cookie.bench.ts @@ -0,0 +1,41 @@ +import { describe, bench } from "vitest"; +import * as cookie from "./index.js"; + +describe("cookie.stringifyCookie", () => { + bench("empty", () => { + cookie.stringifyCookie({}); + }); + + bench("simple", () => { + cookie.stringifyCookie({ foo: "bar" }); + }); + + bench("undefined values", () => { + cookie.stringifyCookie({ + foo: "bar", + baz: undefined, + qux: "quux", + zap: undefined, + }); + }); + + const cookies10 = genCookies(10); + bench("10 cookies", () => { + cookie.stringifyCookie(cookies10); + }); + + const cookies100 = genCookies(100); + bench("100 cookies", () => { + cookie.stringifyCookie(cookies100); + }); +}); + +function genCookies(num: number) { + const cookies: Record = {}; + + for (let i = 0; i < num; i++) { + cookies["foo" + i] = "bar" + i; + } + + return cookies; +} diff --git a/src/stringify-set-cookie.bench.ts b/src/stringify-set-cookie.bench.ts new file mode 100644 index 0000000..281656e --- /dev/null +++ b/src/stringify-set-cookie.bench.ts @@ -0,0 +1,71 @@ +import { describe, bench } from "vitest"; +import * as cookie from "./index.js"; + +describe("cookie.stringifySetCookie", () => { + bench("simple", () => { + cookie.stringifySetCookie("foo", "bar"); + }); + + bench("encode", () => { + cookie.stringifySetCookie("foo", "hello there!"); + }); + + const expires = new Date("Wed, 21 Oct 2015 07:28:00 GMT"); + bench("attributes", () => { + cookie.stringifySetCookie("foo", "bar", { + path: "/", + domain: "example.com", + maxAge: 3600, + expires, + httpOnly: true, + secure: true, + partitioned: true, + priority: "high", + sameSite: "lax", + }); + }); + + bench("object input", () => { + cookie.stringifySetCookie({ + name: "foo", + value: "bar", + path: "/", + maxAge: 3600, + httpOnly: true, + secure: true, + 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; +}