Skip to content

feat: track captcha analytics#7822

Merged
kernelwhisperer merged 7 commits into
developfrom
feat/captcha-analytics
Jul 21, 2026
Merged

feat: track captcha analytics#7822
kernelwhisperer merged 7 commits into
developfrom
feat/captcha-analytics

Conversation

@kernelwhisperer

@kernelwhisperer kernelwhisperer commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds captcha analytics and simplifies shared analytics tracking.

  • Add createCowTracker() helper for category-scoped analytics events
  • Allow analytics event objects to carry custom params like reason
  • Move analytics transport failure handling into GTM data layer push
  • Replace GTM debug console.debug with logAnalytics
  • Track captcha challenge lifecycle events, excluding Turnstile demo site key traffic
  • Simplify affiliate analytics by relying on shared analytics handling

Summary by CodeRabbit

  • New Features

    • Added analytics tracking across the CAPTCHA Turnstile lifecycle, including start, interaction, success, and failure states.
    • Expanded analytics event support for custom fields and optional chain-specific reporting.
    • Introduced a dedicated event-tracking utility for consistent analytics category usage.
  • Bug Fixes

    • Hardened GTM analytics delivery: data layer pushes are now safely handled and failures trigger warnings without breaking the app.
    • Improved payload sanitization so undefined optional fields (e.g., reason) are omitted.
  • Tests

    • Updated analytics-related mocks and assertions to reflect the new tracking and payload behavior.

@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cowfi Ready Ready Preview Jul 21, 2026 11:19am
explorer-dev Ready Ready Preview Jul 21, 2026 11:19am
storybook Ready Ready Preview Jul 21, 2026 11:19am
swap-dev Ready Ready Preview Jul 21, 2026 11:19am
swap-prod-new Ready Ready Preview Jul 21, 2026 11:19am
widget-configurator Ready Ready Preview Jul 21, 2026 11:19am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
cosmos Ignored Ignored Jul 21, 2026 11:19am
sdk-tools Ignored Ignored Preview Jul 21, 2026 11:19am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: aceff9ed-24f5-4b44-bac7-2e07cd4e3195

📥 Commits

Reviewing files that changed from the base of the PR and between 6207bf5 and b5fe814.

📒 Files selected for processing (13)
  • apps/cowswap-frontend/jest.setup.ts
  • apps/cowswap-frontend/src/common/analytics/types.ts
  • apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.test.ts
  • apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts
  • apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx
  • apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx
  • libs/analytics/src/CowAnalytics.ts
  • libs/analytics/src/createCowTracker.ts
  • libs/analytics/src/gtm/CowAnalyticsGtm.test.ts
  • libs/analytics/src/gtm/CowAnalyticsGtm.ts
  • libs/analytics/src/index.ts
  • libs/analytics/src/noop/createNoopCowAnalytics.ts
  • libs/common-utils/src/logger.ts
💤 Files with no reviewable changes (1)
  • apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
  • apps/cowswap-frontend/src/common/analytics/types.ts
  • libs/common-utils/src/logger.ts
  • libs/analytics/src/createCowTracker.ts
  • libs/analytics/src/index.ts
  • libs/analytics/src/noop/createNoopCowAnalytics.ts
  • libs/analytics/src/gtm/CowAnalyticsGtm.ts
  • libs/analytics/src/gtm/CowAnalyticsGtm.test.ts
  • libs/analytics/src/CowAnalytics.ts
  • apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.test.ts
  • apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx
  • apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts

Walkthrough

Adds broader analytics event typing and GTM error handling, introduces a reusable tracker factory, instruments CAPTCHA lifecycle events, changes affiliate payload dispatch, and centralizes frontend analytics test mocks.

Changes

Analytics tracking rework and CAPTCHA integration

