fix(tanstack): dedupe Set-Cookie by name + Domain + Path, not name alone
Problem
deduplicateSetCookies (packages/tanstack/src/sdk/workerEntry.ts) keeps only the last Set-Cookie header per cookie name. A response that clears the same cookie under several scopes collapses to a single header, and the other scopes stay alive in the browser.
The browser keys its cookie jar by name + Domain + Path (RFC 6265 §5.3 step 11): a clear for the host-only cookie does not touch the Domain=example.com one, and vice-versa. Two Set-Cookie headers that differ in scope address two different cookies and must both reach the client.
Reproduction (measured on the site)
/telesales/sign-out on deco-sites/oficina-tanstack expires each session cookie under the three scopes the cookie might have been created with (host-only, Domain=www.<host>, Domain=<parent>), because the response that created it is not ours to inspect:
Set-Cookie: VtexIdclientAutCookie=; Path=/; Max-Age=0
Set-Cookie: VtexIdclientAutCookie=; Path=/; Max-Age=0; Domain=www.example.com
Set-Cookie: VtexIdclientAutCookie=; Path=/; Max-Age=0; Domain=example.com
Through the worker only the parent-domain clear survived, so the host-only admin token the telesales punchout lands stayed in the browser: the next operator on the machine inherited the session. The site currently answers that route ahead of the framework to dodge the dedupe (src/worker-entry.ts, TELESALES_SIGN_OUT_PATH).
Reference behavior
Fresh/deco-runtime never deduplicates Set-Cookie; the multi-scope clear is the site's own expireCookies (sdk/telesales/session-reset.ts, domainScopes) and reaches the browser intact.
Fix
- New
setCookieIdentity(setCookie)— name + Domain (lowercased, leading dot stripped) + Path; exported for tests. deduplicateSetCookieskeys on that identity. True duplicates (same identity) still collapse to the last one, which is the behavior the layers that append the same cookie (VTEX middleware, invoke handlers) rely on.
Tests
packages/tanstack/src/sdk/workerEntry.test.ts — 3 new tests: identity differs by scope; identity ignores domain case/leading dot and the value; end-to-end through createDecoWorkerEntry a three-scope clear survives while a same-scope duplicate collapses.
bunx vitest run packages/tanstack/src/sdk/workerEntry.test.ts: the 3 new tests pass; the 3 failures in that file (draft preview (pull-based) ×2, CDN-Cache-Control … defaults to no-store) are pre-existing on main (same result on a clean checkout). tsc --noEmit for packages/tanstack clean; biome check clean on the touched files.
Found while validating deco-sites/oficina-tanstack#479.
🤖 Generated with Claude Code
Summary by cubic
Fixes Set-Cookie deduplication so multi-scope cookie clears reach the browser intact instead of collapsing to a single scope.
deduplicateSetCookies in packages/tanstack/src/sdk/workerEntry.ts kept only the last Set-Cookie header per cookie name, so a sign-out that clears the same cookie under host-only, Domain=www.<host>, and Domain=<parent> scopes left the other scopes' cookies alive in the browser. Deduplication now keys on name + Domain + Path, matching how browsers key their cookie jar (RFC 6265 §5.3); Domain is case-insensitive and ignores a leading dot. True duplicates (same name, domain, and path) still collapse to the last header.
Adds three tests covering scope identity, domain normalization, and end-to-end deduplication through createDecoWorkerEntry.