@shopify/hydrogen 2025.1.3 → 2025.1.5
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/development/index.cjs +509 -65
- package/dist/development/index.cjs.map +1 -1
- package/dist/development/index.js +508 -69
- package/dist/development/index.js.map +1 -1
- package/dist/production/index.cjs +131 -62
- package/dist/production/index.cjs.map +1 -1
- package/dist/production/index.d.cts +288 -13
- package/dist/production/index.d.ts +288 -13
- package/dist/production/index.js +135 -66
- package/dist/production/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -5,6 +5,7 @@ var react$1 = require('@remix-run/react');
|
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
var hydrogenReact = require('@shopify/hydrogen-react');
|
|
7
7
|
var cookie = require('worktop/cookie');
|
|
8
|
+
var serverRuntime = require('@remix-run/server-runtime');
|
|
8
9
|
var cspBuilder = require('content-security-policy-builder');
|
|
9
10
|
require('url');
|
|
10
11
|
require('path');
|
|
@@ -170,7 +171,62 @@ var AnalyticsEvent = {
|
|
|
170
171
|
// Custom
|
|
171
172
|
CUSTOM_EVENT: `custom_`
|
|
172
173
|
};
|
|
173
|
-
|
|
174
|
+
|
|
175
|
+
// src/constants.ts
|
|
176
|
+
var STOREFRONT_REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
|
|
177
|
+
var STOREFRONT_ACCESS_TOKEN_HEADER = "X-Shopify-Storefront-Access-Token";
|
|
178
|
+
var SDK_VARIANT_HEADER = "X-SDK-Variant";
|
|
179
|
+
var SDK_VARIANT_SOURCE_HEADER = "X-SDK-Variant-Source";
|
|
180
|
+
var SDK_VERSION_HEADER = "X-SDK-Version";
|
|
181
|
+
var SHOPIFY_CLIENT_IP_HEADER = "X-Shopify-Client-IP";
|
|
182
|
+
var SHOPIFY_CLIENT_IP_SIG_HEADER = "X-Shopify-Client-IP-Sig";
|
|
183
|
+
var HYDROGEN_SFAPI_PROXY_KEY = "_sfapi_proxy";
|
|
184
|
+
var HYDROGEN_SERVER_TRACKING_KEY = "_server_tracking";
|
|
185
|
+
|
|
186
|
+
// src/utils/server-timing.ts
|
|
187
|
+
function buildServerTimingHeader(values) {
|
|
188
|
+
return Object.entries(values).map(([key, value]) => value ? `${key};desc=${value}` : void 0).filter(Boolean).join(", ");
|
|
189
|
+
}
|
|
190
|
+
function appendServerTimingHeader(response, values) {
|
|
191
|
+
const header = typeof values === "string" ? values : buildServerTimingHeader(values);
|
|
192
|
+
if (header) {
|
|
193
|
+
response.headers.append("Server-Timing", header);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
var trackedTimings = ["_y", "_s", "_cmp"];
|
|
197
|
+
function extractServerTimingHeader(serverTimingHeader) {
|
|
198
|
+
const values = {};
|
|
199
|
+
if (!serverTimingHeader) return values;
|
|
200
|
+
const re = new RegExp(
|
|
201
|
+
`\\b(${trackedTimings.join("|")});desc="?([^",]+)"?`,
|
|
202
|
+
"g"
|
|
203
|
+
);
|
|
204
|
+
let match;
|
|
205
|
+
while ((match = re.exec(serverTimingHeader)) !== null) {
|
|
206
|
+
values[match[1]] = match[2];
|
|
207
|
+
}
|
|
208
|
+
return values;
|
|
209
|
+
}
|
|
210
|
+
function hasServerTimingInNavigationEntry(key) {
|
|
211
|
+
if (typeof window === "undefined") return false;
|
|
212
|
+
try {
|
|
213
|
+
const navigationEntry = window.performance.getEntriesByType(
|
|
214
|
+
"navigation"
|
|
215
|
+
)[0];
|
|
216
|
+
return !!navigationEntry?.serverTiming?.some((entry) => entry.name === key);
|
|
217
|
+
} catch (e) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function isSfapiProxyEnabled() {
|
|
222
|
+
return hasServerTimingInNavigationEntry(HYDROGEN_SFAPI_PROXY_KEY);
|
|
223
|
+
}
|
|
224
|
+
function hasServerReturnedTrackingValues() {
|
|
225
|
+
return hasServerTimingInNavigationEntry(HYDROGEN_SERVER_TRACKING_KEY);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/customer-privacy/ShopifyCustomerPrivacy.tsx
|
|
229
|
+
var CONSENT_API = "https://cdn.shopify.com/shopifycloud/consent-tracking-api/v0.2/consent-tracking-api.js";
|
|
174
230
|
var CONSENT_API_WITH_BANNER = "https://cdn.shopify.com/shopifycloud/privacy-banner/storefront-banner.js";
|
|
175
231
|
function logMissingConfig(fieldName) {
|
|
176
232
|
console.error(
|
|
@@ -182,19 +238,34 @@ function useCustomerPrivacy(props) {
|
|
|
182
238
|
withPrivacyBanner = false,
|
|
183
239
|
onVisitorConsentCollected,
|
|
184
240
|
onReady,
|
|
185
|
-
|
|
241
|
+
checkoutDomain,
|
|
242
|
+
storefrontAccessToken,
|
|
243
|
+
country,
|
|
244
|
+
locale,
|
|
245
|
+
sameDomainForStorefrontApi
|
|
186
246
|
} = props;
|
|
247
|
+
const hasSfapiProxy = react.useMemo(
|
|
248
|
+
() => sameDomainForStorefrontApi ?? isSfapiProxyEnabled(),
|
|
249
|
+
[sameDomainForStorefrontApi]
|
|
250
|
+
);
|
|
251
|
+
const fetchTrackingValuesFromBrowser = react.useMemo(
|
|
252
|
+
() => hasSfapiProxy && !hasServerReturnedTrackingValues(),
|
|
253
|
+
[hasSfapiProxy]
|
|
254
|
+
);
|
|
255
|
+
const cookiesReady = hydrogenReact.useShopifyCookies({
|
|
256
|
+
fetchTrackingValues: fetchTrackingValuesFromBrowser,
|
|
257
|
+
storefrontAccessToken,
|
|
258
|
+
ignoreDeprecatedCookies: true
|
|
259
|
+
});
|
|
260
|
+
const initialTrackingValues = react.useMemo(hydrogenReact.getTrackingValues, [cookiesReady]);
|
|
261
|
+
const { revalidate } = react$1.useRevalidator();
|
|
187
262
|
hydrogenReact.useLoadScript(withPrivacyBanner ? CONSENT_API_WITH_BANNER : CONSENT_API, {
|
|
188
263
|
attributes: {
|
|
189
264
|
id: "customer-privacy-api"
|
|
190
265
|
}
|
|
191
266
|
});
|
|
192
|
-
const { observing, setLoaded } = useApisLoaded({
|
|
193
|
-
withPrivacyBanner,
|
|
194
|
-
onLoaded: onReady
|
|
195
|
-
});
|
|
267
|
+
const { observing, setLoaded, apisLoaded } = useApisLoaded({ withPrivacyBanner });
|
|
196
268
|
const config = react.useMemo(() => {
|
|
197
|
-
const { checkoutDomain, storefrontAccessToken } = consentConfig;
|
|
198
269
|
if (!checkoutDomain) logMissingConfig("checkoutDomain");
|
|
199
270
|
if (!storefrontAccessToken) logMissingConfig("storefrontAccessToken");
|
|
200
271
|
if (storefrontAccessToken.startsWith("shpat_") || storefrontAccessToken.length !== 32) {
|
|
@@ -202,18 +273,50 @@ function useCustomerPrivacy(props) {
|
|
|
202
273
|
`[h2:error:useCustomerPrivacy] It looks like you passed a private access token, make sure to use the public token`
|
|
203
274
|
);
|
|
204
275
|
}
|
|
276
|
+
const commonAncestorDomain = parseStoreDomain(checkoutDomain);
|
|
277
|
+
const sfapiDomain = (
|
|
278
|
+
// Check if standard route proxy is enabled in Hydrogen server
|
|
279
|
+
// to use it instead of doing a cross-origin request to checkout.
|
|
280
|
+
hasSfapiProxy && typeof window !== "undefined" ? window.location.host : checkoutDomain
|
|
281
|
+
);
|
|
205
282
|
const config2 = {
|
|
206
|
-
|
|
283
|
+
// This domain is used to send requests to SFAPI for setting and getting consent.
|
|
284
|
+
checkoutRootDomain: sfapiDomain,
|
|
285
|
+
// Prefix with a dot to ensure this domain is different from checkoutRootDomain.
|
|
286
|
+
// This will ensure old cookies are set for a cross-subdomain checkout setup
|
|
287
|
+
// so that we keep backward compatibility until new cookies are rolled out.
|
|
288
|
+
// Once consent-tracking-api is updated to not rely on cookies anymore, we can remove this.
|
|
289
|
+
storefrontRootDomain: commonAncestorDomain ? "." + commonAncestorDomain : void 0,
|
|
207
290
|
storefrontAccessToken,
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
locale: consentConfig.locale
|
|
291
|
+
country,
|
|
292
|
+
locale
|
|
211
293
|
};
|
|
212
294
|
return config2;
|
|
213
|
-
}, [
|
|
295
|
+
}, [
|
|
296
|
+
logMissingConfig,
|
|
297
|
+
checkoutDomain,
|
|
298
|
+
storefrontAccessToken,
|
|
299
|
+
country,
|
|
300
|
+
locale
|
|
301
|
+
]);
|
|
214
302
|
react.useEffect(() => {
|
|
215
303
|
const consentCollectedHandler = (event) => {
|
|
304
|
+
const latestTrackingValues = hydrogenReact.getTrackingValues();
|
|
305
|
+
if (initialTrackingValues.visitToken !== latestTrackingValues.visitToken || initialTrackingValues.uniqueToken !== latestTrackingValues.uniqueToken) {
|
|
306
|
+
revalidate();
|
|
307
|
+
}
|
|
216
308
|
if (onVisitorConsentCollected) {
|
|
309
|
+
const customerPrivacy = getCustomerPrivacy();
|
|
310
|
+
if (customerPrivacy?.shouldShowBanner()) {
|
|
311
|
+
const consentValues = customerPrivacy.currentVisitorConsent();
|
|
312
|
+
if (consentValues) {
|
|
313
|
+
const NO_VALUE = "";
|
|
314
|
+
const noInteraction = consentValues.marketing === NO_VALUE && consentValues.analytics === NO_VALUE && consentValues.preferences === NO_VALUE;
|
|
315
|
+
if (noInteraction) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
217
320
|
onVisitorConsentCollected(event.detail);
|
|
218
321
|
}
|
|
219
322
|
};
|
|
@@ -239,14 +342,11 @@ function useCustomerPrivacy(props) {
|
|
|
239
342
|
},
|
|
240
343
|
set(value) {
|
|
241
344
|
if (typeof value === "object" && value !== null && "showPreferences" in value && "loadBanner" in value) {
|
|
242
|
-
const privacyBanner = value;
|
|
243
|
-
privacyBanner.loadBanner(config);
|
|
244
345
|
customPrivacyBanner = overridePrivacyBannerMethods({
|
|
245
|
-
privacyBanner,
|
|
346
|
+
privacyBanner: value,
|
|
246
347
|
config
|
|
247
348
|
});
|
|
248
349
|
setLoaded.privacyBanner();
|
|
249
|
-
emitCustomerPrivacyApiLoaded();
|
|
250
350
|
}
|
|
251
351
|
}
|
|
252
352
|
};
|
|
@@ -280,6 +380,8 @@ function useCustomerPrivacy(props) {
|
|
|
280
380
|
const customerPrivacy = value2;
|
|
281
381
|
customCustomerPrivacy = {
|
|
282
382
|
...customerPrivacy,
|
|
383
|
+
// Note: this method is not used by the privacy-banner,
|
|
384
|
+
// it bundles its own setTrackingConsent.
|
|
283
385
|
setTrackingConsent: overrideCustomerPrivacySetTrackingConsent(
|
|
284
386
|
{ customerPrivacy, config }
|
|
285
387
|
)
|
|
@@ -289,7 +391,6 @@ function useCustomerPrivacy(props) {
|
|
|
289
391
|
customerPrivacy: customCustomerPrivacy
|
|
290
392
|
};
|
|
291
393
|
setLoaded.customerPrivacy();
|
|
292
|
-
emitCustomerPrivacyApiLoaded();
|
|
293
394
|
}
|
|
294
395
|
}
|
|
295
396
|
});
|
|
@@ -301,6 +402,24 @@ function useCustomerPrivacy(props) {
|
|
|
301
402
|
overrideCustomerPrivacySetTrackingConsent,
|
|
302
403
|
setLoaded.customerPrivacy
|
|
303
404
|
]);
|
|
405
|
+
react.useEffect(() => {
|
|
406
|
+
if (!apisLoaded || !cookiesReady) return;
|
|
407
|
+
const customerPrivacy = getCustomerPrivacy();
|
|
408
|
+
if (customerPrivacy && !customerPrivacy.cachedConsent) {
|
|
409
|
+
const trackingValues = hydrogenReact.getTrackingValues();
|
|
410
|
+
if (trackingValues.consent) {
|
|
411
|
+
customerPrivacy.cachedConsent = trackingValues.consent;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (withPrivacyBanner) {
|
|
415
|
+
const privacyBanner = getPrivacyBanner();
|
|
416
|
+
if (privacyBanner) {
|
|
417
|
+
privacyBanner.loadBanner(config);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
emitCustomerPrivacyApiLoaded();
|
|
421
|
+
onReady?.();
|
|
422
|
+
}, [apisLoaded, cookiesReady]);
|
|
304
423
|
const result = {
|
|
305
424
|
customerPrivacy: getCustomerPrivacy()
|
|
306
425
|
};
|
|
@@ -316,15 +435,12 @@ function emitCustomerPrivacyApiLoaded() {
|
|
|
316
435
|
const event = new CustomEvent("shopifyCustomerPrivacyApiLoaded");
|
|
317
436
|
document.dispatchEvent(event);
|
|
318
437
|
}
|
|
319
|
-
function useApisLoaded({
|
|
320
|
-
withPrivacyBanner,
|
|
321
|
-
onLoaded
|
|
322
|
-
}) {
|
|
438
|
+
function useApisLoaded({ withPrivacyBanner }) {
|
|
323
439
|
const observing = react.useRef({ customerPrivacy: false, privacyBanner: false });
|
|
324
|
-
const [
|
|
440
|
+
const [apisLoadedArray, setApisLoaded] = react.useState(
|
|
325
441
|
withPrivacyBanner ? [false, false] : [false]
|
|
326
442
|
);
|
|
327
|
-
const
|
|
443
|
+
const apisLoaded = apisLoadedArray.every(Boolean);
|
|
328
444
|
const setLoaded = {
|
|
329
445
|
customerPrivacy: () => {
|
|
330
446
|
if (withPrivacyBanner) {
|
|
@@ -340,16 +456,11 @@ function useApisLoaded({
|
|
|
340
456
|
setApisLoaded((prev) => [prev[0], true]);
|
|
341
457
|
}
|
|
342
458
|
};
|
|
343
|
-
|
|
344
|
-
if (loaded && onLoaded) {
|
|
345
|
-
onLoaded();
|
|
346
|
-
}
|
|
347
|
-
}, [loaded, onLoaded]);
|
|
348
|
-
return { observing, setLoaded };
|
|
459
|
+
return { observing, setLoaded, apisLoaded };
|
|
349
460
|
}
|
|
350
461
|
function parseStoreDomain(checkoutDomain) {
|
|
351
462
|
if (typeof window === "undefined") return;
|
|
352
|
-
const host = window.
|
|
463
|
+
const host = window.location.host;
|
|
353
464
|
const checkoutDomainParts = checkoutDomain.split(".").reverse();
|
|
354
465
|
const currentDomainParts = host.split(".").reverse();
|
|
355
466
|
const sameDomainParts = [];
|
|
@@ -358,7 +469,7 @@ function parseStoreDomain(checkoutDomain) {
|
|
|
358
469
|
sameDomainParts.push(part);
|
|
359
470
|
}
|
|
360
471
|
});
|
|
361
|
-
return sameDomainParts.reverse().join(".");
|
|
472
|
+
return sameDomainParts.reverse().join(".") || void 0;
|
|
362
473
|
}
|
|
363
474
|
function overrideCustomerPrivacySetTrackingConsent({
|
|
364
475
|
customerPrivacy,
|
|
@@ -416,7 +527,7 @@ function getPrivacyBanner() {
|
|
|
416
527
|
}
|
|
417
528
|
|
|
418
529
|
// package.json
|
|
419
|
-
var version = "2025.1.
|
|
530
|
+
var version = "2025.1.5";
|
|
420
531
|
|
|
421
532
|
// src/analytics-manager/ShopifyAnalytics.tsx
|
|
422
533
|
function getCustomerPrivacyRequired() {
|
|
@@ -436,6 +547,7 @@ function ShopifyAnalytics({
|
|
|
436
547
|
const { subscribe: subscribe2, register: register2, canTrack } = useAnalytics();
|
|
437
548
|
const [shopifyReady, setShopifyReady] = react.useState(false);
|
|
438
549
|
const [privacyReady, setPrivacyReady] = react.useState(false);
|
|
550
|
+
const [collectedConsent, setCollectedConsent] = react.useState("");
|
|
439
551
|
const init = react.useRef(false);
|
|
440
552
|
const { checkoutDomain, storefrontAccessToken, language } = consent;
|
|
441
553
|
const { ready: shopifyAnalyticsReady } = register2("Internal_Shopify_Analytics");
|
|
@@ -444,14 +556,31 @@ function ShopifyAnalytics({
|
|
|
444
556
|
locale: language,
|
|
445
557
|
checkoutDomain: !checkoutDomain ? "mock.shop" : checkoutDomain,
|
|
446
558
|
storefrontAccessToken: !storefrontAccessToken ? "abcdefghijklmnopqrstuvwxyz123456" : storefrontAccessToken,
|
|
447
|
-
|
|
448
|
-
|
|
559
|
+
// If we use privacy banner, we should wait until consent is collected.
|
|
560
|
+
// Otherwise, we can consider privacy ready immediately:
|
|
561
|
+
onReady: () => !consent.withPrivacyBanner && setPrivacyReady(true),
|
|
562
|
+
onVisitorConsentCollected: (consent2) => {
|
|
563
|
+
try {
|
|
564
|
+
setCollectedConsent(JSON.stringify(consent2));
|
|
565
|
+
} catch (e) {
|
|
566
|
+
}
|
|
567
|
+
setPrivacyReady(true);
|
|
568
|
+
}
|
|
449
569
|
});
|
|
570
|
+
const hasUserConsent = react.useMemo(
|
|
571
|
+
// must be initialized with true to avoid removing cookies too early
|
|
572
|
+
() => privacyReady ? canTrack() : true,
|
|
573
|
+
// Make this value depend on collectedConsent to re-run `canTrack()` when consent changes
|
|
574
|
+
[privacyReady, canTrack, collectedConsent]
|
|
575
|
+
);
|
|
450
576
|
hydrogenReact.useShopifyCookies({
|
|
451
|
-
hasUserConsent
|
|
452
|
-
// must be initialized with true
|
|
577
|
+
hasUserConsent,
|
|
453
578
|
domain,
|
|
454
|
-
checkoutDomain
|
|
579
|
+
checkoutDomain,
|
|
580
|
+
// Already done inside useCustomerPrivacy
|
|
581
|
+
fetchTrackingValues: false,
|
|
582
|
+
// Avoid creating local cookies too early
|
|
583
|
+
ignoreDeprecatedCookies: !privacyReady
|
|
455
584
|
});
|
|
456
585
|
react.useEffect(() => {
|
|
457
586
|
if (init.current) return;
|
|
@@ -501,11 +630,11 @@ function prepareBasePageViewPayload(payload) {
|
|
|
501
630
|
...payload.shop,
|
|
502
631
|
hasUserConsent,
|
|
503
632
|
...hydrogenReact.getClientBrowserParameters(),
|
|
504
|
-
ccpaEnforced: !customerPrivacy.saleOfDataAllowed(),
|
|
505
|
-
gdprEnforced: !(customerPrivacy.marketingAllowed() && customerPrivacy.analyticsProcessingAllowed()),
|
|
506
633
|
analyticsAllowed: customerPrivacy.analyticsProcessingAllowed(),
|
|
507
634
|
marketingAllowed: customerPrivacy.marketingAllowed(),
|
|
508
|
-
saleOfDataAllowed: customerPrivacy.saleOfDataAllowed()
|
|
635
|
+
saleOfDataAllowed: customerPrivacy.saleOfDataAllowed(),
|
|
636
|
+
ccpaEnforced: !customerPrivacy.saleOfDataAllowed(),
|
|
637
|
+
gdprEnforced: !(customerPrivacy.marketingAllowed() && customerPrivacy.analyticsProcessingAllowed())
|
|
509
638
|
};
|
|
510
639
|
return eventPayload;
|
|
511
640
|
}
|
|
@@ -938,11 +1067,11 @@ function AnalyticsProvider({
|
|
|
938
1067
|
shop: shopProp = null,
|
|
939
1068
|
cookieDomain
|
|
940
1069
|
}) {
|
|
941
|
-
const listenerSet = react.useRef(false);
|
|
942
1070
|
const { shop } = useShopAnalytics(shopProp);
|
|
943
1071
|
const [analyticsLoaded, setAnalyticsLoaded] = react.useState(
|
|
944
1072
|
customCanTrack ? true : false
|
|
945
1073
|
);
|
|
1074
|
+
const [consentCollected, setConsentCollected] = react.useState(false);
|
|
946
1075
|
const [carts, setCarts] = react.useState({ cart: null, prevCart: null });
|
|
947
1076
|
const [canTrack, setCanTrack] = react.useState(
|
|
948
1077
|
customCanTrack ? () => customCanTrack : () => shopifyCanTrack
|
|
@@ -1010,21 +1139,21 @@ function AnalyticsProvider({
|
|
|
1010
1139
|
children,
|
|
1011
1140
|
!!shop && /* @__PURE__ */ jsxRuntime.jsx(AnalyticsPageView, {}),
|
|
1012
1141
|
!!shop && !!currentCart && /* @__PURE__ */ jsxRuntime.jsx(CartAnalytics, { cart: currentCart, setCarts }),
|
|
1013
|
-
!!shop &&
|
|
1142
|
+
!!shop && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1014
1143
|
ShopifyAnalytics,
|
|
1015
1144
|
{
|
|
1016
1145
|
consent,
|
|
1017
1146
|
onReady: () => {
|
|
1018
|
-
listenerSet.current = true;
|
|
1019
1147
|
setAnalyticsLoaded(true);
|
|
1020
1148
|
setCanTrack(
|
|
1021
1149
|
customCanTrack ? () => customCanTrack : () => shopifyCanTrack
|
|
1022
1150
|
);
|
|
1151
|
+
setConsentCollected(true);
|
|
1023
1152
|
},
|
|
1024
1153
|
domain: cookieDomain
|
|
1025
1154
|
}
|
|
1026
1155
|
),
|
|
1027
|
-
!!shop && /* @__PURE__ */ jsxRuntime.jsx(PerfKit, { shop })
|
|
1156
|
+
!!shop && consentCollected && /* @__PURE__ */ jsxRuntime.jsx(PerfKit, { shop })
|
|
1028
1157
|
] });
|
|
1029
1158
|
}
|
|
1030
1159
|
function useAnalytics() {
|
|
@@ -1103,6 +1232,21 @@ function getDebugHeaders(request) {
|
|
|
1103
1232
|
purpose: request ? getHeader(request, "purpose") : void 0
|
|
1104
1233
|
};
|
|
1105
1234
|
}
|
|
1235
|
+
var SFAPI_RE = /^\/api\/(unstable|2\d{3}-\d{2})\/graphql\.json$/;
|
|
1236
|
+
var getSafePathname = (url) => {
|
|
1237
|
+
try {
|
|
1238
|
+
return new URL(url, "http://e.c").pathname;
|
|
1239
|
+
} catch {
|
|
1240
|
+
return "/";
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
function extractHeaders(extract, keys) {
|
|
1244
|
+
return keys.reduce((acc, key) => {
|
|
1245
|
+
const forwardedValue = extract(key);
|
|
1246
|
+
if (forwardedValue) acc.push([key, forwardedValue]);
|
|
1247
|
+
return acc;
|
|
1248
|
+
}, []);
|
|
1249
|
+
}
|
|
1106
1250
|
|
|
1107
1251
|
// src/utils/callsites.ts
|
|
1108
1252
|
function withSyncStack(promise, options = {}) {
|
|
@@ -1472,13 +1616,16 @@ async function runWithCache(cacheKey, actionFn, {
|
|
|
1472
1616
|
}
|
|
1473
1617
|
|
|
1474
1618
|
// src/cache/server-fetch.ts
|
|
1619
|
+
var excludedHeaders = ["set-cookie", "server-timing"];
|
|
1475
1620
|
function toSerializableResponse(body, response) {
|
|
1476
1621
|
return [
|
|
1477
1622
|
body,
|
|
1478
1623
|
{
|
|
1479
1624
|
status: response.status,
|
|
1480
1625
|
statusText: response.statusText,
|
|
1481
|
-
headers:
|
|
1626
|
+
headers: [...response.headers].filter(
|
|
1627
|
+
([key]) => !excludedHeaders.includes(key.toLowerCase())
|
|
1628
|
+
)
|
|
1482
1629
|
}
|
|
1483
1630
|
];
|
|
1484
1631
|
}
|
|
@@ -1491,7 +1638,8 @@ async function fetchWithServerCache(url, requestInit, {
|
|
|
1491
1638
|
cacheKey = [url, requestInit],
|
|
1492
1639
|
shouldCacheResponse,
|
|
1493
1640
|
waitUntil,
|
|
1494
|
-
debugInfo
|
|
1641
|
+
debugInfo,
|
|
1642
|
+
onRawHeaders
|
|
1495
1643
|
}) {
|
|
1496
1644
|
if (!cacheOptions && (!requestInit.method || requestInit.method === "GET")) {
|
|
1497
1645
|
cacheOptions = CacheShort();
|
|
@@ -1500,6 +1648,7 @@ async function fetchWithServerCache(url, requestInit, {
|
|
|
1500
1648
|
cacheKey,
|
|
1501
1649
|
async () => {
|
|
1502
1650
|
const response = await fetch(url, requestInit);
|
|
1651
|
+
onRawHeaders?.(response.headers);
|
|
1503
1652
|
if (!response.ok) {
|
|
1504
1653
|
return response;
|
|
1505
1654
|
}
|
|
@@ -1674,7 +1823,10 @@ CartForm.ACTIONS = {
|
|
|
1674
1823
|
NoteUpdate: "NoteUpdate",
|
|
1675
1824
|
SelectedDeliveryOptionsUpdate: "SelectedDeliveryOptionsUpdate",
|
|
1676
1825
|
MetafieldsSet: "MetafieldsSet",
|
|
1677
|
-
MetafieldDelete: "MetafieldDelete"
|
|
1826
|
+
MetafieldDelete: "MetafieldDelete",
|
|
1827
|
+
DeliveryAddressesAdd: "DeliveryAddressesAdd",
|
|
1828
|
+
DeliveryAddressesUpdate: "DeliveryAddressesUpdate",
|
|
1829
|
+
DeliveryAddressesRemove: "DeliveryAddressesRemove"
|
|
1678
1830
|
};
|
|
1679
1831
|
function getFormInput(formData) {
|
|
1680
1832
|
const data = {};
|
|
@@ -1682,6 +1834,11 @@ function getFormInput(formData) {
|
|
|
1682
1834
|
const key = pair[0];
|
|
1683
1835
|
const values = formData.getAll(key);
|
|
1684
1836
|
data[key] = values.length > 1 ? values : pair[1];
|
|
1837
|
+
if (data[key] === "on") {
|
|
1838
|
+
data[key] = true;
|
|
1839
|
+
} else if (data[key] === "off") {
|
|
1840
|
+
data[key] = false;
|
|
1841
|
+
}
|
|
1685
1842
|
}
|
|
1686
1843
|
const { cartFormInput, ...otherData } = data;
|
|
1687
1844
|
const { action, inputs } = cartFormInput ? JSON.parse(String(cartFormInput)) : {};
|
|
@@ -1714,13 +1871,6 @@ var cartSetIdDefault = (cookieOptions) => {
|
|
|
1714
1871
|
};
|
|
1715
1872
|
};
|
|
1716
1873
|
|
|
1717
|
-
// src/constants.ts
|
|
1718
|
-
var STOREFRONT_REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
|
|
1719
|
-
var STOREFRONT_ACCESS_TOKEN_HEADER = "X-Shopify-Storefront-Access-Token";
|
|
1720
|
-
var SDK_VARIANT_HEADER = "X-SDK-Variant";
|
|
1721
|
-
var SDK_VARIANT_SOURCE_HEADER = "X-SDK-Variant-Source";
|
|
1722
|
-
var SDK_VERSION_HEADER = "X-SDK-Version";
|
|
1723
|
-
|
|
1724
1874
|
// src/utils/uuid.ts
|
|
1725
1875
|
function generateUUID() {
|
|
1726
1876
|
if (typeof crypto !== "undefined" && !!crypto.randomUUID) {
|
|
@@ -1731,7 +1881,7 @@ function generateUUID() {
|
|
|
1731
1881
|
}
|
|
1732
1882
|
|
|
1733
1883
|
// src/version.ts
|
|
1734
|
-
var LIB_VERSION = "2025.1.
|
|
1884
|
+
var LIB_VERSION = "2025.1.5";
|
|
1735
1885
|
|
|
1736
1886
|
// src/utils/graphql.ts
|
|
1737
1887
|
function minifyQuery(string) {
|
|
@@ -1892,16 +2042,34 @@ function createStorefrontClient(options) {
|
|
|
1892
2042
|
contentType: "json",
|
|
1893
2043
|
buyerIp: storefrontHeaders?.buyerIp || ""
|
|
1894
2044
|
});
|
|
2045
|
+
if (storefrontHeaders?.buyerIp) {
|
|
2046
|
+
defaultHeaders[SHOPIFY_CLIENT_IP_HEADER] = storefrontHeaders.buyerIp;
|
|
2047
|
+
}
|
|
2048
|
+
if (storefrontHeaders?.buyerIpSig) {
|
|
2049
|
+
defaultHeaders[SHOPIFY_CLIENT_IP_SIG_HEADER] = storefrontHeaders.buyerIpSig;
|
|
2050
|
+
}
|
|
1895
2051
|
defaultHeaders[STOREFRONT_REQUEST_GROUP_ID_HEADER] = storefrontHeaders?.requestGroupId || generateUUID();
|
|
1896
2052
|
if (storefrontId) defaultHeaders[hydrogenReact.SHOPIFY_STOREFRONT_ID_HEADER] = storefrontId;
|
|
1897
2053
|
defaultHeaders["user-agent"] = `Hydrogen ${LIB_VERSION}`;
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
2054
|
+
const requestCookie = storefrontHeaders?.cookie ?? "";
|
|
2055
|
+
if (requestCookie) defaultHeaders["cookie"] = requestCookie;
|
|
2056
|
+
let uniqueToken;
|
|
2057
|
+
let visitToken;
|
|
2058
|
+
if (!/\b_shopify_(analytics|marketing)=/.test(requestCookie)) {
|
|
2059
|
+
const legacyUniqueToken = requestCookie.match(/\b_shopify_y=([^;]+)/)?.[1];
|
|
2060
|
+
const legacyVisitToken = requestCookie.match(/\b_shopify_s=([^;]+)/)?.[1];
|
|
2061
|
+
if (legacyUniqueToken) {
|
|
2062
|
+
defaultHeaders[hydrogenReact.SHOPIFY_STOREFRONT_Y_HEADER] = legacyUniqueToken;
|
|
2063
|
+
}
|
|
2064
|
+
if (legacyVisitToken) {
|
|
2065
|
+
defaultHeaders[hydrogenReact.SHOPIFY_STOREFRONT_S_HEADER] = legacyVisitToken;
|
|
2066
|
+
}
|
|
2067
|
+
uniqueToken = legacyUniqueToken ?? generateUUID();
|
|
2068
|
+
visitToken = legacyVisitToken ?? generateUUID();
|
|
2069
|
+
defaultHeaders[hydrogenReact.SHOPIFY_UNIQUE_TOKEN_HEADER] = uniqueToken;
|
|
2070
|
+
defaultHeaders[hydrogenReact.SHOPIFY_VISIT_TOKEN_HEADER] = visitToken;
|
|
1904
2071
|
}
|
|
2072
|
+
let collectedSubrequestHeaders;
|
|
1905
2073
|
const cacheKeyHeader = JSON.stringify({
|
|
1906
2074
|
"content-type": defaultHeaders["content-type"],
|
|
1907
2075
|
"user-agent": defaultHeaders["user-agent"],
|
|
@@ -1963,6 +2131,13 @@ function createStorefrontClient(options) {
|
|
|
1963
2131
|
stackInfo,
|
|
1964
2132
|
graphql: graphqlData,
|
|
1965
2133
|
purpose: storefrontHeaders?.purpose
|
|
2134
|
+
},
|
|
2135
|
+
onRawHeaders: (headers2) => {
|
|
2136
|
+
collectedSubrequestHeaders ??= {
|
|
2137
|
+
// `getSetCookie` may not be available in all environments (e.g., classic Remix compiler)
|
|
2138
|
+
setCookie: typeof headers2.getSetCookie === "function" ? headers2.getSetCookie() : [],
|
|
2139
|
+
serverTiming: headers2.get("server-timing") ?? ""
|
|
2140
|
+
};
|
|
1966
2141
|
}
|
|
1967
2142
|
});
|
|
1968
2143
|
const errorOptions = {
|
|
@@ -2061,9 +2236,90 @@ function createStorefrontClient(options) {
|
|
|
2061
2236
|
generateCacheControlHeader,
|
|
2062
2237
|
getPublicTokenHeaders,
|
|
2063
2238
|
getPrivateTokenHeaders,
|
|
2239
|
+
getHeaders: () => ({ ...defaultHeaders }),
|
|
2064
2240
|
getShopifyDomain,
|
|
2065
2241
|
getApiUrl: getStorefrontApiUrl,
|
|
2066
|
-
i18n: i18n ?? defaultI18n
|
|
2242
|
+
i18n: i18n ?? defaultI18n,
|
|
2243
|
+
/**
|
|
2244
|
+
* Checks if the request is targeting the Storefront API endpoint.
|
|
2245
|
+
*/
|
|
2246
|
+
isStorefrontApiUrl(request) {
|
|
2247
|
+
return SFAPI_RE.test(getSafePathname(request.url ?? ""));
|
|
2248
|
+
},
|
|
2249
|
+
/**
|
|
2250
|
+
* Forwards the request to the Storefront API.
|
|
2251
|
+
*/
|
|
2252
|
+
async forward(request, options2) {
|
|
2253
|
+
const forwardedHeaders = new Headers([
|
|
2254
|
+
// Forward only a selected set of headers to the Storefront API
|
|
2255
|
+
// to avoid getting 403 errors due to unexpected headers.
|
|
2256
|
+
...extractHeaders(
|
|
2257
|
+
(key) => request.headers.get(key),
|
|
2258
|
+
[
|
|
2259
|
+
"accept",
|
|
2260
|
+
"accept-encoding",
|
|
2261
|
+
"accept-language",
|
|
2262
|
+
// Access-Control headers are used for CORS preflight requests.
|
|
2263
|
+
"access-control-request-headers",
|
|
2264
|
+
"access-control-request-method",
|
|
2265
|
+
"content-type",
|
|
2266
|
+
"content-length",
|
|
2267
|
+
"cookie",
|
|
2268
|
+
"origin",
|
|
2269
|
+
"referer",
|
|
2270
|
+
"user-agent",
|
|
2271
|
+
STOREFRONT_ACCESS_TOKEN_HEADER,
|
|
2272
|
+
hydrogenReact.SHOPIFY_UNIQUE_TOKEN_HEADER,
|
|
2273
|
+
hydrogenReact.SHOPIFY_VISIT_TOKEN_HEADER
|
|
2274
|
+
]
|
|
2275
|
+
),
|
|
2276
|
+
// Add some headers to help with geolocalization and debugging
|
|
2277
|
+
...extractHeaders(
|
|
2278
|
+
(key) => defaultHeaders[key],
|
|
2279
|
+
[
|
|
2280
|
+
SHOPIFY_CLIENT_IP_HEADER,
|
|
2281
|
+
SHOPIFY_CLIENT_IP_SIG_HEADER,
|
|
2282
|
+
hydrogenReact.SHOPIFY_STOREFRONT_ID_HEADER,
|
|
2283
|
+
STOREFRONT_REQUEST_GROUP_ID_HEADER
|
|
2284
|
+
]
|
|
2285
|
+
)
|
|
2286
|
+
]);
|
|
2287
|
+
if (storefrontHeaders?.buyerIp) {
|
|
2288
|
+
forwardedHeaders.set("x-forwarded-for", storefrontHeaders.buyerIp);
|
|
2289
|
+
}
|
|
2290
|
+
const storefrontApiVersion = options2?.storefrontApiVersion ?? getSafePathname(request.url).match(SFAPI_RE)?.[1];
|
|
2291
|
+
const sfapiResponse = await fetch(
|
|
2292
|
+
getStorefrontApiUrl({ storefrontApiVersion }),
|
|
2293
|
+
{
|
|
2294
|
+
method: request.method,
|
|
2295
|
+
body: request.body,
|
|
2296
|
+
headers: forwardedHeaders
|
|
2297
|
+
}
|
|
2298
|
+
);
|
|
2299
|
+
return new Response(sfapiResponse.body, sfapiResponse);
|
|
2300
|
+
},
|
|
2301
|
+
setCollectedSubrequestHeaders: (response) => {
|
|
2302
|
+
if (collectedSubrequestHeaders) {
|
|
2303
|
+
for (const value of collectedSubrequestHeaders.setCookie) {
|
|
2304
|
+
response.headers.append("Set-Cookie", value);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
const serverTiming = extractServerTimingHeader(
|
|
2308
|
+
collectedSubrequestHeaders?.serverTiming
|
|
2309
|
+
);
|
|
2310
|
+
const isDocumentResponse = response.headers.get("content-type")?.startsWith("text/html");
|
|
2311
|
+
const fallbackValues = isDocumentResponse ? { _y: uniqueToken, _s: visitToken } : void 0;
|
|
2312
|
+
appendServerTimingHeader(response, {
|
|
2313
|
+
...fallbackValues,
|
|
2314
|
+
...serverTiming
|
|
2315
|
+
});
|
|
2316
|
+
if (isDocumentResponse && collectedSubrequestHeaders && // _shopify_essential cookie is always set, but we need more than that
|
|
2317
|
+
collectedSubrequestHeaders.setCookie.length > 1 && serverTiming?._y && serverTiming?._s && serverTiming?._cmp) {
|
|
2318
|
+
appendServerTimingHeader(response, {
|
|
2319
|
+
[HYDROGEN_SERVER_TRACKING_KEY]: "1"
|
|
2320
|
+
});
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2067
2323
|
}
|
|
2068
2324
|
};
|
|
2069
2325
|
}
|
|
@@ -2741,6 +2997,117 @@ var CART_GIFT_CARD_CODE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT)
|
|
|
2741
2997
|
${CART_WARNING_FRAGMENT}
|
|
2742
2998
|
`;
|
|
2743
2999
|
|
|
3000
|
+
// src/cart/queries/cartDeliveryAddressesAddDefault.tsx
|
|
3001
|
+
function cartDeliveryAddressesAddDefault(options) {
|
|
3002
|
+
return async (addresses, optionalParams) => {
|
|
3003
|
+
const { cartDeliveryAddressesAdd, errors: errors2 } = await options.storefront.mutate(CART_DELIVERY_ADDRESSES_ADD_MUTATION(options.cartFragment), {
|
|
3004
|
+
variables: {
|
|
3005
|
+
cartId: options.getCartId(),
|
|
3006
|
+
addresses,
|
|
3007
|
+
...optionalParams
|
|
3008
|
+
}
|
|
3009
|
+
});
|
|
3010
|
+
return formatAPIResult(cartDeliveryAddressesAdd, errors2);
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
var CART_DELIVERY_ADDRESSES_ADD_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
|
|
3014
|
+
mutation cartDeliveryAddressesAdd(
|
|
3015
|
+
$cartId: ID!
|
|
3016
|
+
$addresses: [CartSelectableAddressInput!]!,
|
|
3017
|
+
$country: CountryCode = ZZ
|
|
3018
|
+
$language: LanguageCode
|
|
3019
|
+
) @inContext(country: $country, language: $language) {
|
|
3020
|
+
cartDeliveryAddressesAdd(addresses: $addresses, cartId: $cartId) {
|
|
3021
|
+
cart {
|
|
3022
|
+
...CartApiMutation
|
|
3023
|
+
}
|
|
3024
|
+
userErrors {
|
|
3025
|
+
...CartApiError
|
|
3026
|
+
}
|
|
3027
|
+
warnings {
|
|
3028
|
+
...CartApiWarning
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
${cartFragment}
|
|
3033
|
+
${USER_ERROR_FRAGMENT}
|
|
3034
|
+
${CART_WARNING_FRAGMENT}
|
|
3035
|
+
`;
|
|
3036
|
+
|
|
3037
|
+
// src/cart/queries/cartDeliveryAddressesRemoveDefault.tsx
|
|
3038
|
+
function cartDeliveryAddressesRemoveDefault(options) {
|
|
3039
|
+
return async (addressIds, optionalParams) => {
|
|
3040
|
+
const { cartDeliveryAddressesRemove, errors: errors2 } = await options.storefront.mutate(CART_DELIVERY_ADDRESSES_REMOVE_MUTATION(options.cartFragment), {
|
|
3041
|
+
variables: {
|
|
3042
|
+
cartId: options.getCartId(),
|
|
3043
|
+
addressIds,
|
|
3044
|
+
...optionalParams
|
|
3045
|
+
}
|
|
3046
|
+
});
|
|
3047
|
+
return formatAPIResult(cartDeliveryAddressesRemove, errors2);
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
var CART_DELIVERY_ADDRESSES_REMOVE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
|
|
3051
|
+
mutation cartDeliveryAddressesRemove(
|
|
3052
|
+
$cartId: ID!
|
|
3053
|
+
$addressIds: [ID!]!,
|
|
3054
|
+
$country: CountryCode = ZZ
|
|
3055
|
+
$language: LanguageCode
|
|
3056
|
+
) @inContext(country: $country, language: $language) {
|
|
3057
|
+
cartDeliveryAddressesRemove(addressIds: $addressIds, cartId: $cartId) {
|
|
3058
|
+
cart {
|
|
3059
|
+
...CartApiMutation
|
|
3060
|
+
}
|
|
3061
|
+
userErrors {
|
|
3062
|
+
...CartApiError
|
|
3063
|
+
}
|
|
3064
|
+
warnings {
|
|
3065
|
+
...CartApiWarning
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
${cartFragment}
|
|
3070
|
+
${USER_ERROR_FRAGMENT}
|
|
3071
|
+
${CART_WARNING_FRAGMENT}
|
|
3072
|
+
`;
|
|
3073
|
+
|
|
3074
|
+
// src/cart/queries/cartDeliveryAddressesUpdateDefault.tsx
|
|
3075
|
+
function cartDeliveryAddressesUpdateDefault(options) {
|
|
3076
|
+
return async (addresses, optionalParams) => {
|
|
3077
|
+
const { cartDeliveryAddressesUpdate, errors: errors2 } = await options.storefront.mutate(CART_DELIVERY_ADDRESSES_UPDATE_MUTATION(options.cartFragment), {
|
|
3078
|
+
variables: {
|
|
3079
|
+
cartId: options.getCartId(),
|
|
3080
|
+
addresses,
|
|
3081
|
+
...optionalParams
|
|
3082
|
+
}
|
|
3083
|
+
});
|
|
3084
|
+
return formatAPIResult(cartDeliveryAddressesUpdate, errors2);
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3087
|
+
var CART_DELIVERY_ADDRESSES_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
|
|
3088
|
+
mutation cartDeliveryAddressesUpdate(
|
|
3089
|
+
$cartId: ID!
|
|
3090
|
+
$addresses: [CartSelectableAddressUpdateInput!]!,
|
|
3091
|
+
$country: CountryCode = ZZ
|
|
3092
|
+
$language: LanguageCode
|
|
3093
|
+
) @inContext(country: $country, language: $language) {
|
|
3094
|
+
cartDeliveryAddressesUpdate(addresses: $addresses, cartId: $cartId) {
|
|
3095
|
+
cart {
|
|
3096
|
+
...CartApiMutation
|
|
3097
|
+
}
|
|
3098
|
+
userErrors {
|
|
3099
|
+
...CartApiError
|
|
3100
|
+
}
|
|
3101
|
+
warnings {
|
|
3102
|
+
...CartApiWarning
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
${cartFragment}
|
|
3107
|
+
${USER_ERROR_FRAGMENT}
|
|
3108
|
+
${CART_WARNING_FRAGMENT}
|
|
3109
|
+
`;
|
|
3110
|
+
|
|
2744
3111
|
// src/cart/createCartHandler.ts
|
|
2745
3112
|
function createCartHandler(options) {
|
|
2746
3113
|
const {
|
|
@@ -2822,7 +3189,10 @@ function createCartHandler(options) {
|
|
|
2822
3189
|
optionalParams
|
|
2823
3190
|
) : await cartCreate({ metafields }, optionalParams);
|
|
2824
3191
|
},
|
|
2825
|
-
deleteMetafield: cartMetafieldDeleteDefault(mutateOptions)
|
|
3192
|
+
deleteMetafield: cartMetafieldDeleteDefault(mutateOptions),
|
|
3193
|
+
addDeliveryAddresses: cartDeliveryAddressesAddDefault(mutateOptions),
|
|
3194
|
+
removeDeliveryAddresses: cartDeliveryAddressesRemoveDefault(mutateOptions),
|
|
3195
|
+
updateDeliveryAddresses: cartDeliveryAddressesUpdateDefault(mutateOptions)
|
|
2826
3196
|
};
|
|
2827
3197
|
if ("customMethods" in options) {
|
|
2828
3198
|
return {
|
|
@@ -3503,7 +3873,18 @@ function createCustomerAccountClient({
|
|
|
3503
3873
|
]).toString()}`
|
|
3504
3874
|
).toString() : postLogoutRedirectUri;
|
|
3505
3875
|
clearSession(session);
|
|
3506
|
-
|
|
3876
|
+
const headers = options?.headers instanceof Headers ? options?.headers : new Headers(options?.headers);
|
|
3877
|
+
if (!options?.keepSession) {
|
|
3878
|
+
if (session.destroy) {
|
|
3879
|
+
headers.set("Set-Cookie", await session.destroy());
|
|
3880
|
+
} else {
|
|
3881
|
+
console.warn(
|
|
3882
|
+
"[h2:warn:customerAccount] session.destroy is not available on your session implementation. All session data might not be cleared on logout."
|
|
3883
|
+
);
|
|
3884
|
+
}
|
|
3885
|
+
session.isPending = false;
|
|
3886
|
+
}
|
|
3887
|
+
return redirect(logoutUrl, { headers });
|
|
3507
3888
|
},
|
|
3508
3889
|
isLoggedIn,
|
|
3509
3890
|
handleAuthStatus,
|
|
@@ -3752,8 +4133,63 @@ function getStorefrontHeaders(request) {
|
|
|
3752
4133
|
return {
|
|
3753
4134
|
requestGroupId: getHeader(request, "request-id"),
|
|
3754
4135
|
buyerIp: getHeader(request, "oxygen-buyer-ip"),
|
|
4136
|
+
buyerIpSig: getHeader(request, "X-Shopify-Client-IP-Sig"),
|
|
3755
4137
|
cookie: getHeader(request, "cookie"),
|
|
3756
|
-
purpose: getHeader(request, "purpose")
|
|
4138
|
+
purpose: getHeader(request, "sec-purpose") || getHeader(request, "purpose")
|
|
4139
|
+
};
|
|
4140
|
+
}
|
|
4141
|
+
function createRequestHandler({
|
|
4142
|
+
build,
|
|
4143
|
+
mode,
|
|
4144
|
+
poweredByHeader = true,
|
|
4145
|
+
getLoadContext,
|
|
4146
|
+
collectTrackingInformation = true,
|
|
4147
|
+
proxyStandardRoutes = true
|
|
4148
|
+
}) {
|
|
4149
|
+
const handleRequest = serverRuntime.createRequestHandler(build, mode);
|
|
4150
|
+
const appendPoweredByHeader = poweredByHeader ? (response) => response.headers.append("powered-by", "Shopify, Hydrogen") : void 0;
|
|
4151
|
+
return async (request) => {
|
|
4152
|
+
const method = request.method;
|
|
4153
|
+
if ((method === "GET" || method === "HEAD") && request.body) {
|
|
4154
|
+
return new Response(`${method} requests cannot have a body`, {
|
|
4155
|
+
status: 400
|
|
4156
|
+
});
|
|
4157
|
+
}
|
|
4158
|
+
const url = new URL(request.url);
|
|
4159
|
+
if (url.pathname.includes("//")) {
|
|
4160
|
+
return new Response(null, {
|
|
4161
|
+
status: 301,
|
|
4162
|
+
headers: {
|
|
4163
|
+
location: url.pathname.replace(/\/+/g, "/")
|
|
4164
|
+
}
|
|
4165
|
+
});
|
|
4166
|
+
}
|
|
4167
|
+
const context = getLoadContext ? await getLoadContext(request) : void 0;
|
|
4168
|
+
const storefront = context?.storefront;
|
|
4169
|
+
if (proxyStandardRoutes) {
|
|
4170
|
+
if (!storefront) {
|
|
4171
|
+
warnOnce(
|
|
4172
|
+
"[h2:createRequestHandler] Storefront instance is required to proxy standard routes."
|
|
4173
|
+
);
|
|
4174
|
+
}
|
|
4175
|
+
if (storefront?.isStorefrontApiUrl(request)) {
|
|
4176
|
+
const response2 = await storefront.forward(request);
|
|
4177
|
+
appendPoweredByHeader?.(response2);
|
|
4178
|
+
return response2;
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
const response = await handleRequest(request, context);
|
|
4182
|
+
if (storefront && proxyStandardRoutes) {
|
|
4183
|
+
if (collectTrackingInformation) {
|
|
4184
|
+
storefront.setCollectedSubrequestHeaders(response);
|
|
4185
|
+
}
|
|
4186
|
+
const fetchDest = request.headers.get("sec-fetch-dest");
|
|
4187
|
+
if (fetchDest && fetchDest === "document" || request.headers.get("accept")?.includes("text/html")) {
|
|
4188
|
+
appendServerTimingHeader(response, { [HYDROGEN_SFAPI_PROXY_KEY]: "1" });
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
appendPoweredByHeader?.(response);
|
|
4192
|
+
return response;
|
|
3757
4193
|
};
|
|
3758
4194
|
}
|
|
3759
4195
|
var NonceContext = react.createContext(void 0);
|
|
@@ -5741,6 +6177,9 @@ var QUERIES = {
|
|
|
5741
6177
|
//! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartMetafieldsSet
|
|
5742
6178
|
//! @see https://shopify.dev/docs/api/storefront/2025-01/mutations/cartMetafieldDelete
|
|
5743
6179
|
//! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartGiftCardCodesUpdate
|
|
6180
|
+
//! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartDeliveryAddressesAdd
|
|
6181
|
+
//! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartDeliveryAddressesRemove
|
|
6182
|
+
//! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartDeliveryAddressesUpdate
|
|
5744
6183
|
|
|
5745
6184
|
Object.defineProperty(exports, "AnalyticsEventName", {
|
|
5746
6185
|
enumerable: true,
|
|
@@ -5810,6 +6249,10 @@ Object.defineProperty(exports, "getShopifyCookies", {
|
|
|
5810
6249
|
enumerable: true,
|
|
5811
6250
|
get: function () { return hydrogenReact.getShopifyCookies; }
|
|
5812
6251
|
});
|
|
6252
|
+
Object.defineProperty(exports, "getTrackingValues", {
|
|
6253
|
+
enumerable: true,
|
|
6254
|
+
get: function () { return hydrogenReact.getTrackingValues; }
|
|
6255
|
+
});
|
|
5813
6256
|
Object.defineProperty(exports, "isOptionValueCombinationInEncodedVariant", {
|
|
5814
6257
|
enumerable: true,
|
|
5815
6258
|
get: function () { return hydrogenReact.isOptionValueCombinationInEncodedVariant; }
|
|
@@ -5885,6 +6328,7 @@ exports.createCartHandler = createCartHandler;
|
|
|
5885
6328
|
exports.createContentSecurityPolicy = createContentSecurityPolicy;
|
|
5886
6329
|
exports.createCustomerAccountClient = createCustomerAccountClient;
|
|
5887
6330
|
exports.createHydrogenContext = createHydrogenContext;
|
|
6331
|
+
exports.createRequestHandler = createRequestHandler;
|
|
5888
6332
|
exports.createStorefrontClient = createStorefrontClient;
|
|
5889
6333
|
exports.createWithCache = createWithCache;
|
|
5890
6334
|
exports.formatAPIResult = formatAPIResult;
|