@shopify/hydrogen 2024.7.3 → 2024.7.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.
@@ -511,7 +511,7 @@ var errorOnce = (string) => {
511
511
  };
512
512
 
513
513
  // src/version.ts
514
- var LIB_VERSION = "2024.7.3";
514
+ var LIB_VERSION = "2024.7.5";
515
515
 
516
516
  // src/utils/graphql.ts
517
517
  function minifyQuery(string) {
@@ -2968,6 +2968,7 @@ CartForm.ACTIONS = {
2968
2968
  BuyerIdentityUpdate: "BuyerIdentityUpdate",
2969
2969
  Create: "Create",
2970
2970
  DiscountCodesUpdate: "DiscountCodesUpdate",
2971
+ GiftCardCodesUpdate: "GiftCardCodesUpdate",
2971
2972
  LinesAdd: "LinesAdd",
2972
2973
  LinesRemove: "LinesRemove",
2973
2974
  LinesUpdate: "LinesUpdate",
@@ -3007,6 +3008,7 @@ var MINIMAL_CART_FRAGMENT = `#graphql
3007
3008
  fragment CartApiMutation on Cart {
3008
3009
  id
3009
3010
  totalQuantity
3011
+ checkoutUrl
3010
3012
  }
3011
3013
  `;
3012
3014
 
@@ -3358,6 +3360,42 @@ var CART_DISCOUNT_CODE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT)
3358
3360
  ${USER_ERROR_FRAGMENT}
3359
3361
  `;
3360
3362
 
3363
+ // src/cart/queries/cartGiftCardCodeUpdateDefault.ts
3364
+ function cartGiftCardCodesUpdateDefault(options) {
3365
+ return async (giftCardCodes, optionalParams) => {
3366
+ const uniqueCodes = giftCardCodes.filter((value, index, array) => {
3367
+ return array.indexOf(value) === index;
3368
+ });
3369
+ const { cartGiftCardCodesUpdate, errors: errors2 } = await options.storefront.mutate(CART_GIFT_CARD_CODE_UPDATE_MUTATION(options.cartFragment), {
3370
+ variables: {
3371
+ cartId: options.getCartId(),
3372
+ giftCardCodes: uniqueCodes,
3373
+ ...optionalParams
3374
+ }
3375
+ });
3376
+ return formatAPIResult(cartGiftCardCodesUpdate, errors2);
3377
+ };
3378
+ }
3379
+ var CART_GIFT_CARD_CODE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
3380
+ mutation cartGiftCardCodesUpdate(
3381
+ $cartId: ID!
3382
+ $giftCardCodes: [String!]!
3383
+ $language: LanguageCode
3384
+ $country: CountryCode
3385
+ ) @inContext(country: $country, language: $language) {
3386
+ cartGiftCardCodesUpdate(cartId: $cartId, giftCardCodes: $giftCardCodes) {
3387
+ cart {
3388
+ ...CartApiMutation
3389
+ }
3390
+ userErrors {
3391
+ ...CartApiError
3392
+ }
3393
+ }
3394
+ }
3395
+ ${cartFragment}
3396
+ ${USER_ERROR_FRAGMENT}
3397
+ `;
3398
+
3361
3399
  // src/cart/queries/cartBuyerIdentityUpdateDefault.ts
3362
3400
  function cartBuyerIdentityUpdateDefault(options) {
3363
3401
  return async (buyerIdentity, optionalParams) => {
@@ -3646,6 +3684,12 @@ function createCartHandler(options) {
3646
3684
  optionalParams
3647
3685
  ) : await cartCreate({ discountCodes }, optionalParams);
3648
3686
  },
3687
+ updateGiftCardCodes: async (giftCardCodes, optionalParams) => {
3688
+ return cartId || optionalParams?.cartId ? await cartGiftCardCodesUpdateDefault(mutateOptions)(
3689
+ giftCardCodes,
3690
+ optionalParams
3691
+ ) : await cartCreate({ giftCardCodes }, optionalParams);
3692
+ },
3649
3693
  updateBuyerIdentity: async (buyerIdentity, optionalParams) => {
3650
3694
  return cartId || optionalParams?.cartId ? await cartBuyerIdentityUpdateDefault(mutateOptions)(
3651
3695
  buyerIdentity,
@@ -3758,6 +3802,10 @@ function useOptimisticCart(cart) {
3758
3802
  if (isOptimistic) {
3759
3803
  optimisticCart.isOptimistic = isOptimistic;
3760
3804
  }
3805
+ optimisticCart.totalQuantity = cartLines.reduce(
3806
+ (sum, line) => sum + line.quantity,
3807
+ 0
3808
+ );
3761
3809
  return optimisticCart;
3762
3810
  }
3763
3811
  function VariantSelector({
@@ -3972,10 +4020,16 @@ function createCSPHeader(nonce, props) {
3972
4020
  );
3973
4021
  }
3974
4022
  }
3975
- if (combinedDirectives.scriptSrc instanceof Array && !combinedDirectives.scriptSrc.includes(nonceString)) {
3976
- combinedDirectives.scriptSrc.push(nonceString);
3977
- } else if (combinedDirectives.defaultSrc instanceof Array && !combinedDirectives.defaultSrc.includes(nonceString)) {
3978
- combinedDirectives.defaultSrc.push(nonceString);
4023
+ if (combinedDirectives.scriptSrc instanceof Array) {
4024
+ combinedDirectives.scriptSrc = [
4025
+ ...combinedDirectives.scriptSrc.filter((ss) => !ss.startsWith(`'nonce`)),
4026
+ nonceString
4027
+ ];
4028
+ } else if (combinedDirectives.defaultSrc instanceof Array) {
4029
+ combinedDirectives.defaultSrc = [
4030
+ ...combinedDirectives.defaultSrc.filter((ss) => !ss.startsWith(`'nonce`)),
4031
+ nonceString
4032
+ ];
3979
4033
  }
3980
4034
  return cspBuilder__default.default({
3981
4035
  directives: combinedDirectives
@@ -3994,7 +4048,6 @@ function addCspDirective(currentValue, value) {
3994
4048
  var Script = react.forwardRef(
3995
4049
  (props, ref) => {
3996
4050
  const { waitForHydration, src, ...rest } = props;
3997
- if (!src) throw new Error("Script components require a `src` prop");
3998
4051
  if (waitForHydration) return /* @__PURE__ */ jsxRuntime.jsx(LazyScript, { src, options: rest });
3999
4052
  const nonce = useNonce();
4000
4053
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -4013,6 +4066,10 @@ function LazyScript({
4013
4066
  src,
4014
4067
  options
4015
4068
  }) {
4069
+ if (!src)
4070
+ throw new Error(
4071
+ "`waitForHydration` with the Script component requires a `src` prop"
4072
+ );
4016
4073
  hydrogenReact.useLoadScript(src, {
4017
4074
  attributes: options
4018
4075
  });
@@ -4131,15 +4188,33 @@ function useCustomerPrivacy(props) {
4131
4188
  onReady,
4132
4189
  ...consentConfig
4133
4190
  } = props;
4134
- const loadedEvent = react.useRef(false);
4135
- const scriptStatus = hydrogenReact.useLoadScript(
4136
- withPrivacyBanner ? CONSENT_API_WITH_BANNER : CONSENT_API,
4137
- {
4138
- attributes: {
4139
- id: "customer-privacy-api"
4140
- }
4191
+ hydrogenReact.useLoadScript(withPrivacyBanner ? CONSENT_API_WITH_BANNER : CONSENT_API, {
4192
+ attributes: {
4193
+ id: "customer-privacy-api"
4141
4194
  }
4142
- );
4195
+ });
4196
+ const { observing, setLoaded } = useApisLoaded({
4197
+ withPrivacyBanner,
4198
+ onLoaded: onReady
4199
+ });
4200
+ const config = react.useMemo(() => {
4201
+ const { checkoutDomain, storefrontAccessToken } = consentConfig;
4202
+ if (!checkoutDomain) logMissingConfig("checkoutDomain");
4203
+ if (!storefrontAccessToken) logMissingConfig("storefrontAccessToken");
4204
+ if (storefrontAccessToken.startsWith("shpat_") || storefrontAccessToken.length !== 32) {
4205
+ console.error(
4206
+ `[h2:error:useCustomerPrivacy] It looks like you passed a private access token, make sure to use the public token`
4207
+ );
4208
+ }
4209
+ const config2 = {
4210
+ checkoutRootDomain: checkoutDomain,
4211
+ storefrontAccessToken,
4212
+ storefrontRootDomain: parseStoreDomain(checkoutDomain),
4213
+ country: consentConfig.country,
4214
+ locale: consentConfig.locale
4215
+ };
4216
+ return config2;
4217
+ }, [consentConfig, parseStoreDomain, logMissingConfig]);
4143
4218
  react.useEffect(() => {
4144
4219
  const consentCollectedHandler = (event) => {
4145
4220
  if (onVisitorConsentCollected) {
@@ -4158,54 +4233,166 @@ function useCustomerPrivacy(props) {
4158
4233
  };
4159
4234
  }, [onVisitorConsentCollected]);
4160
4235
  react.useEffect(() => {
4161
- if (scriptStatus !== "done" || loadedEvent.current) return;
4162
- loadedEvent.current = true;
4163
- const { checkoutDomain, storefrontAccessToken } = consentConfig;
4164
- if (!checkoutDomain) logMissingConfig("checkoutDomain");
4165
- if (!storefrontAccessToken) logMissingConfig("storefrontAccessToken");
4166
- if (storefrontAccessToken.startsWith("shpat_") || storefrontAccessToken.length !== 32) {
4167
- console.error(
4168
- `[h2:error:useCustomerPrivacy] It looks like you passed a private access token, make sure to use the public token`
4169
- );
4170
- }
4171
- const config = {
4172
- checkoutRootDomain: checkoutDomain,
4173
- storefrontAccessToken
4236
+ if (!withPrivacyBanner || observing.current.privacyBanner) return;
4237
+ observing.current.privacyBanner = true;
4238
+ let customPrivacyBanner = void 0;
4239
+ const privacyBannerWatcher = {
4240
+ configurable: true,
4241
+ get() {
4242
+ return customPrivacyBanner;
4243
+ },
4244
+ set(value) {
4245
+ if (typeof value === "object" && value !== null && "showPreferences" in value && "loadBanner" in value) {
4246
+ const privacyBanner = value;
4247
+ privacyBanner.loadBanner(config);
4248
+ customPrivacyBanner = overridePrivacyBannerMethods({
4249
+ privacyBanner,
4250
+ config
4251
+ });
4252
+ setLoaded.privacyBanner();
4253
+ }
4254
+ }
4174
4255
  };
4175
- if (checkoutDomain) {
4176
- let storefrontRootDomain = window.document.location.host;
4177
- const checkoutDomainParts = checkoutDomain.split(".").reverse();
4178
- const currentDomainParts = storefrontRootDomain.split(".").reverse();
4179
- const sameDomainParts = [];
4180
- checkoutDomainParts.forEach((part, index) => {
4181
- if (part === currentDomainParts[index]) {
4182
- sameDomainParts.push(part);
4256
+ Object.defineProperty(window, "privacyBanner", privacyBannerWatcher);
4257
+ }, [
4258
+ withPrivacyBanner,
4259
+ config,
4260
+ overridePrivacyBannerMethods,
4261
+ setLoaded.privacyBanner
4262
+ ]);
4263
+ react.useEffect(() => {
4264
+ if (observing.current.customerPrivacy) return;
4265
+ observing.current.customerPrivacy = true;
4266
+ let customCustomerPrivacy = null;
4267
+ let customShopify = void 0;
4268
+ Object.defineProperty(window, "Shopify", {
4269
+ configurable: true,
4270
+ get() {
4271
+ return customShopify;
4272
+ },
4273
+ set(value) {
4274
+ if (typeof value === "object" && value !== null && Object.keys(value).length === 0) {
4275
+ customShopify = value;
4276
+ Object.defineProperty(window.Shopify, "customerPrivacy", {
4277
+ configurable: true,
4278
+ get() {
4279
+ return customCustomerPrivacy;
4280
+ },
4281
+ set(value2) {
4282
+ if (typeof value2 === "object" && value2 !== null && "setTrackingConsent" in value2) {
4283
+ const customerPrivacy = value2;
4284
+ customCustomerPrivacy = {
4285
+ ...customerPrivacy,
4286
+ setTrackingConsent: overrideCustomerPrivacySetTrackingConsent(
4287
+ { customerPrivacy, config }
4288
+ )
4289
+ };
4290
+ customShopify = {
4291
+ ...customShopify,
4292
+ customerPrivacy: customCustomerPrivacy
4293
+ };
4294
+ setLoaded.customerPrivacy();
4295
+ }
4296
+ }
4297
+ });
4183
4298
  }
4184
- });
4185
- storefrontRootDomain = sameDomainParts.reverse().join(".");
4186
- if (storefrontRootDomain) {
4187
- config.storefrontRootDomain = storefrontRootDomain;
4188
4299
  }
4300
+ });
4301
+ }, [
4302
+ config,
4303
+ overrideCustomerPrivacySetTrackingConsent,
4304
+ setLoaded.customerPrivacy
4305
+ ]);
4306
+ const result = {
4307
+ customerPrivacy: getCustomerPrivacy()
4308
+ };
4309
+ if (withPrivacyBanner) {
4310
+ result.privacyBanner = getPrivacyBanner();
4311
+ }
4312
+ return result;
4313
+ }
4314
+ function useApisLoaded({
4315
+ withPrivacyBanner,
4316
+ onLoaded
4317
+ }) {
4318
+ const observing = react.useRef({ customerPrivacy: false, privacyBanner: false });
4319
+ const [apisLoaded, setApisLoaded] = react.useState(
4320
+ withPrivacyBanner ? [false, false] : [false]
4321
+ );
4322
+ const loaded = apisLoaded.every(Boolean);
4323
+ const setLoaded = {
4324
+ customerPrivacy: () => {
4325
+ if (withPrivacyBanner) {
4326
+ setApisLoaded((prev) => [true, prev[1]]);
4327
+ } else {
4328
+ setApisLoaded(() => [true]);
4329
+ }
4330
+ },
4331
+ privacyBanner: () => {
4332
+ if (!withPrivacyBanner) {
4333
+ return;
4334
+ }
4335
+ setApisLoaded((prev) => [prev[0], true]);
4189
4336
  }
4190
- if (withPrivacyBanner && window?.privacyBanner) {
4191
- window.privacyBanner?.loadBanner(config);
4337
+ };
4338
+ react.useEffect(() => {
4339
+ if (loaded && onLoaded) {
4340
+ onLoaded();
4192
4341
  }
4193
- if (!window.Shopify?.customerPrivacy) return;
4194
- const originalSetTrackingConsent = window.Shopify.customerPrivacy.setTrackingConsent;
4195
- function overrideSetTrackingConsent(consent, callback) {
4196
- originalSetTrackingConsent(
4197
- {
4198
- ...consent,
4199
- headlessStorefront: true,
4200
- ...config
4201
- },
4202
- callback
4203
- );
4342
+ }, [loaded, onLoaded]);
4343
+ return { observing, setLoaded };
4344
+ }
4345
+ function parseStoreDomain(checkoutDomain) {
4346
+ if (typeof window === "undefined") return;
4347
+ const host = window.document.location.host;
4348
+ const checkoutDomainParts = checkoutDomain.split(".").reverse();
4349
+ const currentDomainParts = host.split(".").reverse();
4350
+ const sameDomainParts = [];
4351
+ checkoutDomainParts.forEach((part, index) => {
4352
+ if (part === currentDomainParts[index]) {
4353
+ sameDomainParts.push(part);
4204
4354
  }
4205
- window.Shopify.customerPrivacy.setTrackingConsent = overrideSetTrackingConsent;
4206
- onReady && onReady();
4207
- }, [scriptStatus, withPrivacyBanner, consentConfig]);
4208
- return;
4355
+ });
4356
+ return sameDomainParts.reverse().join(".");
4357
+ }
4358
+ function overrideCustomerPrivacySetTrackingConsent({
4359
+ customerPrivacy,
4360
+ config
4361
+ }) {
4362
+ const original = customerPrivacy.setTrackingConsent;
4363
+ function updatedSetTrackingConsent(consent, callback) {
4364
+ original(
4365
+ {
4366
+ ...consent,
4367
+ headlessStorefront: true,
4368
+ ...config
4369
+ },
4370
+ callback
4371
+ );
4372
+ }
4373
+ return updatedSetTrackingConsent;
4374
+ }
4375
+ function overridePrivacyBannerMethods({
4376
+ privacyBanner,
4377
+ config
4378
+ }) {
4379
+ const originalLoadBanner = privacyBanner.loadBanner;
4380
+ const originalShowPreferences = privacyBanner.showPreferences;
4381
+ function loadBanner(userConfig) {
4382
+ if (typeof userConfig === "object") {
4383
+ originalLoadBanner({ ...config, ...userConfig });
4384
+ return;
4385
+ }
4386
+ originalLoadBanner(config);
4387
+ }
4388
+ function showPreferences(userConfig) {
4389
+ if (typeof userConfig === "object") {
4390
+ originalShowPreferences({ ...config, ...userConfig });
4391
+ return;
4392
+ }
4393
+ originalShowPreferences(config);
4394
+ }
4395
+ return { loadBanner, showPreferences };
4209
4396
  }
4210
4397
  function getCustomerPrivacy() {
4211
4398
  try {
@@ -4214,6 +4401,13 @@ function getCustomerPrivacy() {
4214
4401
  return null;
4215
4402
  }
4216
4403
  }
4404
+ function getPrivacyBanner() {
4405
+ try {
4406
+ return window && window?.privacyBanner ? window.privacyBanner : null;
4407
+ } catch (e) {
4408
+ return null;
4409
+ }
4410
+ }
4217
4411
  function getCustomerPrivacyRequired() {
4218
4412
  const customerPrivacy = getCustomerPrivacy();
4219
4413
  if (!customerPrivacy) {
@@ -4232,30 +4426,19 @@ function ShopifyAnalytics({
4232
4426
  const [shopifyReady, setShopifyReady] = react.useState(false);
4233
4427
  const [privacyReady, setPrivacyReady] = react.useState(false);
4234
4428
  const init = react.useRef(false);
4429
+ const { checkoutDomain, storefrontAccessToken, language } = consent;
4235
4430
  const { ready: shopifyAnalyticsReady } = register2("Internal_Shopify_Analytics");
4236
- const { ready: customerPrivacyReady } = register2(
4237
- "Internal_Shopify_CustomerPrivacy"
4238
- );
4239
- const analyticsReady = () => {
4240
- shopifyReady && privacyReady && onReady();
4241
- };
4242
- const setCustomerPrivacyReady = () => {
4243
- setPrivacyReady(true);
4244
- customerPrivacyReady();
4245
- analyticsReady();
4246
- };
4247
- const { checkoutDomain, storefrontAccessToken, withPrivacyBanner } = consent;
4248
4431
  useCustomerPrivacy({
4432
+ ...consent,
4433
+ locale: language,
4249
4434
  checkoutDomain: !checkoutDomain ? "mock.shop" : checkoutDomain,
4250
4435
  storefrontAccessToken: !storefrontAccessToken ? "abcdefghijklmnopqrstuvwxyz123456" : storefrontAccessToken,
4251
- withPrivacyBanner,
4252
- onVisitorConsentCollected: setCustomerPrivacyReady,
4253
- onReady: () => {
4254
- setTimeout(setCustomerPrivacyReady, 3e3);
4255
- }
4436
+ onVisitorConsentCollected: () => setPrivacyReady(true),
4437
+ onReady: () => setPrivacyReady(true)
4256
4438
  });
4257
4439
  hydrogenReact.useShopifyCookies({
4258
- hasUserConsent: shopifyReady && privacyReady ? canTrack() : true,
4440
+ hasUserConsent: privacyReady ? canTrack() : true,
4441
+ // must be initialized with true
4259
4442
  domain,
4260
4443
  checkoutDomain
4261
4444
  });
@@ -4267,10 +4450,14 @@ function ShopifyAnalytics({
4267
4450
  subscribe2(AnalyticsEvent.COLLECTION_VIEWED, collectionViewHandler);
4268
4451
  subscribe2(AnalyticsEvent.SEARCH_VIEWED, searchViewHandler);
4269
4452
  subscribe2(AnalyticsEvent.PRODUCT_ADD_TO_CART, productAddedToCartHandler);
4270
- shopifyAnalyticsReady();
4271
4453
  setShopifyReady(true);
4272
- analyticsReady();
4273
- }, [subscribe2, shopifyAnalyticsReady]);
4454
+ }, [subscribe2]);
4455
+ react.useEffect(() => {
4456
+ if (shopifyReady && privacyReady) {
4457
+ shopifyAnalyticsReady();
4458
+ onReady();
4459
+ }
4460
+ }, [shopifyReady, privacyReady, onReady]);
4274
4461
  return null;
4275
4462
  }
4276
4463
  function logMissingConfig2(fieldName) {
@@ -4591,12 +4778,12 @@ function CartAnalytics({
4591
4778
  }, [cart, prevCart, publish2, shop, customData, canTrack]);
4592
4779
  return null;
4593
4780
  }
4594
- var PERF_KIT_UNSTABLE = "https://cdn.shopify.com/shopifycloud/perf-kit/shopify-perf-kit-1bd852a.min.js";
4781
+ var PERF_KIT_URL = "https://cdn.shopify.com/shopifycloud/perf-kit/shopify-perf-kit-1.0.0.min.js";
4595
4782
  function PerfKit({ shop }) {
4596
4783
  const loadedEvent = react.useRef(false);
4597
4784
  const { subscribe: subscribe2, register: register2 } = useAnalytics();
4598
4785
  const { ready } = register2("Internal_Shopify_Perf_Kit");
4599
- const scriptStatus = hydrogenReact.useLoadScript(PERF_KIT_UNSTABLE, {
4786
+ const scriptStatus = hydrogenReact.useLoadScript(PERF_KIT_URL, {
4600
4787
  attributes: {
4601
4788
  id: "perfkit",
4602
4789
  "data-application": "hydrogen",
@@ -4640,7 +4827,9 @@ var defaultAnalyticsContext = {
4640
4827
  subscribe: () => {
4641
4828
  },
4642
4829
  register: () => ({ ready: () => {
4643
- } })
4830
+ } }),
4831
+ customerPrivacy: null,
4832
+ privacyBanner: null
4644
4833
  };
4645
4834
  var AnalyticsContext = react.createContext(
4646
4835
  defaultAnalyticsContext
@@ -4719,7 +4908,7 @@ function AnalyticsProvider({
4719
4908
  }) {
4720
4909
  const listenerSet = react.useRef(false);
4721
4910
  const { shop } = useShopAnalytics(shopProp);
4722
- const [consentLoaded, setConsentLoaded] = react.useState(
4911
+ const [analyticsLoaded, setAnalyticsLoaded] = react.useState(
4723
4912
  customCanTrack ? true : false
4724
4913
  );
4725
4914
  const [carts, setCarts] = react.useState({ cart: null, prevCart: null });
@@ -4746,6 +4935,15 @@ function AnalyticsProvider({
4746
4935
  );
4747
4936
  errorOnce(errorMsg);
4748
4937
  }
4938
+ if (!consent?.country) {
4939
+ consent.country = "US";
4940
+ }
4941
+ if (!consent?.language) {
4942
+ consent.language = "EN";
4943
+ }
4944
+ if (consent.withPrivacyBanner === void 0) {
4945
+ consent.withPrivacyBanner = true;
4946
+ }
4749
4947
  }
4750
4948
  }
4751
4949
  const value = react.useMemo(() => {
@@ -4757,13 +4955,13 @@ function AnalyticsProvider({
4757
4955
  },
4758
4956
  shop,
4759
4957
  subscribe,
4760
- register
4958
+ register,
4959
+ customerPrivacy: getCustomerPrivacy(),
4960
+ privacyBanner: getPrivacyBanner()
4761
4961
  };
4762
4962
  }, [
4763
- consentLoaded,
4764
- canTrack(),
4963
+ analyticsLoaded,
4765
4964
  canTrack,
4766
- JSON.stringify(canTrack),
4767
4965
  carts,
4768
4966
  carts.cart?.updatedAt,
4769
4967
  carts.prevCart,
@@ -4772,7 +4970,9 @@ function AnalyticsProvider({
4772
4970
  customData,
4773
4971
  shop,
4774
4972
  register,
4775
- JSON.stringify(registers)
4973
+ JSON.stringify(registers),
4974
+ getCustomerPrivacy,
4975
+ getPrivacyBanner
4776
4976
  ]);
4777
4977
  return /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsContext.Provider, { value, children: [
4778
4978
  children,
@@ -4784,7 +4984,7 @@ function AnalyticsProvider({
4784
4984
  consent,
4785
4985
  onReady: () => {
4786
4986
  listenerSet.current = true;
4787
- setConsentLoaded(true);
4987
+ setAnalyticsLoaded(true);
4788
4988
  setCanTrack(() => shopifyCanTrack);
4789
4989
  },
4790
4990
  domain: cookieDomain
@@ -4953,12 +5153,284 @@ function getStorefrontHeaders(request) {
4953
5153
  purpose: getHeader(request, "purpose")
4954
5154
  };
4955
5155
  }
5156
+
5157
+ // src/sitemap/sitemap.ts
5158
+ var SITEMAP_INDEX_PREFIX = `<?xml version="1.0" encoding="UTF-8"?>
5159
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
5160
+ `;
5161
+ var SITEMAP_INDEX_SUFFIX = `
5162
+ </sitemapindex>`;
5163
+ var SITEMAP_PREFIX = `<?xml version="1.0" encoding="UTF-8"?>
5164
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">`;
5165
+ var SITEMAP_SUFFIX = `</urlset>`;
5166
+ async function getSitemapIndex(options) {
5167
+ const {
5168
+ storefront,
5169
+ request,
5170
+ types = [
5171
+ "products",
5172
+ "pages",
5173
+ "collections",
5174
+ "metaObjects",
5175
+ "articles",
5176
+ "blogs"
5177
+ ],
5178
+ customChildSitemaps = []
5179
+ } = options;
5180
+ if (!request || !request.url)
5181
+ throw new Error("A request object is required to generate a sitemap index");
5182
+ if (!storefront || !storefront.query)
5183
+ throw new Error(
5184
+ "A storefront client is required to generate a sitemap index"
5185
+ );
5186
+ const data = await storefront.query(SITEMAP_INDEX_QUERY, {
5187
+ storefrontApiVersion: "unstable"
5188
+ });
5189
+ if (!data) {
5190
+ throw new Response("No data found", { status: 404 });
5191
+ }
5192
+ const baseUrl = new URL(request.url).origin;
5193
+ const body = SITEMAP_INDEX_PREFIX + types.map((type) => {
5194
+ if (!data[type]) {
5195
+ throw new Error(
5196
+ `[h2:sitemap:error] No data found for type ${type}. Check types passed to \`getSitemapIndex\``
5197
+ );
5198
+ }
5199
+ return getSiteMapLinks(type, data[type].pagesCount.count, baseUrl);
5200
+ }).join("\n") + customChildSitemaps.map(
5201
+ (url) => " <sitemap><loc>" + (baseUrl + (url.startsWith("/") ? url : "/" + url)) + "</loc></sitemap>"
5202
+ ).join("\n") + SITEMAP_INDEX_SUFFIX;
5203
+ return new Response(body, {
5204
+ headers: {
5205
+ "Content-Type": "application/xml",
5206
+ "Cache-Control": `max-age=${60 * 60 * 24}`
5207
+ }
5208
+ });
5209
+ }
5210
+ async function getSitemap(options) {
5211
+ const {
5212
+ storefront,
5213
+ request,
5214
+ params,
5215
+ getLink,
5216
+ locales = [],
5217
+ getChangeFreq,
5218
+ noItemsFallback = "/"
5219
+ } = options;
5220
+ if (!params)
5221
+ throw new Error(
5222
+ "[h2:sitemap:error] Remix params object is required to generate a sitemap"
5223
+ );
5224
+ if (!request || !request.url)
5225
+ throw new Error("A request object is required to generate a sitemap");
5226
+ if (!storefront || !storefront.query)
5227
+ throw new Error("A storefront client is required to generate a index");
5228
+ if (!getLink)
5229
+ throw new Error(
5230
+ "A `getLink` function to generate each resource is required to build a sitemap"
5231
+ );
5232
+ if (!params.type || !params.page)
5233
+ throw new Response("No data found", { status: 404 });
5234
+ const type = params.type;
5235
+ const query = QUERIES[type];
5236
+ if (!query) throw new Response("Not found", { status: 404 });
5237
+ const data = await storefront.query(query, {
5238
+ variables: {
5239
+ page: parseInt(params.page, 10)
5240
+ },
5241
+ storefrontApiVersion: "unstable"
5242
+ });
5243
+ const baseUrl = new URL(request.url).origin;
5244
+ let body = "";
5245
+ if (!data?.sitemap?.resources?.items?.length) {
5246
+ body = SITEMAP_PREFIX + `
5247
+ <url><loc>${baseUrl + noItemsFallback}</loc></url>
5248
+ ` + SITEMAP_SUFFIX;
5249
+ } else {
5250
+ body = SITEMAP_PREFIX + data.sitemap.resources.items.map((item) => {
5251
+ return renderUrlTag({
5252
+ getChangeFreq,
5253
+ url: getLink({
5254
+ type: item.type ?? type,
5255
+ baseUrl,
5256
+ handle: item.handle
5257
+ }),
5258
+ type,
5259
+ getLink,
5260
+ updatedAt: item.updatedAt,
5261
+ handle: item.handle,
5262
+ metaobjectType: item.type,
5263
+ locales,
5264
+ baseUrl
5265
+ });
5266
+ }).join("\n") + SITEMAP_SUFFIX;
5267
+ }
5268
+ return new Response(body, {
5269
+ headers: {
5270
+ "Content-Type": "application/xml",
5271
+ "Cache-Control": `max-age=${60 * 60 * 24}`
5272
+ }
5273
+ });
5274
+ }
5275
+ function getSiteMapLinks(resource, count, baseUrl) {
5276
+ let links = ``;
5277
+ for (let i = 1; i <= count; i++) {
5278
+ links += ` <sitemap><loc>${baseUrl}/sitemap/${resource}/${i}.xml</loc></sitemap>
5279
+ `;
5280
+ }
5281
+ return links;
5282
+ }
5283
+ function renderUrlTag({
5284
+ url,
5285
+ updatedAt,
5286
+ locales,
5287
+ type,
5288
+ getLink,
5289
+ baseUrl,
5290
+ handle,
5291
+ getChangeFreq,
5292
+ metaobjectType
5293
+ }) {
5294
+ return `<url>
5295
+ <loc>${url}</loc>
5296
+ <lastmod>${updatedAt}</lastmod>
5297
+ <changefreq>${getChangeFreq ? getChangeFreq({ type: metaobjectType ?? type, handle }) : "weekly"}</changefreq>
5298
+ ${locales.map(
5299
+ (locale) => renderAlternateTag(
5300
+ getLink({ type: metaobjectType ?? type, baseUrl, handle, locale }),
5301
+ locale
5302
+ )
5303
+ ).join("\n")}
5304
+ </url>
5305
+ `.trim();
5306
+ }
5307
+ function renderAlternateTag(url, locale) {
5308
+ return ` <xhtml:link rel="alternate" hreflang="${locale}" href="${url}" />`;
5309
+ }
5310
+ var PRODUCT_SITEMAP_QUERY = `#graphql
5311
+ query SitemapProducts($page: Int!) {
5312
+ sitemap(type: PRODUCT) {
5313
+ resources(page: $page) {
5314
+ items {
5315
+ handle
5316
+ updatedAt
5317
+ }
5318
+ }
5319
+ }
5320
+ }
5321
+ `;
5322
+ var COLLECTION_SITEMAP_QUERY = `#graphql
5323
+ query SitemapCollections($page: Int!) {
5324
+ sitemap(type: COLLECTION) {
5325
+ resources(page: $page) {
5326
+ items {
5327
+ handle
5328
+ updatedAt
5329
+ }
5330
+ }
5331
+ }
5332
+ }
5333
+ `;
5334
+ var ARTICLE_SITEMAP_QUERY = `#graphql
5335
+ query SitemapArticles($page: Int!) {
5336
+ sitemap(type: ARTICLE) {
5337
+ resources(page: $page) {
5338
+ items {
5339
+ handle
5340
+ updatedAt
5341
+ }
5342
+ }
5343
+ }
5344
+ }
5345
+ `;
5346
+ var PAGE_SITEMAP_QUERY = `#graphql
5347
+ query SitemapPages($page: Int!) {
5348
+ sitemap(type: PAGE) {
5349
+ resources(page: $page) {
5350
+ items {
5351
+ handle
5352
+ updatedAt
5353
+ }
5354
+ }
5355
+ }
5356
+ }
5357
+ `;
5358
+ var BLOG_SITEMAP_QUERY = `#graphql
5359
+ query SitemapBlogs($page: Int!) {
5360
+ sitemap(type: BLOG) {
5361
+ resources(page: $page) {
5362
+ items {
5363
+ handle
5364
+ updatedAt
5365
+ }
5366
+ }
5367
+ }
5368
+ }
5369
+ `;
5370
+ var METAOBJECT_SITEMAP_QUERY = `#graphql
5371
+ query SitemapMetaobjects($page: Int!) {
5372
+ sitemap(type: METAOBJECT_PAGE) {
5373
+ resources(page: $page) {
5374
+ items {
5375
+ handle
5376
+ updatedAt
5377
+ ... on SitemapResourceMetaobject {
5378
+ type
5379
+ }
5380
+ }
5381
+ }
5382
+ }
5383
+ }
5384
+ `;
5385
+ var SITEMAP_INDEX_QUERY = `#graphql
5386
+ query SitemapIndex {
5387
+ products: sitemap(type: PRODUCT) {
5388
+ pagesCount {
5389
+ count
5390
+ }
5391
+ }
5392
+ collections: sitemap(type: COLLECTION) {
5393
+ pagesCount {
5394
+ count
5395
+ }
5396
+ }
5397
+ articles: sitemap(type: ARTICLE) {
5398
+ pagesCount {
5399
+ count
5400
+ }
5401
+ }
5402
+ pages: sitemap(type: PAGE) {
5403
+ pagesCount {
5404
+ count
5405
+ }
5406
+ }
5407
+ blogs: sitemap(type: BLOG) {
5408
+ pagesCount {
5409
+ count
5410
+ }
5411
+ }
5412
+ metaObjects: sitemap(type: METAOBJECT_PAGE) {
5413
+ pagesCount {
5414
+ count
5415
+ }
5416
+ }
5417
+ }
5418
+ `;
5419
+ var QUERIES = {
5420
+ products: PRODUCT_SITEMAP_QUERY,
5421
+ articles: ARTICLE_SITEMAP_QUERY,
5422
+ collections: COLLECTION_SITEMAP_QUERY,
5423
+ pages: PAGE_SITEMAP_QUERY,
5424
+ blogs: BLOG_SITEMAP_QUERY,
5425
+ metaObjects: METAOBJECT_SITEMAP_QUERY
5426
+ };
4956
5427
  //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartCreate
4957
5428
  //! @see https://shopify.dev/docs/api/storefront/latest/queries/cart
4958
5429
  //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesAdd
4959
5430
  //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesUpdate
4960
5431
  //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesRemove
4961
5432
  //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartDiscountCodesUpdate
5433
+ //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartGiftCardCodesUpdate
4962
5434
  //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartBuyerIdentityUpdate
4963
5435
  //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartNoteUpdate
4964
5436
  //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartSelectedDeliveryOptionsUpdate
@@ -5070,6 +5542,7 @@ exports.cartCreateDefault = cartCreateDefault;
5070
5542
  exports.cartDiscountCodesUpdateDefault = cartDiscountCodesUpdateDefault;
5071
5543
  exports.cartGetDefault = cartGetDefault;
5072
5544
  exports.cartGetIdDefault = cartGetIdDefault;
5545
+ exports.cartGiftCardCodesUpdateDefault = cartGiftCardCodesUpdateDefault;
5073
5546
  exports.cartLinesAddDefault = cartLinesAddDefault;
5074
5547
  exports.cartLinesRemoveDefault = cartLinesRemoveDefault;
5075
5548
  exports.cartLinesUpdateDefault = cartLinesUpdateDefault;
@@ -5094,6 +5567,8 @@ exports.getSeoMeta = getSeoMeta;
5094
5567
  exports.getShopAnalytics = getShopAnalytics;
5095
5568
  exports.graphiqlLoader = graphiqlLoader;
5096
5569
  exports.storefrontRedirect = storefrontRedirect;
5570
+ exports.unstable__getSitemap = getSitemap;
5571
+ exports.unstable__getSitemapIndex = getSitemapIndex;
5097
5572
  exports.useAnalytics = useAnalytics;
5098
5573
  exports.useCustomerPrivacy = useCustomerPrivacy;
5099
5574
  exports.useNonce = useNonce;