Skip to content

Commit 24664db

Browse files
authored
[2025-04 back-fix] 🍪 Hydrogen Cookie Migration for New Shopify Cookie Architecture (#3332)
* Add tracking cookies migration infrastructure This commit adds the infrastructure for migrating tracking cookies from legacy _shopify_y/_shopify_s cookies to consolidated encrypted cookies using Server-Timing headers. Key changes: - Add tracking-utils.ts with getTrackingValues() to read tracking values from Server-Timing headers via Performance API - Add server-timing.ts with utilities for detecting SFAPI proxy mode and server-returned tracking values - Update storefront client to forward tracking values via headers - Add sameDomainForStorefrontApi option for proxy detection - Update customer-privacy component to support new cookie migration - Add buyerIpSig to StorefrontHeaders type - Update useShopifyCookies with fetchTrackingValues option * Add E2E tests for tracking cookies migration This commit adds comprehensive E2E tests for the tracking cookies migration feature, including both new and legacy cookie scenarios. Test structure: - new-cookies/: Tests for the new consolidated cookie system - consent-tracking-accept/decline.spec.ts - privacy-banner-accept/decline/consent-change.spec.ts - privacy-banner-migration.spec.ts (migration from legacy cookies) - old-cookies/: Tests for legacy _shopify_y/_shopify_s cookies - consent-tracking-accept/decline.spec.ts - privacy-banner-accept/decline.spec.ts - smoke/: Basic smoke tests for cart and home page Also adds: - Unit tests for tracking-utils.ts and server-timing.ts - E2E fixtures for server setup and storefront mocking - Environment configurations for different consent scenarios - Updated playwright configuration * Fix: Delay PerfKit loading until consent is collected This fixes a bug where PerfKit would load before consent was collected, causing it to use stale tracking values. The fix delays PerfKit loading until after the consent collection callback is fired. Changes: - Add consentCollected state to AnalyticsProvider - Only render PerfKit after consentCollected is true - Update onVisitorConsentCollected callback to track consent state - Add sameDomainForStorefrontApi to Consent type * Add SFAPI proxy support to createRequestHandler - Update @shopify/remix-oxygen createRequestHandler with SFAPI proxy - Add new createRequestHandler export to @Shopify/hydrogen - Proxy automatically routes /api/.../graphql.json to Storefront API - Forward Set-Cookie and server-timing headers from subrequests - Set _sfapi_proxy server-timing header for document requests - New proxyStorefrontApiRequests option (default: true) * Update package-lock.json for version bumps * Align createRequestHandler with PR #3309 Updates both @Shopify/hydrogen and @shopify/remix-oxygen createRequestHandler to match the implementation in PR #3309: - Add collectTrackingInformation option (default: true) - Add warning when storefront instance is missing from context - Use appendServerTimingHeader utility instead of manual header - Move poweredByHeader append to after tracking header processing - Add TODO comments for future major version changes - Improve JSDoc documentation for all options * Add missing getTrackingValues export from hydrogen package Re-export getTrackingValues from @shopify/hydrogen-react to align with PR #3309 cookie migration implementation. * Add buyerIpSig to remix-oxygen StorefrontHeaders Add the buyerIpSig field to both the StorefrontHeaders type and getStorefrontHeaders function in remix-oxygen to align with the hydrogen package's StorefrontHeaders type. This fixes type compatibility when passing getStorefrontHeaders() result to createStorefrontClient(). * Add sec-purpose header check to remix-oxygen getStorefrontHeaders Align with hydrogen's getStorefrontHeaders to check sec-purpose first, then fall back to purpose. This ensures browser-initiated prefetches (via Speculation Rules API or <link rel="prefetch">) are properly detected. * Rename proxyStorefrontApiRequests to proxyStandardRoutes Aligns the parameter name with PR #3309 for consistency. * Update Playwright to 1.57.0 Fixes Chromium crashes (SIGTRAP/SEGV) on macOS 15 Sequoia when running e2e tests. The older version 1.40.1 had compatibility issues with the newer macOS version. * Fix e2e test bundle interception pattern for Vite pre-bundled deps Update the route interception pattern in setWithPrivacyBanner to match Vite's pre-bundled dependency path format. The previous pattern (**/@fs/**/hydrogen/dist/**/*.js) didn't match the actual paths served by Vite dev server (**/node_modules/.vite/deps/@shopify_hydrogen.js*). * Fix * fix * Fix create-hydrogen snapshot for TokenlessApi route Update the integration test snapshot to include the TokenlessApi route that was added in #2948 but the snapshot was never updated. Also includes skeleton template changes for the cookie migration backport: - Use createRequestHandler from @Shopify/hydrogen with proxyStandardRoutes - Minor formatting cleanup in root.tsx * Disable proxyStandardRoutes in classic-remix example The classic-remix example uses @shopify/remix-oxygen's createRequestHandler which now defaults to proxyStandardRoutes: true. This requires a storefront instance with specific methods that the classic-remix example doesn't provide. Setting proxyStandardRoutes: false restores the previous behavior and fixes the dev test. * Also disable collectTrackingInformation in classic-remix example The classic-remix example doesn't use the full Hydrogen stack, so disable both proxyStandardRoutes and collectTrackingInformation to avoid issues with the new request handler options. * Guard getSetCookie call for environments without Headers support Some environments like the classic Remix compiler may not have the getSetCookie method on their Headers implementation. * Remove redundant in skeleton template; update changesets to be more consistent with 2025-07 PR
1 parent c39c766 commit 24664db

56 files changed

Lines changed: 4684 additions & 141 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/cold-rules-wave.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@shopify/hydrogen': patch
3+
---
4+
5+
This version adds support for the new cookie system in Shopify (`_shopify_analytics` and `_shopify_marketing` http-only cookies). It is backward compatible and still supports the deprecated `_shopify_y` and `_shopify_s` cookies.
6+
7+
- `createRequestHandler` can now be used for every Hydrogen app, not only the ones deployed to Oxygen. It is now exported from `@shopify/hydrogen`.
8+
- A new Storefront API proxy is now available in Hydrogen's `createRequestHandler`. This will be used to obtain http-only cookies from Storefront API. In general, it should be transparent but it can be disabled with the `proxyStandardRoutes` option.
9+
- `Analytics.Provider` component and `useCustomerPrivacy` hook now make a request internally to the mentioned proxy to obtain cookies in the storefront domain.

.changeset/remix-oxygen-proxy.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@shopify/remix-oxygen': minor
3+
---
4+
5+
6+
Support Shopify's new consolidated cookie architecture. Adds built-in Storefront API proxy support to `createRequestHandler`.

.changeset/rotten-bobcats-grab.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@shopify/hydrogen-react': patch
3+
---
4+
5+
New export `getTrackingValues` to obtain information for analytics and marketing. Use this instead of `getShopifyCookies` (which is now deprecated).
6+
7+
`useShopifyCookies` now accepts a `fetchTrackingValues` parameter that can be used to make a Storefront API request and obtain Shopify http-only cookies, `_shopify_analytics` and `_shopify_marketing` (which replace the deprecated `_shopify_y` and `_shopify_s` cookies).

.changeset/two-melons-design.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/hydrogen': patch
3+
---
4+
5+
Fixed a number of issues related to irregular behaviors between Privacy Banner and Hydrogen's analytics events.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
SESSION_SECRET="mock-session"
2+
PUBLIC_CHECKOUT_DOMAIN="checkout.hydrogen.shop"
3+
PUBLIC_STORE_DOMAIN="checkout.hydrogen.shop"
4+
PUBLIC_STOREFRONT_API_TOKEN="b97a750a8afa8fe33f2b4012cb3a9f6f"
5+
PUBLIC_STOREFRONT_ID="1000014875"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
SESSION_SECRET="mock-session"
2+
PUBLIC_CHECKOUT_DOMAIN="www.iwantacheapdomainfortesting12345.club"
3+
PUBLIC_STORE_DOMAIN="www.iwantacheapdomainfortesting12345.club"
4+
PUBLIC_STOREFRONT_API_TOKEN="2aac2e4420f32ba0c7dadf55c7cc387b"
5+
PUBLIC_STOREFRONT_ID="1000070232"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
SESSION_SECRET="mock-session"
2+
PUBLIC_CHECKOUT_DOMAIN="www.kara2345.xyz"
3+
PUBLIC_STORE_DOMAIN="www.kara2345.xyz"
4+
PUBLIC_STOREFRONT_API_TOKEN="8eece95833df895900c1b285987c7f40"
5+
PUBLIC_STOREFRONT_ID="1000070242"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
SESSION_SECRET="mock-session"
2+
PUBLIC_CHECKOUT_DOMAIN="checkout.daviduik.com"
3+
PUBLIC_STORE_DOMAIN="checkout.daviduik.com"
4+
PUBLIC_STOREFRONT_API_TOKEN="a79d329fc13657352c6e4734e5d4ca75"
5+
PUBLIC_STOREFRONT_ID="1000061747"

e2e/envs/.env.mockShop

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
SESSION_SECRET="mock-session"
2+
PUBLIC_CHECKOUT_DOMAIN="mock.shop"
3+
PUBLIC_STORE_DOMAIN="mock.shop"
4+
PUBLIC_STOREFRONT_API_TOKEN=""
5+
PUBLIC_STOREFRONT_ID=""

e2e/fixtures/index.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {test as base} from '@playwright/test';
2+
import {DevServer} from './server';
3+
import path from 'node:path';
4+
import {stat} from 'node:fs/promises';
5+
import {StorefrontPage} from './storefront';
6+
7+
export * from '@playwright/test';
8+
export * from './storefront';
9+
10+
export const test = base.extend<
11+
{storefront: StorefrontPage},
12+
{forEachWorker: void}
13+
>({
14+
storefront: async ({page}, use) => {
15+
const storefront = new StorefrontPage(page);
16+
// eslint-disable-next-line react-hooks/rules-of-hooks
17+
await use(storefront);
18+
},
19+
});
20+
21+
const TEST_STORE_KEYS = [
22+
'mockShop',
23+
'defaultConsentDisallowed_cookiesEnabled',
24+
'defaultConsentAllowed_cookiesEnabled',
25+
'defaultConsentDisallowed_cookiesDisabled',
26+
'defaultConsentAllowed_cookiesDisabled',
27+
] as const;
28+
29+
type TestStoreKey = (typeof TEST_STORE_KEYS)[number];
30+
31+
export const setTestStore = async (
32+
testStore: TestStoreKey | `https://${string}`,
33+
) => {
34+
const isLocal = !testStore.startsWith('https://');
35+
let server: DevServer | null = null;
36+
37+
test.use({
38+
// eslint-disable-next-line no-empty-pattern
39+
baseURL: async ({}, use) => {
40+
// eslint-disable-next-line react-hooks/rules-of-hooks
41+
await use(isLocal ? server?.getUrl() : testStore);
42+
},
43+
});
44+
45+
if (!isLocal) {
46+
console.log(`Using test store: ${testStore}`);
47+
return;
48+
}
49+
50+
test.afterAll(async () => {
51+
await server?.stop();
52+
});
53+
54+
// eslint-disable-next-line no-empty-pattern
55+
test.beforeAll(async ({}) => {
56+
const filepath = path.resolve(__dirname, `../envs/.env.${testStore}`);
57+
await stat(filepath); // Ensure the file exists
58+
59+
server = new DevServer({
60+
storeKey: testStore,
61+
customerAccountPush: false,
62+
envFile: filepath,
63+
});
64+
65+
await server.start();
66+
});
67+
};

0 commit comments

Comments
 (0)