feat(core): TRAC-813 Gate analytics cookies behind shopper consent#3046
Merged
chanceaclark merged 1 commit intoJun 12, 2026
Conversation
🦋 Changeset detectedLatest commit: 5c8aab0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
Bundle Size ReportComparing against baseline from Per-Route First Load JS
|
3d151e9 to
6441b26
Compare
6441b26 to
5a7a44f
Compare
Contributor
Author
Screen.Recording.2026-06-11.at.16.27.34.mov |
When the store's cookie consent setting is enabled, the catalyst.visitorId and catalyst.visitId analytics cookies are no longer set or refreshed until the shopper grants measurement consent, and leftover cookies are deleted once consent is withdrawn. The currencyCode preference cookie now requires functionality consent. With no consent decision recorded yet, cookies are only allowed when the store does not require cookie consent. The proxy only starts visits on full page navigations, so granting consent mid-session now triggers a startVisit server action that sets the cookies and fires the server-side visitStartedEvent immediately. Server action POSTs no longer start visits in the proxy, which would otherwise duplicate the event fired by the action. Fixes TRAC-813 Co-Authored-By: Claude <noreply@anthropic.com>
5a7a44f to
5c8aab0
Compare
Contributor
Unlighthouse Performance Comparison — VercelComparing PR preview deployment Unlighthouse scores vs production Unlighthouse scores. Summary ScoreAggregate score across all categories as reported by Unlighthouse.
Category Scores
Core Web Vitals
|
jorgemoya
approved these changes
Jun 12, 2026
parthshahp
approved these changes
Jun 12, 2026
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.
Jira: TRAC-813
What/Why?
catalyst.visitorId,catalyst.visitId, andcurrencyCodewere set unconditionally, ignoring the shopper's consent decision. This gates them behind consent.Consent decision logic (new
hasConsentFor(category)inlib/consent-manager): thec15t-consentcookie is the source of truth. When no consent cookie exists yet, consent is treated as not given — the client-side consent manager writes the cookie once the shopper decides (or c15t auto-grants when consent is disabled), andstartVisithandles recording the visit at that point.Category mapping mirrors
scripts-transformer's BC-to-c15t map: the visit cookies are ANALYTICS →measurement;currencyCodeis a preference →functionality. Without functionality consent, switching currency still updates the cart but the preference isn't persisted.Withdrawal: the proxy deletes both analytics cookies when a consent cookie exists without measurement granted, so identifiers don't linger for the cookie's remaining 400-day lifetime after the shopper opts out.
Firing the visit event after mid-session consent (the open question in the ticket): the proxy only starts visits on full-page navigations (prefetch/RSC requests are skipped), so accepting the banner wouldn't record a visit until the next hard reload. A new
startVisitserver action sets the cookies and firesvisitStartedEvent, triggered by a small client listener when measurement consent flips to granted. Two non-obvious details:document.cookiedirectly before calling the action — c15t updates its React state before persisting the cookie, so the action (which validates consent from the cookie server-side) would no-op if called immediately after the state change.Next-Actionheader). Cookies set in the proxy aren't visible to the action handler's cookie store; without this, accepting the banner fired twovisitStartedEvents with different visit IDs.Also fixed a latent bug in two e2e expiry tests: they looked up
visitId/visitorIdinstead ofcatalyst.visitId/catalyst.visitorId, so their assertions never executed.Refs TRAC-813
Testing
pnpm lint,pnpm typecheck, and all 6analytics-session.spec.tse2e tests pass locally (tests now seed accept/decline consent cookies, so they're deterministic regardless of the store's consent setting; new tests cover the declined and withdrawal paths).Verified against a local dev server connected to a store with cookie consent enabled:
c.measurement:1VisitStartedmutationBrowser flow via Playwright: loading the home page sets no analytics cookies, clicking "Accept All" sets both cookies without a reload and fires exactly one
VisitStartedmutation, and a subsequent reload keeps the same visitId (sliding-window refresh, no duplicate event).Migration
None. No files moved;
withAnalyticsCookiesis back in its original position in the proxy chain.🤖 Generated with Claude Code