@shopify/hydrogen 2025.7.0 → 2025.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev/{get-virtual-routes-KWSSQH4L.js → get-virtual-routes-ZEUPNZWL.js} +0 -36
- package/dist/dev/hydrogen-routes.js +1 -1
- package/dist/development/{get-virtual-routes-6PVSMJPH.js → get-virtual-routes-XE7G57DS.js} +3 -36
- package/dist/development/get-virtual-routes-XE7G57DS.js.map +1 -0
- package/dist/development/index.cjs +406 -124
- package/dist/development/index.cjs.map +1 -1
- package/dist/development/index.js +395 -81
- package/dist/development/index.js.map +1 -1
- package/dist/development/react-router-preset.d.ts +5 -5
- package/dist/development/react-router-preset.js +4 -4
- package/dist/development/react-router-preset.js.map +1 -1
- package/dist/oxygen/index.d.ts +120 -3
- package/dist/oxygen/index.js +111 -5
- package/dist/production/get-virtual-routes-MYYLGSAS.js +3 -0
- package/dist/production/get-virtual-routes-MYYLGSAS.js.map +1 -0
- package/dist/production/index.cjs +76 -76
- package/dist/production/index.cjs.map +1 -1
- package/dist/production/index.d.cts +76 -15
- package/dist/production/index.d.ts +76 -15
- package/dist/production/index.js +76 -76
- package/dist/production/index.js.map +1 -1
- package/dist/production/react-router-preset.d.ts +5 -5
- package/dist/production/react-router-preset.js +2 -2
- package/dist/vite/get-virtual-routes.d.ts +1 -17
- package/dist/vite/get-virtual-routes.js +0 -36
- package/package.json +6 -6
- package/dist/development/get-virtual-routes-6PVSMJPH.js.map +0 -1
- package/dist/oxygen/chunk-RVXKHOUX.js +0 -39
- package/dist/oxygen/chunk-T4YWBSCF.js +0 -14
- package/dist/oxygen/createRequestHandler.d.ts +0 -10
- package/dist/oxygen/createRequestHandler.js +0 -6
- package/dist/oxygen/getStorefrontHeaders-BqPh5S1b.d.ts +0 -69
- package/dist/oxygen/getStorefrontHeaders.d.ts +0 -1
- package/dist/oxygen/getStorefrontHeaders.js +0 -6
- package/dist/production/get-virtual-routes-JVKSNI4M.js +0 -3
- package/dist/production/get-virtual-routes-JVKSNI4M.js.map +0 -1
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var url = require('url');
|
|
4
|
-
var path = require('path');
|
|
5
|
-
var promises = require('fs/promises');
|
|
6
3
|
var react = require('react');
|
|
7
4
|
var reactRouter = require('react-router');
|
|
8
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
9
6
|
var hydrogenReact = require('@shopify/hydrogen-react');
|
|
7
|
+
var loadScript = require('@shopify/hydrogen-react/load-script');
|
|
10
8
|
var graphqlClient = require('@shopify/graphql-client');
|
|
11
9
|
var cookie = require('worktop/cookie');
|
|
12
10
|
var cspBuilder = require('content-security-policy-builder');
|
|
@@ -14,7 +12,6 @@ var cspBuilder = require('content-security-policy-builder');
|
|
|
14
12
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
15
13
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
14
|
|
|
17
|
-
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
18
15
|
var cspBuilder__default = /*#__PURE__*/_interopDefault(cspBuilder);
|
|
19
16
|
|
|
20
17
|
var __defProp = Object.defineProperty;
|
|
@@ -30,13 +27,9 @@ var __export = (target, all) => {
|
|
|
30
27
|
// src/vite/get-virtual-routes.ts
|
|
31
28
|
var get_virtual_routes_exports = {};
|
|
32
29
|
__export(get_virtual_routes_exports, {
|
|
33
|
-
VIRTUAL_ROOT: () => VIRTUAL_ROOT,
|
|
34
|
-
VIRTUAL_ROOT_ORIG: () => VIRTUAL_ROOT_ORIG,
|
|
35
30
|
VIRTUAL_ROUTES_DIR: () => VIRTUAL_ROUTES_DIR,
|
|
36
|
-
VIRTUAL_ROUTES_DIR_ORIG: () => VIRTUAL_ROUTES_DIR_ORIG,
|
|
37
31
|
VIRTUAL_ROUTES_DIR_PARTS: () => VIRTUAL_ROUTES_DIR_PARTS,
|
|
38
32
|
VIRTUAL_ROUTES_ROUTES_DIR_PARTS: () => VIRTUAL_ROUTES_ROUTES_DIR_PARTS,
|
|
39
|
-
getVirtualRoutes: () => getVirtualRoutes,
|
|
40
33
|
getVirtualRoutesV3: () => getVirtualRoutesV3
|
|
41
34
|
});
|
|
42
35
|
function getVirtualRoutesPath(pathParts, forFile) {
|
|
@@ -94,33 +87,7 @@ async function getVirtualRoutesV3() {
|
|
|
94
87
|
}
|
|
95
88
|
};
|
|
96
89
|
}
|
|
97
|
-
|
|
98
|
-
const distPath = path__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
99
|
-
const virtualRoutesPath = path__default.default.join(distPath, VIRTUAL_ROUTES_DIR_ORIG);
|
|
100
|
-
const routes = await promises.readdir(virtualRoutesPath, { recursive: true }).then(
|
|
101
|
-
(files) => files.map((relativeFilePath) => {
|
|
102
|
-
const absoluteFilePath = path__default.default.join(virtualRoutesPath, relativeFilePath);
|
|
103
|
-
const id = relativeFilePath.replace(/\.[jt]sx?$/, "").replaceAll("\\", "/");
|
|
104
|
-
const isIndex = /(^|\/)index$/.test(id);
|
|
105
|
-
const routePath = id.replace(/(^|\/)index$/, "");
|
|
106
|
-
return {
|
|
107
|
-
id: `${VIRTUAL_ROUTES_DIR_ORIG}/${id}`,
|
|
108
|
-
path: routePath,
|
|
109
|
-
file: absoluteFilePath,
|
|
110
|
-
index: isIndex
|
|
111
|
-
};
|
|
112
|
-
})
|
|
113
|
-
);
|
|
114
|
-
return {
|
|
115
|
-
routes,
|
|
116
|
-
root: {
|
|
117
|
-
id: VIRTUAL_ROOT_ORIG,
|
|
118
|
-
path: "",
|
|
119
|
-
file: path__default.default.join(distPath, VIRTUAL_ROOT_ORIG + ".jsx")
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
var VIRTUAL_ROUTES_DIR, VIRTUAL_ROUTES_ROUTES_DIR_PARTS, VIRTUAL_ROUTES_DIR_PARTS, VIRTUAL_ROOT, VIRTUAL_ROUTES_DIR_ORIG, VIRTUAL_ROOT_ORIG;
|
|
90
|
+
var VIRTUAL_ROUTES_DIR, VIRTUAL_ROUTES_ROUTES_DIR_PARTS, VIRTUAL_ROUTES_DIR_PARTS;
|
|
124
91
|
var init_get_virtual_routes = __esm({
|
|
125
92
|
"src/vite/get-virtual-routes.ts"() {
|
|
126
93
|
VIRTUAL_ROUTES_DIR = "vite/virtual-routes/routes";
|
|
@@ -130,9 +97,6 @@ var init_get_virtual_routes = __esm({
|
|
|
130
97
|
"routes"
|
|
131
98
|
];
|
|
132
99
|
VIRTUAL_ROUTES_DIR_PARTS = ["vite", "virtual-routes"];
|
|
133
|
-
VIRTUAL_ROOT = "vite/virtual-routes/virtual-root";
|
|
134
|
-
VIRTUAL_ROUTES_DIR_ORIG = "virtual-routes/routes";
|
|
135
|
-
VIRTUAL_ROOT_ORIG = "virtual-routes/virtual-root-with-layout";
|
|
136
100
|
}
|
|
137
101
|
});
|
|
138
102
|
|
|
@@ -281,7 +245,62 @@ var AnalyticsEvent = {
|
|
|
281
245
|
// Custom
|
|
282
246
|
CUSTOM_EVENT: `custom_`
|
|
283
247
|
};
|
|
284
|
-
|
|
248
|
+
|
|
249
|
+
// src/constants.ts
|
|
250
|
+
var STOREFRONT_REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
|
|
251
|
+
var STOREFRONT_ACCESS_TOKEN_HEADER = "X-Shopify-Storefront-Access-Token";
|
|
252
|
+
var SDK_VARIANT_HEADER = "X-SDK-Variant";
|
|
253
|
+
var SDK_VARIANT_SOURCE_HEADER = "X-SDK-Variant-Source";
|
|
254
|
+
var SDK_VERSION_HEADER = "X-SDK-Version";
|
|
255
|
+
var SHOPIFY_CLIENT_IP_HEADER = "X-Shopify-Client-IP";
|
|
256
|
+
var SHOPIFY_CLIENT_IP_SIG_HEADER = "X-Shopify-Client-IP-Sig";
|
|
257
|
+
var HYDROGEN_SFAPI_PROXY_KEY = "_sfapi_proxy";
|
|
258
|
+
var HYDROGEN_SERVER_TRACKING_KEY = "_server_tracking";
|
|
259
|
+
|
|
260
|
+
// src/utils/server-timing.ts
|
|
261
|
+
function buildServerTimingHeader(values) {
|
|
262
|
+
return Object.entries(values).map(([key, value]) => value ? `${key};desc=${value}` : void 0).filter(Boolean).join(", ");
|
|
263
|
+
}
|
|
264
|
+
function appendServerTimingHeader(response, values) {
|
|
265
|
+
const header = typeof values === "string" ? values : buildServerTimingHeader(values);
|
|
266
|
+
if (header) {
|
|
267
|
+
response.headers.append("Server-Timing", header);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
var trackedTimings = ["_y", "_s", "_cmp"];
|
|
271
|
+
function extractServerTimingHeader(serverTimingHeader) {
|
|
272
|
+
const values = {};
|
|
273
|
+
if (!serverTimingHeader) return values;
|
|
274
|
+
const re = new RegExp(
|
|
275
|
+
`\\b(${trackedTimings.join("|")});desc="?([^",]+)"?`,
|
|
276
|
+
"g"
|
|
277
|
+
);
|
|
278
|
+
let match;
|
|
279
|
+
while ((match = re.exec(serverTimingHeader)) !== null) {
|
|
280
|
+
values[match[1]] = match[2];
|
|
281
|
+
}
|
|
282
|
+
return values;
|
|
283
|
+
}
|
|
284
|
+
function hasServerTimingInNavigationEntry(key) {
|
|
285
|
+
if (typeof window === "undefined") return false;
|
|
286
|
+
try {
|
|
287
|
+
const navigationEntry = window.performance.getEntriesByType(
|
|
288
|
+
"navigation"
|
|
289
|
+
)[0];
|
|
290
|
+
return !!navigationEntry?.serverTiming?.some((entry) => entry.name === key);
|
|
291
|
+
} catch (e) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function isSfapiProxyEnabled() {
|
|
296
|
+
return hasServerTimingInNavigationEntry(HYDROGEN_SFAPI_PROXY_KEY);
|
|
297
|
+
}
|
|
298
|
+
function hasServerReturnedTrackingValues() {
|
|
299
|
+
return hasServerTimingInNavigationEntry(HYDROGEN_SERVER_TRACKING_KEY);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/customer-privacy/ShopifyCustomerPrivacy.tsx
|
|
303
|
+
var CONSENT_API = "https://cdn.shopify.com/shopifycloud/consent-tracking-api/v0.2/consent-tracking-api.js";
|
|
285
304
|
var CONSENT_API_WITH_BANNER = "https://cdn.shopify.com/shopifycloud/privacy-banner/storefront-banner.js";
|
|
286
305
|
function logMissingConfig(fieldName) {
|
|
287
306
|
console.error(
|
|
@@ -293,19 +312,34 @@ function useCustomerPrivacy(props) {
|
|
|
293
312
|
withPrivacyBanner = false,
|
|
294
313
|
onVisitorConsentCollected,
|
|
295
314
|
onReady,
|
|
296
|
-
|
|
315
|
+
checkoutDomain,
|
|
316
|
+
storefrontAccessToken,
|
|
317
|
+
country,
|
|
318
|
+
locale,
|
|
319
|
+
sameDomainForStorefrontApi
|
|
297
320
|
} = props;
|
|
298
|
-
|
|
321
|
+
const hasSfapiProxy = react.useMemo(
|
|
322
|
+
() => sameDomainForStorefrontApi ?? isSfapiProxyEnabled(),
|
|
323
|
+
[sameDomainForStorefrontApi]
|
|
324
|
+
);
|
|
325
|
+
const fetchTrackingValuesFromBrowser = react.useMemo(
|
|
326
|
+
() => hasSfapiProxy && !hasServerReturnedTrackingValues(),
|
|
327
|
+
[hasSfapiProxy]
|
|
328
|
+
);
|
|
329
|
+
const cookiesReady = hydrogenReact.useShopifyCookies({
|
|
330
|
+
fetchTrackingValues: fetchTrackingValuesFromBrowser,
|
|
331
|
+
storefrontAccessToken,
|
|
332
|
+
ignoreDeprecatedCookies: true
|
|
333
|
+
});
|
|
334
|
+
const initialTrackingValues = react.useMemo(hydrogenReact.getTrackingValues, [cookiesReady]);
|
|
335
|
+
const { revalidate } = reactRouter.useRevalidator();
|
|
336
|
+
loadScript.useLoadScript(withPrivacyBanner ? CONSENT_API_WITH_BANNER : CONSENT_API, {
|
|
299
337
|
attributes: {
|
|
300
338
|
id: "customer-privacy-api"
|
|
301
339
|
}
|
|
302
340
|
});
|
|
303
|
-
const { observing, setLoaded } = useApisLoaded({
|
|
304
|
-
withPrivacyBanner,
|
|
305
|
-
onLoaded: onReady
|
|
306
|
-
});
|
|
341
|
+
const { observing, setLoaded, apisLoaded } = useApisLoaded({ withPrivacyBanner });
|
|
307
342
|
const config = react.useMemo(() => {
|
|
308
|
-
const { checkoutDomain, storefrontAccessToken } = consentConfig;
|
|
309
343
|
if (!checkoutDomain) logMissingConfig("checkoutDomain");
|
|
310
344
|
if (!storefrontAccessToken) logMissingConfig("storefrontAccessToken");
|
|
311
345
|
if (storefrontAccessToken.startsWith("shpat_") || storefrontAccessToken.length !== 32) {
|
|
@@ -313,18 +347,54 @@ function useCustomerPrivacy(props) {
|
|
|
313
347
|
`[h2:error:useCustomerPrivacy] It looks like you passed a private access token, make sure to use the public token`
|
|
314
348
|
);
|
|
315
349
|
}
|
|
350
|
+
const commonAncestorDomain = parseStoreDomain(checkoutDomain);
|
|
351
|
+
const sfapiDomain = (
|
|
352
|
+
// Check if standard route proxy is enabled in Hydrogen server
|
|
353
|
+
// to use it instead of doing a cross-origin request to checkout.
|
|
354
|
+
hasSfapiProxy && typeof window !== "undefined" ? window.location.host : checkoutDomain
|
|
355
|
+
);
|
|
316
356
|
const config2 = {
|
|
317
|
-
|
|
357
|
+
// This domain is used to send requests to SFAPI for setting and getting consent.
|
|
358
|
+
checkoutRootDomain: sfapiDomain,
|
|
359
|
+
// Prefix with a dot to ensure this domain is different from checkoutRootDomain.
|
|
360
|
+
// This will ensure old cookies are set for a cross-subdomain checkout setup
|
|
361
|
+
// so that we keep backward compatibility until new cookies are rolled out.
|
|
362
|
+
// Once consent-tracking-api is updated to not rely on cookies anymore, we can remove this.
|
|
363
|
+
storefrontRootDomain: commonAncestorDomain ? "." + commonAncestorDomain : void 0,
|
|
318
364
|
storefrontAccessToken,
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
locale: consentConfig.locale
|
|
365
|
+
country,
|
|
366
|
+
locale
|
|
322
367
|
};
|
|
323
368
|
return config2;
|
|
324
|
-
}, [
|
|
369
|
+
}, [
|
|
370
|
+
logMissingConfig,
|
|
371
|
+
checkoutDomain,
|
|
372
|
+
storefrontAccessToken,
|
|
373
|
+
country,
|
|
374
|
+
locale
|
|
375
|
+
]);
|
|
325
376
|
react.useEffect(() => {
|
|
326
377
|
const consentCollectedHandler = (event) => {
|
|
378
|
+
const latestTrackingValues = hydrogenReact.getTrackingValues();
|
|
379
|
+
if (initialTrackingValues.visitToken !== latestTrackingValues.visitToken || initialTrackingValues.uniqueToken !== latestTrackingValues.uniqueToken) {
|
|
380
|
+
revalidate().catch(() => {
|
|
381
|
+
console.warn(
|
|
382
|
+
"[h2:warn:useCustomerPrivacy] Revalidation failed after consent change."
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
}
|
|
327
386
|
if (onVisitorConsentCollected) {
|
|
387
|
+
const customerPrivacy = getCustomerPrivacy();
|
|
388
|
+
if (customerPrivacy?.shouldShowBanner()) {
|
|
389
|
+
const consentValues = customerPrivacy.currentVisitorConsent();
|
|
390
|
+
if (consentValues) {
|
|
391
|
+
const NO_VALUE = "";
|
|
392
|
+
const noInteraction = consentValues.marketing === NO_VALUE && consentValues.analytics === NO_VALUE && consentValues.preferences === NO_VALUE;
|
|
393
|
+
if (noInteraction) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
328
398
|
onVisitorConsentCollected(event.detail);
|
|
329
399
|
}
|
|
330
400
|
};
|
|
@@ -350,14 +420,11 @@ function useCustomerPrivacy(props) {
|
|
|
350
420
|
},
|
|
351
421
|
set(value) {
|
|
352
422
|
if (typeof value === "object" && value !== null && "showPreferences" in value && "loadBanner" in value) {
|
|
353
|
-
const privacyBanner = value;
|
|
354
|
-
privacyBanner.loadBanner(config);
|
|
355
423
|
customPrivacyBanner = overridePrivacyBannerMethods({
|
|
356
|
-
privacyBanner,
|
|
424
|
+
privacyBanner: value,
|
|
357
425
|
config
|
|
358
426
|
});
|
|
359
427
|
setLoaded.privacyBanner();
|
|
360
|
-
emitCustomerPrivacyApiLoaded();
|
|
361
428
|
}
|
|
362
429
|
}
|
|
363
430
|
};
|
|
@@ -391,6 +458,8 @@ function useCustomerPrivacy(props) {
|
|
|
391
458
|
const customerPrivacy = value2;
|
|
392
459
|
customCustomerPrivacy = {
|
|
393
460
|
...customerPrivacy,
|
|
461
|
+
// Note: this method is not used by the privacy-banner,
|
|
462
|
+
// it bundles its own setTrackingConsent.
|
|
394
463
|
setTrackingConsent: overrideCustomerPrivacySetTrackingConsent(
|
|
395
464
|
{ customerPrivacy, config }
|
|
396
465
|
)
|
|
@@ -400,7 +469,6 @@ function useCustomerPrivacy(props) {
|
|
|
400
469
|
customerPrivacy: customCustomerPrivacy
|
|
401
470
|
};
|
|
402
471
|
setLoaded.customerPrivacy();
|
|
403
|
-
emitCustomerPrivacyApiLoaded();
|
|
404
472
|
}
|
|
405
473
|
}
|
|
406
474
|
});
|
|
@@ -412,6 +480,24 @@ function useCustomerPrivacy(props) {
|
|
|
412
480
|
overrideCustomerPrivacySetTrackingConsent,
|
|
413
481
|
setLoaded.customerPrivacy
|
|
414
482
|
]);
|
|
483
|
+
react.useEffect(() => {
|
|
484
|
+
if (!apisLoaded || !cookiesReady) return;
|
|
485
|
+
const customerPrivacy = getCustomerPrivacy();
|
|
486
|
+
if (customerPrivacy && !customerPrivacy.cachedConsent) {
|
|
487
|
+
const trackingValues = hydrogenReact.getTrackingValues();
|
|
488
|
+
if (trackingValues.consent) {
|
|
489
|
+
customerPrivacy.cachedConsent = trackingValues.consent;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (withPrivacyBanner) {
|
|
493
|
+
const privacyBanner = getPrivacyBanner();
|
|
494
|
+
if (privacyBanner) {
|
|
495
|
+
privacyBanner.loadBanner(config);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
emitCustomerPrivacyApiLoaded();
|
|
499
|
+
onReady?.();
|
|
500
|
+
}, [apisLoaded, cookiesReady]);
|
|
415
501
|
const result = {
|
|
416
502
|
customerPrivacy: getCustomerPrivacy()
|
|
417
503
|
};
|
|
@@ -427,15 +513,12 @@ function emitCustomerPrivacyApiLoaded() {
|
|
|
427
513
|
const event = new CustomEvent("shopifyCustomerPrivacyApiLoaded");
|
|
428
514
|
document.dispatchEvent(event);
|
|
429
515
|
}
|
|
430
|
-
function useApisLoaded({
|
|
431
|
-
withPrivacyBanner,
|
|
432
|
-
onLoaded
|
|
433
|
-
}) {
|
|
516
|
+
function useApisLoaded({ withPrivacyBanner }) {
|
|
434
517
|
const observing = react.useRef({ customerPrivacy: false, privacyBanner: false });
|
|
435
|
-
const [
|
|
518
|
+
const [apisLoadedArray, setApisLoaded] = react.useState(
|
|
436
519
|
withPrivacyBanner ? [false, false] : [false]
|
|
437
520
|
);
|
|
438
|
-
const
|
|
521
|
+
const apisLoaded = apisLoadedArray.every(Boolean);
|
|
439
522
|
const setLoaded = {
|
|
440
523
|
customerPrivacy: () => {
|
|
441
524
|
if (withPrivacyBanner) {
|
|
@@ -451,16 +534,11 @@ function useApisLoaded({
|
|
|
451
534
|
setApisLoaded((prev) => [prev[0], true]);
|
|
452
535
|
}
|
|
453
536
|
};
|
|
454
|
-
|
|
455
|
-
if (loaded && onLoaded) {
|
|
456
|
-
onLoaded();
|
|
457
|
-
}
|
|
458
|
-
}, [loaded, onLoaded]);
|
|
459
|
-
return { observing, setLoaded };
|
|
537
|
+
return { observing, setLoaded, apisLoaded };
|
|
460
538
|
}
|
|
461
539
|
function parseStoreDomain(checkoutDomain) {
|
|
462
540
|
if (typeof window === "undefined") return;
|
|
463
|
-
const host = window.
|
|
541
|
+
const host = window.location.host;
|
|
464
542
|
const checkoutDomainParts = checkoutDomain.split(".").reverse();
|
|
465
543
|
const currentDomainParts = host.split(".").reverse();
|
|
466
544
|
const sameDomainParts = [];
|
|
@@ -469,7 +547,7 @@ function parseStoreDomain(checkoutDomain) {
|
|
|
469
547
|
sameDomainParts.push(part);
|
|
470
548
|
}
|
|
471
549
|
});
|
|
472
|
-
return sameDomainParts.reverse().join(".");
|
|
550
|
+
return sameDomainParts.reverse().join(".") || void 0;
|
|
473
551
|
}
|
|
474
552
|
function overrideCustomerPrivacySetTrackingConsent({
|
|
475
553
|
customerPrivacy,
|
|
@@ -527,7 +605,7 @@ function getPrivacyBanner() {
|
|
|
527
605
|
}
|
|
528
606
|
|
|
529
607
|
// package.json
|
|
530
|
-
var version = "2025.7.
|
|
608
|
+
var version = "2025.7.2";
|
|
531
609
|
|
|
532
610
|
// src/analytics-manager/ShopifyAnalytics.tsx
|
|
533
611
|
function getCustomerPrivacyRequired() {
|
|
@@ -547,6 +625,7 @@ function ShopifyAnalytics({
|
|
|
547
625
|
const { subscribe: subscribe2, register: register2, canTrack } = useAnalytics();
|
|
548
626
|
const [shopifyReady, setShopifyReady] = react.useState(false);
|
|
549
627
|
const [privacyReady, setPrivacyReady] = react.useState(false);
|
|
628
|
+
const [collectedConsent, setCollectedConsent] = react.useState("");
|
|
550
629
|
const init = react.useRef(false);
|
|
551
630
|
const { checkoutDomain, storefrontAccessToken, language } = consent;
|
|
552
631
|
const { ready: shopifyAnalyticsReady } = register2("Internal_Shopify_Analytics");
|
|
@@ -555,14 +634,31 @@ function ShopifyAnalytics({
|
|
|
555
634
|
locale: language,
|
|
556
635
|
checkoutDomain: !checkoutDomain ? "mock.shop" : checkoutDomain,
|
|
557
636
|
storefrontAccessToken: !storefrontAccessToken ? "abcdefghijklmnopqrstuvwxyz123456" : storefrontAccessToken,
|
|
558
|
-
|
|
559
|
-
|
|
637
|
+
// If we use privacy banner, we should wait until consent is collected.
|
|
638
|
+
// Otherwise, we can consider privacy ready immediately:
|
|
639
|
+
onReady: () => !consent.withPrivacyBanner && setPrivacyReady(true),
|
|
640
|
+
onVisitorConsentCollected: (consent2) => {
|
|
641
|
+
try {
|
|
642
|
+
setCollectedConsent(JSON.stringify(consent2));
|
|
643
|
+
} catch (e) {
|
|
644
|
+
}
|
|
645
|
+
setPrivacyReady(true);
|
|
646
|
+
}
|
|
560
647
|
});
|
|
648
|
+
const hasUserConsent = react.useMemo(
|
|
649
|
+
// must be initialized with true to avoid removing cookies too early
|
|
650
|
+
() => privacyReady ? canTrack() : true,
|
|
651
|
+
// Make this value depend on collectedConsent to re-run `canTrack()` when consent changes
|
|
652
|
+
[privacyReady, canTrack, collectedConsent]
|
|
653
|
+
);
|
|
561
654
|
hydrogenReact.useShopifyCookies({
|
|
562
|
-
hasUserConsent
|
|
563
|
-
// must be initialized with true
|
|
655
|
+
hasUserConsent,
|
|
564
656
|
domain,
|
|
565
|
-
checkoutDomain
|
|
657
|
+
checkoutDomain,
|
|
658
|
+
// Already done inside useCustomerPrivacy
|
|
659
|
+
fetchTrackingValues: false,
|
|
660
|
+
// Avoid creating local cookies too early
|
|
661
|
+
ignoreDeprecatedCookies: !privacyReady
|
|
566
662
|
});
|
|
567
663
|
react.useEffect(() => {
|
|
568
664
|
if (init.current) return;
|
|
@@ -612,11 +708,11 @@ function prepareBasePageViewPayload(payload) {
|
|
|
612
708
|
...payload.shop,
|
|
613
709
|
hasUserConsent,
|
|
614
710
|
...hydrogenReact.getClientBrowserParameters(),
|
|
615
|
-
ccpaEnforced: !customerPrivacy.saleOfDataAllowed(),
|
|
616
|
-
gdprEnforced: !(customerPrivacy.marketingAllowed() && customerPrivacy.analyticsProcessingAllowed()),
|
|
617
711
|
analyticsAllowed: customerPrivacy.analyticsProcessingAllowed(),
|
|
618
712
|
marketingAllowed: customerPrivacy.marketingAllowed(),
|
|
619
|
-
saleOfDataAllowed: customerPrivacy.saleOfDataAllowed()
|
|
713
|
+
saleOfDataAllowed: customerPrivacy.saleOfDataAllowed(),
|
|
714
|
+
ccpaEnforced: !customerPrivacy.saleOfDataAllowed(),
|
|
715
|
+
gdprEnforced: !(customerPrivacy.marketingAllowed() && customerPrivacy.analyticsProcessingAllowed())
|
|
620
716
|
};
|
|
621
717
|
return eventPayload;
|
|
622
718
|
}
|
|
@@ -1049,11 +1145,11 @@ function AnalyticsProvider({
|
|
|
1049
1145
|
shop: shopProp = null,
|
|
1050
1146
|
cookieDomain
|
|
1051
1147
|
}) {
|
|
1052
|
-
const listenerSet = react.useRef(false);
|
|
1053
1148
|
const { shop } = useShopAnalytics(shopProp);
|
|
1054
1149
|
const [analyticsLoaded, setAnalyticsLoaded] = react.useState(
|
|
1055
1150
|
customCanTrack ? true : false
|
|
1056
1151
|
);
|
|
1152
|
+
const [consentCollected, setConsentCollected] = react.useState(false);
|
|
1057
1153
|
const [carts, setCarts] = react.useState({ cart: null, prevCart: null });
|
|
1058
1154
|
const [canTrack, setCanTrack] = react.useState(
|
|
1059
1155
|
customCanTrack ? () => customCanTrack : () => shopifyCanTrack
|
|
@@ -1121,21 +1217,21 @@ function AnalyticsProvider({
|
|
|
1121
1217
|
children,
|
|
1122
1218
|
!!shop && /* @__PURE__ */ jsxRuntime.jsx(AnalyticsPageView, {}),
|
|
1123
1219
|
!!shop && !!currentCart && /* @__PURE__ */ jsxRuntime.jsx(CartAnalytics, { cart: currentCart, setCarts }),
|
|
1124
|
-
!!shop &&
|
|
1220
|
+
!!shop && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1125
1221
|
ShopifyAnalytics,
|
|
1126
1222
|
{
|
|
1127
1223
|
consent,
|
|
1128
1224
|
onReady: () => {
|
|
1129
|
-
listenerSet.current = true;
|
|
1130
1225
|
setAnalyticsLoaded(true);
|
|
1131
1226
|
setCanTrack(
|
|
1132
1227
|
customCanTrack ? () => customCanTrack : () => shopifyCanTrack
|
|
1133
1228
|
);
|
|
1229
|
+
setConsentCollected(true);
|
|
1134
1230
|
},
|
|
1135
1231
|
domain: cookieDomain
|
|
1136
1232
|
}
|
|
1137
1233
|
),
|
|
1138
|
-
!!shop && /* @__PURE__ */ jsxRuntime.jsx(PerfKit, { shop })
|
|
1234
|
+
!!shop && consentCollected && /* @__PURE__ */ jsxRuntime.jsx(PerfKit, { shop })
|
|
1139
1235
|
] });
|
|
1140
1236
|
}
|
|
1141
1237
|
function useAnalytics() {
|
|
@@ -1214,6 +1310,31 @@ function getDebugHeaders(request) {
|
|
|
1214
1310
|
purpose: request ? getHeader(request, "purpose") : void 0
|
|
1215
1311
|
};
|
|
1216
1312
|
}
|
|
1313
|
+
function getStorefrontHeaders(request) {
|
|
1314
|
+
return {
|
|
1315
|
+
requestGroupId: getHeader(request, "request-id"),
|
|
1316
|
+
buyerIp: getHeader(request, "oxygen-buyer-ip"),
|
|
1317
|
+
buyerIpSig: getHeader(request, SHOPIFY_CLIENT_IP_SIG_HEADER),
|
|
1318
|
+
cookie: getHeader(request, "cookie"),
|
|
1319
|
+
// sec-purpose is added by browsers automatically when using link/prefetch or Speculation Rules
|
|
1320
|
+
purpose: getHeader(request, "sec-purpose") || getHeader(request, "purpose")
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
var SFAPI_RE = /^\/api\/(unstable|2\d{3}-\d{2})\/graphql\.json$/;
|
|
1324
|
+
var getSafePathname = (url) => {
|
|
1325
|
+
try {
|
|
1326
|
+
return new URL(url, "http://e.c").pathname;
|
|
1327
|
+
} catch {
|
|
1328
|
+
return "/";
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
function extractHeaders(extract, keys) {
|
|
1332
|
+
return keys.reduce((acc, key) => {
|
|
1333
|
+
const forwardedValue = extract(key);
|
|
1334
|
+
if (forwardedValue) acc.push([key, forwardedValue]);
|
|
1335
|
+
return acc;
|
|
1336
|
+
}, []);
|
|
1337
|
+
}
|
|
1217
1338
|
|
|
1218
1339
|
// src/utils/callsites.ts
|
|
1219
1340
|
function withSyncStack(promise, options = {}) {
|
|
@@ -1581,13 +1702,16 @@ async function runWithCache(cacheKey, actionFn, {
|
|
|
1581
1702
|
}
|
|
1582
1703
|
return result;
|
|
1583
1704
|
}
|
|
1705
|
+
var excludedHeaders = ["set-cookie", "server-timing"];
|
|
1584
1706
|
function toSerializableResponse(body, response) {
|
|
1585
1707
|
return [
|
|
1586
1708
|
body,
|
|
1587
1709
|
{
|
|
1588
1710
|
status: response.status,
|
|
1589
1711
|
statusText: response.statusText,
|
|
1590
|
-
headers:
|
|
1712
|
+
headers: [...response.headers].filter(
|
|
1713
|
+
([key]) => !excludedHeaders.includes(key.toLowerCase())
|
|
1714
|
+
)
|
|
1591
1715
|
}
|
|
1592
1716
|
];
|
|
1593
1717
|
}
|
|
@@ -1601,7 +1725,8 @@ async function fetchWithServerCache(url, requestInit, {
|
|
|
1601
1725
|
shouldCacheResponse,
|
|
1602
1726
|
waitUntil,
|
|
1603
1727
|
debugInfo,
|
|
1604
|
-
streamConfig
|
|
1728
|
+
streamConfig,
|
|
1729
|
+
onRawHeaders
|
|
1605
1730
|
}) {
|
|
1606
1731
|
if (!cacheOptions && (!requestInit.method || requestInit.method === "GET")) {
|
|
1607
1732
|
cacheOptions = CacheShort();
|
|
@@ -1615,6 +1740,7 @@ async function fetchWithServerCache(url, requestInit, {
|
|
|
1615
1740
|
url,
|
|
1616
1741
|
customFetchApi: async (url2, options) => {
|
|
1617
1742
|
rawResponse = await fetch(url2, options);
|
|
1743
|
+
onRawHeaders?.(rawResponse.headers);
|
|
1618
1744
|
return rawResponse;
|
|
1619
1745
|
},
|
|
1620
1746
|
headers: requestInit.headers
|
|
@@ -1638,6 +1764,7 @@ async function fetchWithServerCache(url, requestInit, {
|
|
|
1638
1764
|
);
|
|
1639
1765
|
}
|
|
1640
1766
|
const response = await fetch(url, requestInit);
|
|
1767
|
+
onRawHeaders?.(response.headers);
|
|
1641
1768
|
if (!response.ok) {
|
|
1642
1769
|
return response;
|
|
1643
1770
|
}
|
|
@@ -1861,13 +1988,6 @@ var cartSetIdDefault = (cookieOptions) => {
|
|
|
1861
1988
|
};
|
|
1862
1989
|
};
|
|
1863
1990
|
|
|
1864
|
-
// src/constants.ts
|
|
1865
|
-
var STOREFRONT_REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
|
|
1866
|
-
var STOREFRONT_ACCESS_TOKEN_HEADER = "X-Shopify-Storefront-Access-Token";
|
|
1867
|
-
var SDK_VARIANT_HEADER = "X-SDK-Variant";
|
|
1868
|
-
var SDK_VARIANT_SOURCE_HEADER = "X-SDK-Variant-Source";
|
|
1869
|
-
var SDK_VERSION_HEADER = "X-SDK-Version";
|
|
1870
|
-
|
|
1871
1991
|
// src/utils/uuid.ts
|
|
1872
1992
|
function generateUUID() {
|
|
1873
1993
|
if (typeof crypto !== "undefined" && !!crypto.randomUUID) {
|
|
@@ -1878,7 +1998,7 @@ function generateUUID() {
|
|
|
1878
1998
|
}
|
|
1879
1999
|
|
|
1880
2000
|
// src/version.ts
|
|
1881
|
-
var LIB_VERSION = "2025.7.
|
|
2001
|
+
var LIB_VERSION = "2025.7.2";
|
|
1882
2002
|
|
|
1883
2003
|
// src/utils/graphql.ts
|
|
1884
2004
|
function minifyQuery(string) {
|
|
@@ -2042,16 +2162,34 @@ function createStorefrontClient(options) {
|
|
|
2042
2162
|
contentType: "json",
|
|
2043
2163
|
buyerIp: storefrontHeaders?.buyerIp || ""
|
|
2044
2164
|
});
|
|
2165
|
+
if (storefrontHeaders?.buyerIp) {
|
|
2166
|
+
defaultHeaders[SHOPIFY_CLIENT_IP_HEADER] = storefrontHeaders.buyerIp;
|
|
2167
|
+
}
|
|
2168
|
+
if (storefrontHeaders?.buyerIpSig) {
|
|
2169
|
+
defaultHeaders[SHOPIFY_CLIENT_IP_SIG_HEADER] = storefrontHeaders.buyerIpSig;
|
|
2170
|
+
}
|
|
2045
2171
|
defaultHeaders[STOREFRONT_REQUEST_GROUP_ID_HEADER] = storefrontHeaders?.requestGroupId || generateUUID();
|
|
2046
2172
|
if (storefrontId) defaultHeaders[hydrogenReact.SHOPIFY_STOREFRONT_ID_HEADER] = storefrontId;
|
|
2047
2173
|
defaultHeaders["user-agent"] = `Hydrogen ${LIB_VERSION}`;
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2174
|
+
const requestCookie = storefrontHeaders?.cookie ?? "";
|
|
2175
|
+
if (requestCookie) defaultHeaders["cookie"] = requestCookie;
|
|
2176
|
+
let uniqueToken;
|
|
2177
|
+
let visitToken;
|
|
2178
|
+
if (!/\b_shopify_(analytics|marketing)=/.test(requestCookie)) {
|
|
2179
|
+
const legacyUniqueToken = requestCookie.match(/\b_shopify_y=([^;]+)/)?.[1];
|
|
2180
|
+
const legacyVisitToken = requestCookie.match(/\b_shopify_s=([^;]+)/)?.[1];
|
|
2181
|
+
if (legacyUniqueToken) {
|
|
2182
|
+
defaultHeaders[hydrogenReact.SHOPIFY_STOREFRONT_Y_HEADER] = legacyUniqueToken;
|
|
2183
|
+
}
|
|
2184
|
+
if (legacyVisitToken) {
|
|
2185
|
+
defaultHeaders[hydrogenReact.SHOPIFY_STOREFRONT_S_HEADER] = legacyVisitToken;
|
|
2186
|
+
}
|
|
2187
|
+
uniqueToken = legacyUniqueToken ?? generateUUID();
|
|
2188
|
+
visitToken = legacyVisitToken ?? generateUUID();
|
|
2189
|
+
defaultHeaders[hydrogenReact.SHOPIFY_UNIQUE_TOKEN_HEADER] = uniqueToken;
|
|
2190
|
+
defaultHeaders[hydrogenReact.SHOPIFY_VISIT_TOKEN_HEADER] = visitToken;
|
|
2191
|
+
}
|
|
2192
|
+
let collectedSubrequestHeaders;
|
|
2055
2193
|
const cacheKeyHeader = JSON.stringify({
|
|
2056
2194
|
"content-type": defaultHeaders["content-type"],
|
|
2057
2195
|
"user-agent": defaultHeaders["user-agent"],
|
|
@@ -2118,7 +2256,13 @@ function createStorefrontClient(options) {
|
|
|
2118
2256
|
graphql: graphqlData,
|
|
2119
2257
|
purpose: storefrontHeaders?.purpose
|
|
2120
2258
|
},
|
|
2121
|
-
streamConfig
|
|
2259
|
+
streamConfig,
|
|
2260
|
+
onRawHeaders: (headers2) => {
|
|
2261
|
+
collectedSubrequestHeaders ??= {
|
|
2262
|
+
setCookie: headers2.getSetCookie(),
|
|
2263
|
+
serverTiming: headers2.get("server-timing") ?? ""
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2122
2266
|
});
|
|
2123
2267
|
const errorOptions = {
|
|
2124
2268
|
url,
|
|
@@ -2217,9 +2361,90 @@ function createStorefrontClient(options) {
|
|
|
2217
2361
|
generateCacheControlHeader,
|
|
2218
2362
|
getPublicTokenHeaders,
|
|
2219
2363
|
getPrivateTokenHeaders,
|
|
2364
|
+
getHeaders: () => ({ ...defaultHeaders }),
|
|
2220
2365
|
getShopifyDomain,
|
|
2221
2366
|
getApiUrl: getStorefrontApiUrl,
|
|
2222
|
-
i18n: i18n ?? defaultI18n
|
|
2367
|
+
i18n: i18n ?? defaultI18n,
|
|
2368
|
+
/**
|
|
2369
|
+
* Checks if the request is targeting the Storefront API endpoint.
|
|
2370
|
+
*/
|
|
2371
|
+
isStorefrontApiUrl(request) {
|
|
2372
|
+
return SFAPI_RE.test(getSafePathname(request.url ?? ""));
|
|
2373
|
+
},
|
|
2374
|
+
/**
|
|
2375
|
+
* Forwards the request to the Storefront API.
|
|
2376
|
+
*/
|
|
2377
|
+
async forward(request, options2) {
|
|
2378
|
+
const forwardedHeaders = new Headers([
|
|
2379
|
+
// Forward only a selected set of headers to the Storefront API
|
|
2380
|
+
// to avoid getting 403 errors due to unexpected headers.
|
|
2381
|
+
...extractHeaders(
|
|
2382
|
+
(key) => request.headers.get(key),
|
|
2383
|
+
[
|
|
2384
|
+
"accept",
|
|
2385
|
+
"accept-encoding",
|
|
2386
|
+
"accept-language",
|
|
2387
|
+
// Access-Control headers are used for CORS preflight requests.
|
|
2388
|
+
"access-control-request-headers",
|
|
2389
|
+
"access-control-request-method",
|
|
2390
|
+
"content-type",
|
|
2391
|
+
"content-length",
|
|
2392
|
+
"cookie",
|
|
2393
|
+
"origin",
|
|
2394
|
+
"referer",
|
|
2395
|
+
"user-agent",
|
|
2396
|
+
STOREFRONT_ACCESS_TOKEN_HEADER,
|
|
2397
|
+
hydrogenReact.SHOPIFY_UNIQUE_TOKEN_HEADER,
|
|
2398
|
+
hydrogenReact.SHOPIFY_VISIT_TOKEN_HEADER
|
|
2399
|
+
]
|
|
2400
|
+
),
|
|
2401
|
+
// Add some headers to help with geolocalization and debugging
|
|
2402
|
+
...extractHeaders(
|
|
2403
|
+
(key) => defaultHeaders[key],
|
|
2404
|
+
[
|
|
2405
|
+
SHOPIFY_CLIENT_IP_HEADER,
|
|
2406
|
+
SHOPIFY_CLIENT_IP_SIG_HEADER,
|
|
2407
|
+
hydrogenReact.SHOPIFY_STOREFRONT_ID_HEADER,
|
|
2408
|
+
STOREFRONT_REQUEST_GROUP_ID_HEADER
|
|
2409
|
+
]
|
|
2410
|
+
)
|
|
2411
|
+
]);
|
|
2412
|
+
if (storefrontHeaders?.buyerIp) {
|
|
2413
|
+
forwardedHeaders.set("x-forwarded-for", storefrontHeaders.buyerIp);
|
|
2414
|
+
}
|
|
2415
|
+
const storefrontApiVersion = options2?.storefrontApiVersion ?? getSafePathname(request.url).match(SFAPI_RE)?.[1];
|
|
2416
|
+
const sfapiResponse = await fetch(
|
|
2417
|
+
getStorefrontApiUrl({ storefrontApiVersion }),
|
|
2418
|
+
{
|
|
2419
|
+
method: request.method,
|
|
2420
|
+
body: request.body,
|
|
2421
|
+
headers: forwardedHeaders
|
|
2422
|
+
}
|
|
2423
|
+
);
|
|
2424
|
+
return new Response(sfapiResponse.body, sfapiResponse);
|
|
2425
|
+
},
|
|
2426
|
+
setCollectedSubrequestHeaders: (response) => {
|
|
2427
|
+
if (collectedSubrequestHeaders) {
|
|
2428
|
+
for (const value of collectedSubrequestHeaders.setCookie) {
|
|
2429
|
+
response.headers.append("Set-Cookie", value);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
const serverTiming = extractServerTimingHeader(
|
|
2433
|
+
collectedSubrequestHeaders?.serverTiming
|
|
2434
|
+
);
|
|
2435
|
+
const isDocumentResponse = response.headers.get("content-type")?.startsWith("text/html");
|
|
2436
|
+
const fallbackValues = isDocumentResponse ? { _y: uniqueToken, _s: visitToken } : void 0;
|
|
2437
|
+
appendServerTimingHeader(response, {
|
|
2438
|
+
...fallbackValues,
|
|
2439
|
+
...serverTiming
|
|
2440
|
+
});
|
|
2441
|
+
if (isDocumentResponse && collectedSubrequestHeaders && // _shopify_essential cookie is always set, but we need more than that
|
|
2442
|
+
collectedSubrequestHeaders.setCookie.length > 1 && serverTiming?._y && serverTiming?._s && serverTiming?._cmp) {
|
|
2443
|
+
appendServerTimingHeader(response, {
|
|
2444
|
+
[HYDROGEN_SERVER_TRACKING_KEY]: "1"
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2223
2448
|
}
|
|
2224
2449
|
};
|
|
2225
2450
|
}
|
|
@@ -3305,9 +3530,9 @@ var logSubRequestEvent = ({
|
|
|
3305
3530
|
}
|
|
3306
3531
|
});
|
|
3307
3532
|
} ;
|
|
3308
|
-
function redirect(
|
|
3533
|
+
function redirect(path, options = {}) {
|
|
3309
3534
|
const headers = options.headers ? new Headers(options.headers) : new Headers({});
|
|
3310
|
-
headers.set("location",
|
|
3535
|
+
headers.set("location", path);
|
|
3311
3536
|
return new Response(null, { status: options.status || 302, headers });
|
|
3312
3537
|
}
|
|
3313
3538
|
async function refreshToken({
|
|
@@ -3815,6 +4040,12 @@ function createCustomerAccountClient({
|
|
|
3815
4040
|
if (options?.countryCode) {
|
|
3816
4041
|
loginUrl.searchParams.append("region_country", options.countryCode);
|
|
3817
4042
|
}
|
|
4043
|
+
if (options?.acrValues) {
|
|
4044
|
+
loginUrl.searchParams.append("acr_values", options.acrValues);
|
|
4045
|
+
}
|
|
4046
|
+
if (options?.loginHint) {
|
|
4047
|
+
loginUrl.searchParams.append("login_hint", options.loginHint);
|
|
4048
|
+
}
|
|
3818
4049
|
const verifier = generateCodeVerifier();
|
|
3819
4050
|
const challenge = await generateCodeChallenge(verifier);
|
|
3820
4051
|
session.set(CUSTOMER_ACCOUNT_SESSION_KEY, {
|
|
@@ -4145,12 +4376,58 @@ function createHydrogenContext(options, additionalContext) {
|
|
|
4145
4376
|
});
|
|
4146
4377
|
return hybridProvider;
|
|
4147
4378
|
}
|
|
4148
|
-
function
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4379
|
+
function createRequestHandler({
|
|
4380
|
+
build,
|
|
4381
|
+
mode,
|
|
4382
|
+
poweredByHeader = true,
|
|
4383
|
+
getLoadContext,
|
|
4384
|
+
collectTrackingInformation = true,
|
|
4385
|
+
proxyStandardRoutes = true
|
|
4386
|
+
}) {
|
|
4387
|
+
const handleRequest = reactRouter.createRequestHandler(build, mode);
|
|
4388
|
+
const appendPoweredByHeader = poweredByHeader ? (response) => response.headers.append("powered-by", "Shopify, Hydrogen") : void 0;
|
|
4389
|
+
return async (request) => {
|
|
4390
|
+
const method = request.method;
|
|
4391
|
+
if ((method === "GET" || method === "HEAD") && request.body) {
|
|
4392
|
+
return new Response(`${method} requests cannot have a body`, {
|
|
4393
|
+
status: 400
|
|
4394
|
+
});
|
|
4395
|
+
}
|
|
4396
|
+
const url = new URL(request.url);
|
|
4397
|
+
if (url.pathname.includes("//")) {
|
|
4398
|
+
return new Response(null, {
|
|
4399
|
+
status: 301,
|
|
4400
|
+
headers: {
|
|
4401
|
+
location: url.pathname.replace(/\/+/g, "/")
|
|
4402
|
+
}
|
|
4403
|
+
});
|
|
4404
|
+
}
|
|
4405
|
+
const context = await getLoadContext?.(request);
|
|
4406
|
+
const storefront = context?.storefront || context?.get?.(storefrontContext);
|
|
4407
|
+
if (proxyStandardRoutes) {
|
|
4408
|
+
if (!storefront) {
|
|
4409
|
+
warnOnce(
|
|
4410
|
+
"[h2:createRequestHandler] Storefront instance is required to proxy standard routes."
|
|
4411
|
+
);
|
|
4412
|
+
}
|
|
4413
|
+
if (storefront?.isStorefrontApiUrl(request)) {
|
|
4414
|
+
const response2 = await storefront.forward(request);
|
|
4415
|
+
appendPoweredByHeader?.(response2);
|
|
4416
|
+
return response2;
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
const response = await handleRequest(request, context);
|
|
4420
|
+
if (storefront && proxyStandardRoutes) {
|
|
4421
|
+
if (collectTrackingInformation) {
|
|
4422
|
+
storefront.setCollectedSubrequestHeaders(response);
|
|
4423
|
+
}
|
|
4424
|
+
const fetchDest = request.headers.get("sec-fetch-dest");
|
|
4425
|
+
if (fetchDest && fetchDest === "document" || request.headers.get("accept")?.includes("text/html")) {
|
|
4426
|
+
appendServerTimingHeader(response, { [HYDROGEN_SFAPI_PROXY_KEY]: "1" });
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
appendPoweredByHeader?.(response);
|
|
4430
|
+
return response;
|
|
4154
4431
|
};
|
|
4155
4432
|
}
|
|
4156
4433
|
var NonceContext = react.createContext(void 0);
|
|
@@ -4279,12 +4556,12 @@ function LazyScript({
|
|
|
4279
4556
|
async function hydrogenRoutes(currentRoutes) {
|
|
4280
4557
|
const { getVirtualRoutesV3: getVirtualRoutesV32 } = await Promise.resolve().then(() => (init_get_virtual_routes(), get_virtual_routes_exports));
|
|
4281
4558
|
const { layout, routes: virtualRoutes } = await getVirtualRoutesV32();
|
|
4282
|
-
const childVirtualRoutes = virtualRoutes.map(({ path
|
|
4559
|
+
const childVirtualRoutes = virtualRoutes.map(({ path, file, index, id }) => {
|
|
4283
4560
|
return {
|
|
4284
4561
|
file,
|
|
4285
4562
|
id,
|
|
4286
4563
|
index,
|
|
4287
|
-
path
|
|
4564
|
+
path
|
|
4288
4565
|
};
|
|
4289
4566
|
});
|
|
4290
4567
|
const virtualLayout = {
|
|
@@ -4645,7 +4922,7 @@ function VariantSelector({
|
|
|
4645
4922
|
}
|
|
4646
4923
|
}
|
|
4647
4924
|
const variants = _variants instanceof Array ? _variants : hydrogenReact.flattenConnection(_variants);
|
|
4648
|
-
const { searchParams, path
|
|
4925
|
+
const { searchParams, path, alreadyOnProductPage } = useVariantPath(
|
|
4649
4926
|
handle,
|
|
4650
4927
|
productPath,
|
|
4651
4928
|
waitForNavigation
|
|
@@ -4701,7 +4978,7 @@ function VariantSelector({
|
|
|
4701
4978
|
value: value.name,
|
|
4702
4979
|
optionValue: value,
|
|
4703
4980
|
isAvailable: variant ? variant.availableForSale : true,
|
|
4704
|
-
to:
|
|
4981
|
+
to: path + searchString,
|
|
4705
4982
|
search: searchString,
|
|
4706
4983
|
isActive: calculatedActiveValue,
|
|
4707
4984
|
variant
|
|
@@ -4735,7 +5012,7 @@ function useVariantPath(handle, productPath, waitForNavigation) {
|
|
|
4735
5012
|
const match = /(\/[a-zA-Z]{2}-[a-zA-Z]{2}\/)/g.exec(pathname);
|
|
4736
5013
|
const isLocalePathname = match && match.length > 0;
|
|
4737
5014
|
productPath = productPath.startsWith("/") ? productPath.substring(1) : productPath;
|
|
4738
|
-
const
|
|
5015
|
+
const path = isLocalePathname ? `${match[0]}${productPath}/${handle}` : `/${productPath}/${handle}`;
|
|
4739
5016
|
const searchParams = new URLSearchParams(
|
|
4740
5017
|
// Remix doesn't update the location until pending loaders complete.
|
|
4741
5018
|
// By default we use the destination search params to make selecting a variant
|
|
@@ -4748,8 +5025,8 @@ function useVariantPath(handle, productPath, waitForNavigation) {
|
|
|
4748
5025
|
// If the current pathname matches the product page, we need to make sure
|
|
4749
5026
|
// that we append to the current search params. Otherwise all the search
|
|
4750
5027
|
// params can be generated new.
|
|
4751
|
-
alreadyOnProductPage:
|
|
4752
|
-
path
|
|
5028
|
+
alreadyOnProductPage: path === pathname,
|
|
5029
|
+
path
|
|
4753
5030
|
};
|
|
4754
5031
|
}, [pathname, search, waitForNavigation, handle, productPath, navigation]);
|
|
4755
5032
|
}
|
|
@@ -4764,10 +5041,10 @@ function hydrogenPreset() {
|
|
|
4764
5041
|
ssr: true,
|
|
4765
5042
|
future: {
|
|
4766
5043
|
v8_middleware: true,
|
|
5044
|
+
v8_splitRouteModules: true,
|
|
5045
|
+
v8_viteEnvironmentApi: false,
|
|
4767
5046
|
unstable_optimizeDeps: true,
|
|
4768
|
-
|
|
4769
|
-
unstable_subResourceIntegrity: false,
|
|
4770
|
-
unstable_viteEnvironmentApi: false
|
|
5047
|
+
unstable_subResourceIntegrity: false
|
|
4771
5048
|
}
|
|
4772
5049
|
}),
|
|
4773
5050
|
reactRouterConfigResolved: ({ reactRouterConfig }) => {
|
|
@@ -4783,7 +5060,7 @@ function hydrogenPreset() {
|
|
|
4783
5060
|
}
|
|
4784
5061
|
if (reactRouterConfig.serverBundles) {
|
|
4785
5062
|
throw new Error(
|
|
4786
|
-
"[Hydrogen Preset] serverBundles is not supported in Hydrogen 2025.7.0.\nReason: React Router plugin manifest incompatibility with Hydrogen CLI.\nAlternative: Route-level code splitting via
|
|
5063
|
+
"[Hydrogen Preset] serverBundles is not supported in Hydrogen 2025.7.0.\nReason: React Router plugin manifest incompatibility with Hydrogen CLI.\nAlternative: Route-level code splitting via v8_splitRouteModules is enabled."
|
|
4787
5064
|
);
|
|
4788
5065
|
}
|
|
4789
5066
|
if (reactRouterConfig.buildEnd) {
|
|
@@ -5298,10 +5575,10 @@ var schema = {
|
|
|
5298
5575
|
if (typeof value !== "string") {
|
|
5299
5576
|
throw new Error(ERROR_PREFIX.concat("`title` should be a string"));
|
|
5300
5577
|
}
|
|
5301
|
-
if (typeof value === "string" && value.length >
|
|
5578
|
+
if (typeof value === "string" && value.length > 70) {
|
|
5302
5579
|
throw new Error(
|
|
5303
5580
|
ERROR_PREFIX.concat(
|
|
5304
|
-
"`title` should not be longer than
|
|
5581
|
+
"`title` should not be longer than 70 characters"
|
|
5305
5582
|
)
|
|
5306
5583
|
);
|
|
5307
5584
|
}
|
|
@@ -5318,7 +5595,7 @@ var schema = {
|
|
|
5318
5595
|
if (typeof value === "string" && value.length > 155) {
|
|
5319
5596
|
throw new Error(
|
|
5320
5597
|
ERROR_PREFIX.concat(
|
|
5321
|
-
"`description` should not be longer than
|
|
5598
|
+
"`description` should not be longer than 160 characters"
|
|
5322
5599
|
)
|
|
5323
5600
|
);
|
|
5324
5601
|
}
|
|
@@ -6262,6 +6539,10 @@ Object.defineProperty(exports, "getShopifyCookies", {
|
|
|
6262
6539
|
enumerable: true,
|
|
6263
6540
|
get: function () { return hydrogenReact.getShopifyCookies; }
|
|
6264
6541
|
});
|
|
6542
|
+
Object.defineProperty(exports, "getTrackingValues", {
|
|
6543
|
+
enumerable: true,
|
|
6544
|
+
get: function () { return hydrogenReact.getTrackingValues; }
|
|
6545
|
+
});
|
|
6265
6546
|
Object.defineProperty(exports, "isOptionValueCombinationInEncodedVariant", {
|
|
6266
6547
|
enumerable: true,
|
|
6267
6548
|
get: function () { return hydrogenReact.isOptionValueCombinationInEncodedVariant; }
|
|
@@ -6339,6 +6620,7 @@ exports.createCartHandler = createCartHandler;
|
|
|
6339
6620
|
exports.createContentSecurityPolicy = createContentSecurityPolicy;
|
|
6340
6621
|
exports.createCustomerAccountClient = createCustomerAccountClient;
|
|
6341
6622
|
exports.createHydrogenContext = createHydrogenContext;
|
|
6623
|
+
exports.createRequestHandler = createRequestHandler;
|
|
6342
6624
|
exports.createStorefrontClient = createStorefrontClient;
|
|
6343
6625
|
exports.createWithCache = createWithCache;
|
|
6344
6626
|
exports.formatAPIResult = formatAPIResult;
|