Layer / File(s) Summary
Analytics contracts and tracker factory
libs/analytics/src/CowAnalytics.ts, libs/analytics/src/createCowTracker.ts, libs/analytics/src/index.ts, libs/analytics/src/noop/*, libs/common-utils/src/logger.ts
Analytics events support extended metadata and optional chain IDs; the tracker factory is exported, noop analytics accepts the new type, and logAnalytics is available.
GTM data layer push handling
libs/analytics/src/gtm/*
GTM accepts the broader event type, conditionally logs debug data, sanitizes payloads, and warns when data layer pushes fail.
CAPTCHA lifecycle analytics
apps/cowswap-frontend/src/common/analytics/types.ts, apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx
Turnstile callbacks emit categorized lifecycle events, while the demo site key uses a no-op tracker.
Affiliate analytics dispatch
apps/cowswap-frontend/src/modules/affiliate/analytics/*, apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx
Affiliate events send complete payloads directly, and related logging and exception-continuation assertions are removed.
Shared analytics Jest mocks
apps/cowswap-frontend/jest.setup.ts
Tracker and hook mocks share one sendEvent spy and support disabled tracking.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Turnstile
  participant CaptchaWidget
  participant trackCaptcha
  participant CowAnalytics
  Turnstile->>CaptchaWidget: lifecycle callback
  CaptchaWidget->>trackCaptcha: CAPTCHA event
  trackCaptcha->>CowAnalytics: sendEvent
Loading

Possibly related PRs

Suggested reviewers: danziger, shoom3301, limitofzero, elena-zh

Poem

I’m a rabbit hopping through events,
With CAPTCHA trails and logging scents.
Start, solve, fail—I track them all,
While shared mocks stand stout and tall.
Analytics hops along its way!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the summary well, but it omits the required To Test steps and Background section from the template. Add a To Test section with concrete verification steps and include Background and issue details if applicable.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately captures the main change: adding captcha analytics tracking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/captcha-analytics

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 7, 2026

Copy link
Copy Markdown

Deploying explorer-dev with  Cloudflare Pages  Cloudflare Pages

Latest commit: 60b2a0f
Status: ✅  Deploy successful!
Preview URL: https://df84b8dd.explorer-dev-dxz.pages.dev
Branch Preview URL: https://feat-captcha-analytics.explorer-dev-dxz.pages.dev

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 7, 2026

Copy link
Copy Markdown

Deploying swap-dev with  Cloudflare Pages  Cloudflare Pages

Latest commit: 60b2a0f
Status: ✅  Deploy successful!
Preview URL: https://d32d34db.swap-dev-5u6.pages.dev
Branch Preview URL: https://feat-captcha-analytics.swap-dev-5u6.pages.dev

View logs

@kernelwhisperer
kernelwhisperer requested a review from a team July 9, 2026 14:14
@kernelwhisperer
kernelwhisperer marked this pull request as ready for review July 9, 2026 14:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx (2)

141-147: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Include errorCode in the analytics event.

onError receives errorCode and logs it, but doesn't pass it to trackCaptcha. Including it as a custom param would make captcha_challenge_failed events with reason: 'turnstileError' actionable in analytics dashboards without cross-referencing logs.

♻️ Pass errorCode to trackCaptcha
       onError={(errorCode) => {
         exchangeRequestIdRef.current += 1

         logCaptcha.error('Challenge errored', { errorCode, hostname: window.location.hostname })
-        trackCaptcha({ action: 'captcha_challenge_failed', reason: 'turnstileError' })
+        trackCaptcha({ action: 'captcha_challenge_failed', reason: 'turnstileError', errorCode })
         setCaptchaJwt(null)
       }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx`
around lines 141 - 147, The CaptchaWidget.container.tsx onError handler logs
errorCode but does not send it with the captcha analytics event, so the failure
data is incomplete. Update the onError callback in CaptchaWidget.container to
pass errorCode through to trackCaptcha as an additional custom param alongside
action and reason, keeping the existing logCaptcha.error call unchanged.

29-31: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider memoizing trackCaptcha.

createCowTracker is called in the component body, producing a new closure on every render. Since Turnstile uses key={siteKey}, callbacks are refreshed on siteKey change regardless, so this is not a bug — but wrapping in useMemo keyed on siteKey avoids needless allocations and makes the enabled-dependency explicit.

♻️ Wrap in useMemo
+  const trackCaptcha = useMemo(
+    () => createCowTracker(CowSwapAnalyticsCategory.CAPTCHA, {
+      enabled: siteKey !== TURNSTILE_DEMO_INTERACTIVE_SITE_KEY,
+    }),
+    [siteKey]
+  )
-  const trackCaptcha = createCowTracker(CowSwapAnalyticsCategory.CAPTCHA, {
-    enabled: siteKey !== TURNSTILE_DEMO_INTERACTIVE_SITE_KEY,
-  })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx`
around lines 29 - 31, The `trackCaptcha` callback in `CaptchaWidget.container`
is recreated on every render because `createCowTracker` is called directly in
the component body. Wrap the `createCowTracker(CowSwapAnalyticsCategory.CAPTCHA,
{ enabled: siteKey !== TURNSTILE_DEMO_INTERACTIVE_SITE_KEY })` call in
`useMemo`, keyed on `siteKey`, so the closure is only rebuilt when the site key
changes and the `enabled` dependency is explicit.
libs/analytics/src/CowAnalytics.ts (1)

3-3: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant union member in AnalyticsEvent.

EventOptions & Record<string, unknown> is assignable to EventOptions, so the third union member adds no new type-level coverage. The union collapses to string | EventOptions. If the intent is to signal that arbitrary extra keys are allowed, a comment or a dedicated branded type would be clearer than a redundant intersection.

♻️ Simplify the union
-export type AnalyticsEvent = string | EventOptions | (EventOptions & Record<string, unknown>)
+export type AnalyticsEvent = string | EventOptions
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/analytics/src/CowAnalytics.ts` at line 3, Simplify the AnalyticsEvent
type by removing the redundant EventOptions & Record<string, unknown> union
member, since it is already covered by EventOptions. Update the exported type in
CowAnalytics to use only string | EventOptions, and if extra arbitrary fields
are intended, make that intent explicit elsewhere with a clearer type or comment
rather than keeping an equivalent union branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts`:
- Around line 31-38: The `trackAffiliateEvent` helper is masking a `chainId`
type mismatch with an `as GtmEvent` cast, allowing invalid numeric values to
reach `analytics.sendEvent`. Fix this in `trackAffiliateEvent` by removing the
cast-based bypass and either narrowing `TrackAffiliateEventParams.chainId` to
`SupportedChainId` or validating/coercing `chainId` before building the event
payload so the `GtmEvent` type is satisfied without unsafe assertions.

---

Nitpick comments:
In
`@apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx`:
- Around line 141-147: The CaptchaWidget.container.tsx onError handler logs
errorCode but does not send it with the captcha analytics event, so the failure
data is incomplete. Update the onError callback in CaptchaWidget.container to
pass errorCode through to trackCaptcha as an additional custom param alongside
action and reason, keeping the existing logCaptcha.error call unchanged.
- Around line 29-31: The `trackCaptcha` callback in `CaptchaWidget.container` is
recreated on every render because `createCowTracker` is called directly in the
component body. Wrap the `createCowTracker(CowSwapAnalyticsCategory.CAPTCHA, {
enabled: siteKey !== TURNSTILE_DEMO_INTERACTIVE_SITE_KEY })` call in `useMemo`,
keyed on `siteKey`, so the closure is only rebuilt when the site key changes and
the `enabled` dependency is explicit.

In `@libs/analytics/src/CowAnalytics.ts`:
- Line 3: Simplify the AnalyticsEvent type by removing the redundant
EventOptions & Record<string, unknown> union member, since it is already covered
by EventOptions. Update the exported type in CowAnalytics to use only string |
EventOptions, and if extra arbitrary fields are intended, make that intent
explicit elsewhere with a clearer type or comment rather than keeping an
equivalent union branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a4f68aa5-6f3e-42ff-9343-5cbe96d382e8

📥 Commits

Reviewing files that changed from the base of the PR and between 4b8c6af and e675478.

📒 Files selected for processing (14)
  • apps/cowswap-frontend/jest.setup.ts
  • apps/cowswap-frontend/src/common/analytics/types.ts
  • apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.test.ts
  • apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts
  • apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx
  • apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx
  • apps/cowswap-frontend/vercel.ts
  • libs/analytics/src/CowAnalytics.ts
  • libs/analytics/src/createCowTracker.ts
  • libs/analytics/src/gtm/CowAnalyticsGtm.test.ts
  • libs/analytics/src/gtm/CowAnalyticsGtm.ts
  • libs/analytics/src/index.ts
  • libs/analytics/src/noop/createNoopCowAnalytics.ts
  • libs/common-utils/src/logger.ts
💤 Files with no reviewable changes (1)
  • apps/cowswap-frontend/src/modules/affiliate/hooks/useAffiliatePartnerCodeCreate.test.tsx

Comment on lines 31 to 38
export function trackAffiliateEvent({ analytics, action, chainId, ...customParams }: TrackAffiliateEventParams): void {
try {
analytics.sendEvent(
compactRecord({
category: CowSwapAnalyticsCategory.AFFILIATE,
action,
chainId,
...customParams,
}) as GtmEvent<CowSwapAnalyticsCategory.AFFILIATE>,
)
} catch (error) {
logAffiliate('Failed to send analytics event', { action, error })
}
analytics.sendEvent({
category: CowSwapAnalyticsCategory.AFFILIATE,
action,
chainId,
...customParams,
} as GtmEvent<CowSwapAnalyticsCategory.AFFILIATE>)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

as GtmEvent cast bypasses chainId type mismatch.

TrackAffiliateEventParams.chainId is typed SupportedChainId | number, but EventOptions.chainId expects SupportedChainId. The cast silently widens invalid numeric values through to the analytics payload. Either narrow the parameter type or validate at the boundary.

🛡️ Narrow chainId type instead of casting
 interface TrackAffiliateEventParams {
   analytics: CowAnalytics
   action: AffiliateAnalyticsAction
-  chainId?: SupportedChainId | number
+  chainId?: SupportedChainId
   [key: string]: unknown
 }

If raw number support is intentional, validate before sending:

 export function trackAffiliateEvent({ analytics, action, chainId, ...customParams }: TrackAffiliateEventParams): void {
+  if (chainId !== undefined && !Object.values(SupportedChainId).includes(chainId as SupportedChainId)) {
+    return
+  }
   analytics.sendEvent({
     category: CowSwapAnalyticsCategory.AFFILIATE,
     action,
     chainId,
     ...customParams,
   } as GtmEvent<CowSwapAnalyticsCategory.AFFILIATE>)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/cowswap-frontend/src/modules/affiliate/analytics/affiliateAnalytics.utils.ts`
around lines 31 - 38, The `trackAffiliateEvent` helper is masking a `chainId`
type mismatch with an `as GtmEvent` cast, allowing invalid numeric values to
reach `analytics.sendEvent`. Fix this in `trackAffiliateEvent` by removing the
cast-based bypass and either narrowing `TrackAffiliateEventParams.chainId` to
`SupportedChainId` or validating/coercing `chainId` before building the event
payload so the `GtmEvent` type is satisfied without unsafe assertions.

@fairlighteth fairlighteth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ AI Review (Codex GPT-5, worked 10m): question on reserved GTM custom-param names

Finding: [QUESTION] Should custom analytics params protect GTM’s reserved names?

  • Location: libs/analytics/src/gtm/CowAnalyticsGtm.ts:264
  • I do not see a current regression in this PR: the new captcha/affiliate events use custom fields like reason, result, and failureReason.
  • The edge case is the new generic custom-param path. GTM uses event as the event name, and this code builds official fields first, then spreads getAdditionalEventParams(...) last. A future caller passing { event: 'wrong_name' } could overwrite the canonical event: event.action.
  • This pattern is not new only in this PR: toGtmEvent() has a similar “official fields first, extra fields last” shape. The difference is that this PR makes arbitrary custom params a first-class path through AnalyticsEvent / createCowTracker, so this may be a good moment to define the reserved-key behavior centrally.

Suggested fix

  • Filter reserved GTM output keys like event from custom params, or spread custom params before the official GTM fields so official fields always win.
  • Add a small CowAnalyticsGtm unit test showing { action: 'captcha_challenge_failed', event: 'wrong_name' } still sends event: 'captcha_challenge_failed'.
Review scope and related context

This is separate from existing review comments:

  • CodeRabbit already covers the affiliate chainId cast.
  • CodeRabbit’s captcha errorCode note is about richer analytics metadata; this question is about protecting reserved GTM output fields.
  • This is not framed as a current PR regression, only as an API-contract question now that custom params are easier to send.
🤖 Prompt for AI agents
Verify this finding against current code. Fix only if still valid, keep the change minimal, and validate with a targeted unit test.

Context:
- libs/analytics/src/CowAnalytics.ts now allows custom params through AnalyticsEvent.
- libs/analytics/src/createCowTracker.ts forwards arbitrary custom params.
- libs/analytics/src/gtm/CowAnalyticsGtm.ts spreads getAdditionalEventParams(gtmEvent) after canonical fields, so a custom `event` key could override the GTM event name.
- toGtmEvent() has a similar shape, so prefer a central reserved-key policy if practical.

Generated using the pr-review skill from the CoW Protocol skills repo.

Comment thread apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx Outdated
Comment thread apps/cowswap-frontend/vercel.ts Outdated

@azebuado azebuado left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A minor question.
Nothing else to add other than looking at Sasha's comments.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/analytics/src/createCowTracker.ts`:
- Around line 6-10: Update CowTrackerEvent and createCowTracker so event
payloads cannot include or override category; omit category from the accepted
event type and ensure the constructed sendEvent payload always preserves the
category argument.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 147bea4e-2078-49fb-80a5-dc3c299934f0

📥 Commits

Reviewing files that changed from the base of the PR and between 6ca1948 and 6207bf5.

📒 Files selected for processing (2)
  • apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx
  • libs/analytics/src/createCowTracker.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/cowswap-frontend/src/modules/captcha/containers/CaptchaWidget.container.tsx

Comment thread libs/analytics/src/createCowTracker.ts
@kernelwhisperer

Copy link
Copy Markdown
Contributor Author

What's the best wallet for this

CosmicWallet

@cowprotocol cowprotocol deleted a comment from Themoor1 Jul 20, 2026
@cowprotocol cowprotocol deleted a comment from Themoor1 Jul 21, 2026
kernelwhisperer and others added 7 commits July 21, 2026 14:12
## Summary

- Sets `Cross-Origin-Opener-Policy` to `unsafe-none` for non-production
Vercel deployments.
- Keeps production on `same-origin-allow-popups`.

## Why

Google Tag Assistant preview can lose its cross-origin opener/debug
connection when the preview page sends a stricter COOP header. This
branch gives us an isolated Vercel preview URL to verify whether
relaxing COOP fixes the Tag Assistant connection without changing the
original captcha analytics PR branch.

## Preview

-
https://swap-dev-git-agent-gtm-tag-assistant-coop-preview-cowswap-dev.vercel.app/?gtm_debug=x
- Verified response header: `cross-origin-opener-policy: unsafe-none`

## Checks

- `pnpm exec prettier --check apps/cowswap-frontend/vercel.ts`
- `pnpm exec eslint apps/cowswap-frontend/vercel.ts`
- `pnpm exec tsc --noEmit --pretty false --skipLibCheck
--moduleResolution node16 --module Node16 --target ES2022
apps/cowswap-frontend/vercel.ts`
- `curl -sI
'https://swap-dev-git-agent-gtm-tag-assistant-coop-preview-cowswap-dev.vercel.app/?gtm_debug=x'`
@kernelwhisperer
kernelwhisperer force-pushed the feat/captcha-analytics branch from 6207bf5 to b5fe814 Compare July 21, 2026 11:12
@kernelwhisperer
kernelwhisperer enabled auto-merge (squash) July 21, 2026 11:12
@kernelwhisperer
kernelwhisperer merged commit 90b82e7 into develop Jul 21, 2026
19 checks passed
@kernelwhisperer
kernelwhisperer deleted the feat/captcha-analytics branch July 21, 2026 11:19
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants