feat(vtex)!: forward Set-Cookie through cart-adjacent actions#56
Merged
Conversation
Closes the framework-level VTEX Set-Cookie propagation gap for sites
consuming `@decocms/apps` via `createServerFn` action handlers. VTEX's
`checkout.vtex.com` and `CheckoutOrderFormOwnership` cookies were
silently dropped on the action path, letting the storefront's local
orderForm reference drift away from VTEX's server-side orderForm.
Three fixes in one PR:
1. Cart-adjacent actions switched to `vtexFetchWithCookies`. Seven
`/api/checkout/...` actions previously used the plain `vtexFetch`
helper, which does not capture VTEX's `Set-Cookie` headers onto
`RequestContext.responseHeaders`. The bridge in generated
`invoke.gen.ts` had nothing to forward. Updated:
`simulateCart`, `setShippingPostalCode` (per VTEX docs, can rotate
the ownership cookie), `getInstallments`, `updateItemPrice`,
`changeToAnonymousUser`, `clearOrderFormMessages`,
`getSellersByRegion`.
2. Headers-aware merge in `vtexFetchWithCookies`. The previous
implementation cast `init.headers` to `Record<string, string>` and
spread it into a new object to inject the auto-forwarded `Cookie`
header. When the caller passed a `Headers` instance, the spread
collapsed to `{}` and silently wiped every other header the caller
had set (auth, content-type, trace context). Replaced with a
`withCookieHeader(headers, cookieValue)` helper backed by the
`Headers` constructor — the same Headers-aware fix that landed in
#53 for `vtexFetchResponse`, applied here for completeness.
3. Restore `vtex/invoke.ts` as the generator contract. The file was
deleted on 2026-03-30 (commit 0303cbb) on the assumption that
`setupApps()` would auto-register handlers from the manifest. But
the framework's `@decocms/start/scripts/generate-invoke.ts` still
scans this file as its source of truth, so sites running
`npm run generate:invoke` against any apps version >= 1.15.0 hit a
hard "invoke.ts not found" error. Restored at the current action
shapes (single-props-object call convention, no `VtexFetchResult`
unwrapping). All 17 actions previously exposed are re-exposed here.
Paired with a matching `@decocms/start` major that emits a
`forwardResponseCookies()` bridge in `invoke.gen.ts`. Sites get the
full fix after bumping both packages and re-running
`npm run generate:invoke`.
BREAKING CHANGE: requires the matching `@decocms/start` major
containing the `forwardResponseCookies()` emit. Sites bumping
`@decocms/apps` alone gain the per-action cookie capture but won't
propagate Set-Cookies to the browser until they also bump
`@decocms/start` and regenerate `src/server/invoke.gen.ts`.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
🎉 This PR is included in version 2.0.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the framework-level VTEX Set-Cookie propagation gap for sites consuming
@decocms/appsviacreateServerFnaction handlers. Three coupled fixes that have to ship together.The bug class
@decocms/apps/vtex/client.tsexposes two fetch helpers:vtexFetch— plain VTEX API call, noRequestContextinteraction.vtexFetchWithCookies— captures upstreamSet-Cookieheaders ontoRequestContext.responseHeaders, expects the host framework to forward them onto the actual HTTP response.The cookies that VTEX sets in response to cart-adjacent calls (
checkout.vtex.com,CheckoutOrderFormOwnership, segment cookies) need to reach the browser to keep the storefront's local orderForm reference and VTEX's server-side orderForm bound to the same orderFormId. Two independent gaps were stopping that.Hole A — cart-adjacent actions bypassed the cookie-capture helper
Seven actions hit
/api/checkout/...endpoints but used the plainvtexFetch, leaving anySet-CookieVTEX returned unobserved. The downstreamforwardResponseCookiesbridge had nothing to forward for these endpoints. Switched tovtexFetchWithCookies:simulateCartPOST /api/checkout/pub/orderForms/simulationsetShippingPostalCodePOST /api/checkout/pub/orderForm/{id}/attachments/shippingDataCheckoutOrderFormOwnership.getInstallmentsGET /api/checkout/pub/orderForm/{id}/installmentsupdateItemPricePUT /api/checkout/pub/orderForm/{id}/items/{idx}/pricechangeToAnonymousUserGET /api/checkout/changeToAnonymousUser/{id}clearOrderFormMessagesPOST /api/checkout/pub/orderForm/{id}/messages/cleargetSellersByRegionGET /api/checkout/pub/regions//api/checkout/*family is cookie-aware.Hole B — Headers-init silently collapsed in
vtexFetchWithCookiesThe function cast
init.headerstoRecord<string, string>and spread it into a new object to inject the auto-forwardedCookie:```ts
init = { ...init, headers: { ...existingHeaders, cookie: cookies } };
```
When the caller passes a
Headersinstance (whichcreateVtexCheckoutProxydoes invtexFetchResponse), spreading it collapses to{}— wiping every other header the caller had set (auth, content-type, trace context). Same failure mode that PR #53 fixed forvtexFetchResponse/vtexCachedFetch; this PR closes the same hole invtexFetchWithCookies.Replaced with a
withCookieHeader(headers, cookieValue)helper backed by theHeadersconstructor — absorbs everyHeadersInitshape (Headers,string[][],Record) correctly.Hole C — generator contract was deleted, sites can no longer regen
vtex/invoke.tswas deleted on 2026-03-30 (commit 0303cbb) on the assumption thatsetupApps()would auto-register handlers from the manifest. But@decocms/start/scripts/generate-invoke.tsstill scans this file as its source of truth, so sites runningnpm run generate:invokeagainst any apps version >= 1.15.0 hit a hardinvoke.ts not founderror.Restored at the current action shapes:
props-object call convention (matches every action signature invtex/actions/*)VtexFetchResultunwrapping (actions return data directly since 0303cbb)Changes
vtex/actions/checkout.ts— 7 cart-adjacent actions switched tovtexFetchWithCookies; drop the now-unusedvtexFetchimport.vtex/client.ts— newreadCookieHeader/withCookieHeaderhelpers;vtexFetchWithCookiesrefactored to funnel every header-merge throughHeaders.vtex/invoke.ts— restored generator contract, 17 actions, current props-object call shape.vtex/__tests__/client-set-cookie-forward.test.ts— 6 new regression tests:RequestContext.responseHeadersDomain=stripping (browser scope correction)vtex_is_session/vtex_is_anonymous(managed by middleware, not actions)RequestContextCoordination
Paired with
decocms/deco-start#195(matching major) that emits aforwardResponseCookies()bridge ininvoke.gen.ts. Sites get the full fix after bumping both packages and re-runningnpm run generate:invoke.Test plan
bun run test— 34 files, 424/424 passbun run typecheck— cleanbun run lint— 0 errors (pre-existing warnings only)@decocms/start@HEADgenerator run against this branch produces a validinvoke.gen.tswithforwardResponseCookies()calls on every handler.BREAKING CHANGE
Requires the matching
@decocms/startmajor containing theforwardResponseCookies()emit. Sites bumping@decocms/appsalone gain the per-action cookie capture but won't propagate Set-Cookies to the browser until they also bump@decocms/startand regeneratesrc/server/invoke.gen.ts.Made with Cursor
Summary by cubic
Forward VTEX Set-Cookie headers through cart-adjacent actions so checkout cookies reach the browser and the cart stays in sync. Fixes header merging in
vtexFetchWithCookiesand restores thevtex/invoke.tsgenerator contract.Bug Fixes
/api/checkout/*actions tovtexFetchWithCookies:simulateCart,setShippingPostalCode,getInstallments,updateItemPrice,changeToAnonymousUser,clearOrderFormMessages,getSellersByRegion.vtexFetchWithCookiesusingreadCookieHeaderandwithCookieHeaderto preserve all caller headers and sanitize cookies across allHeadersInitshapes.vtex/invoke.tssonpm run generate:invokeworks again and re-exposes 17 actions; added regression tests for Set-Cookie capture, domain stripping, skipping IS cookies, and header preservation.Migration
@decocms/startmajor that emitsforwardResponseCookies()in generated handlers.@decocms/appsand@decocms/start, then runnpm run generate:invoke.Written for commit 9ffefb3. Summary will update on new commits. Review in cubic