refactor(shims): dedupe cookie value serialization - #1072
Conversation
Extract the shared encodeURIComponent-based value encoding from headers.ts and server.ts ResponseCookies into a single helper. Follow-up to #1049. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commit: |
|
knip flagged this as an unused export — the type is only consumed within cookie-serialize.ts itself. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
/bigbonk review |
There was a problem hiding this comment.
Clean refactor. The extracted helper is byte-for-byte identical to the two inlined copies it replaces, the internal/ placement follows the existing pattern (parse-cookie-header.ts), and the static-import constraint from server.ts → headers.ts (Vite "use cache" transform) is properly sidestepped.
Two minor observations — neither blocking:
-
RequestCookies.deleteinheaders.tsstill builds its deletion string inline (lines 907-923) rather than going throughserializeSetCookie. The format is slightly different (barename=vsname=%sviaencodeURIComponent(""), and it omits HttpOnly/Secure/SameSite), so leaving it separate is reasonable. But worth noting that if the deletion format ever needs to change, there are now three serialization sites rather than two — this one, plus theResponseCookies.deletepath (which delegates toset()→serializeSetCookie). -
SerializeSetCookieOptionsvsCookieOptions— the new module defines its own structurally-identical type rather than importing fromserver.ts(which would reintroduce the circular-import concern). That's correct, but ifCookieOptionslater gains fields (e.g.partitioned,priority), both types need updating. A shared type underinternal/would prevent drift, though that's a separate cleanup.
LGTM — the dedup is well-scoped and the PR description clearly explains what moved and what was intentionally left alone.
|
Review posted with approval on PR #1072. Summary: This is a clean, well-scoped dedup refactor. The new I noted two non-blocking observations:
|
Summary
Extract the shared Set-Cookie serialization from the next/headers and next/server shims into a single helper at
packages/vinext/src/shims/internal/cookie-serialize.ts. Follow-up to #1049.The two call sites —
cookies().set()inheaders.tsandResponseCookies.set()inserver.ts— were producing byte-for-byte identical Set-Cookie strings via copy-pasted code (sameencodeURIComponent(value), same defaultPath=/, same attribute order: Path → Domain → Max-Age → Expires → HttpOnly → Secure → SameSite, same validators). Pure refactor — no behavior change.The third candidate site,
RequestCookies._serializeinserver.ts:574, builds a requestCookie:header (name=value; name=value, no attributes) — different format, intentionally left alone.What moved
packages/vinext/src/shims/internal/cookie-serialize.ts(new):serializeSetCookie(name, value, options)— builds the full Set-Cookie value stringvalidateCookieName(name)— RFC 6265 §4.1.1 token validatorvalidateCookieAttributeValue(value, attr)— control-char/semicolon guardThe validators were also duplicated verbatim across both files; consolidating them avoids subtle drift.
Why a separate module instead of importing across shims
server.tsdeliberately avoids static-importingheaders.tsbecause of a Vite "use cache" transform issue (existing comment inserver.ts:18-30). The new helper lives underinternal/alongsideparse-cookie-header.ts, mirroring the existing pattern.Files changed
packages/vinext/src/shims/internal/cookie-serialize.ts(new, 84 lines)packages/vinext/src/shims/headers.ts(-43 lines, unchanged behavior)packages/vinext/src/shims/server.ts(-37 lines, unchanged behavior)Net: 9 insertions, 79 deletions across the two existing files.
Test plan
pnpm vp test run tests/app-router.test.ts— 308 tests passpnpm vp test run tests/pages-router.test.ts— 200 tests pass (one pre-existingafterAlltimeout in the unrelatedallowedDevOriginssuite, reproduces onmainwithout these changes)pnpm vp test run tests/shims.test.ts tests/app-route-handler-execution.test.ts tests/app-page-execution.test.ts tests/api-handler.test.ts— 912 tests passpnpm tsc --noEmit(inpackages/vinext) — cleanpnpm fmt --writeon touched files🤖 Generated with Claude Code