@shopify/hydrogen 2024.4.7 → 2024.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.
@@ -102,22 +102,6 @@ var init_log_seo_tags = __esm({
102
102
  }
103
103
  });
104
104
 
105
- // src/utils/hash.ts
106
- function hashKey(queryKey) {
107
- const rawKeys = Array.isArray(queryKey) ? queryKey : [queryKey];
108
- let hash = "";
109
- for (const key of rawKeys) {
110
- if (key != null) {
111
- if (typeof key === "object") {
112
- hash += JSON.stringify(key);
113
- } else {
114
- hash += key.toString();
115
- }
116
- }
117
- }
118
- return encodeURIComponent(hash);
119
- }
120
-
121
105
  // src/cache/strategies.ts
122
106
  var PUBLIC = "public";
123
107
  var PRIVATE = "private";
@@ -187,13 +171,11 @@ function CacheCustom(overrideOptions) {
187
171
 
188
172
  // src/utils/parse-json.ts
189
173
  function parseJSON(json) {
190
- if (String(json).includes("__proto__"))
191
- return JSON.parse(json, noproto);
174
+ if (String(json).includes("__proto__")) return JSON.parse(json, noproto);
192
175
  return JSON.parse(json);
193
176
  }
194
177
  function noproto(k, v) {
195
- if (k !== "__proto__")
196
- return v;
178
+ if (k !== "__proto__") return v;
197
179
  }
198
180
  function getCacheControlSetting(userCacheOptions, options) {
199
181
  if (userCacheOptions && options) {
@@ -209,8 +191,7 @@ function generateDefaultCacheControlHeader(userCacheOptions) {
209
191
  return generateCacheControlHeader(getCacheControlSetting(userCacheOptions));
210
192
  }
211
193
  async function getItem(cache, request) {
212
- if (!cache)
213
- return;
194
+ if (!cache) return;
214
195
  const response = await cache.match(request);
215
196
  if (!response) {
216
197
  return;
@@ -218,8 +199,7 @@ async function getItem(cache, request) {
218
199
  return response;
219
200
  }
220
201
  async function setItem(cache, request, response, userCacheOptions) {
221
- if (!cache)
222
- return;
202
+ if (!cache) return;
223
203
  const cacheControl = getCacheControlSetting(userCacheOptions);
224
204
  const paddedCacheControlString = generateDefaultCacheControlHeader(
225
205
  getCacheControlSetting(cacheControl, {
@@ -231,12 +211,11 @@ async function setItem(cache, request, response, userCacheOptions) {
231
211
  );
232
212
  response.headers.set("cache-control", paddedCacheControlString);
233
213
  response.headers.set("real-cache-control", cacheControlString);
234
- response.headers.set("cache-put-date", (/* @__PURE__ */ new Date()).toUTCString());
214
+ response.headers.set("cache-put-date", String(Date.now()));
235
215
  await cache.put(request, response);
236
216
  }
237
217
  async function deleteItem(cache, request) {
238
- if (!cache)
239
- return;
218
+ if (!cache) return;
240
219
  await cache.delete(request);
241
220
  }
242
221
  function calculateAge(response, responseDate) {
@@ -248,7 +227,7 @@ function calculateAge(response, responseDate) {
248
227
  responseMaxAge = parseFloat(maxAgeMatch[1]);
249
228
  }
250
229
  }
251
- const ageInMs = (/* @__PURE__ */ new Date()).valueOf() - new Date(responseDate).valueOf();
230
+ const ageInMs = Date.now() - Number(responseDate);
252
231
  return [ageInMs / 1e3, responseMaxAge];
253
232
  }
254
233
  function isStale(request, response) {
@@ -276,8 +255,7 @@ function getCacheOption(userCacheOptions) {
276
255
  return userCacheOptions || CacheDefault();
277
256
  }
278
257
  async function getItemFromCache(cache, key) {
279
- if (!cache)
280
- return;
258
+ if (!cache) return;
281
259
  const url = getKeyUrl(key);
282
260
  const request = new Request(url);
283
261
  const response = await CacheAPI.get(cache, request);
@@ -292,8 +270,7 @@ async function getItemFromCache(cache, key) {
292
270
  }
293
271
  }
294
272
  async function setItemInCache(cache, key, value, userCacheOptions) {
295
- if (!cache)
296
- return;
273
+ if (!cache) return;
297
274
  const url = getKeyUrl(key);
298
275
  const request = new Request(url);
299
276
  const response = new Response(JSON.stringify(value));
@@ -308,21 +285,23 @@ function isStale2(key, response) {
308
285
  return CacheAPI.isStale(new Request(getKeyUrl(key)), response);
309
286
  }
310
287
 
311
- // src/cache/fetch.ts
312
- function toSerializableResponse(body, response) {
313
- return [
314
- body,
315
- {
316
- status: response.status,
317
- statusText: response.statusText,
318
- headers: Array.from(response.headers.entries())
288
+ // src/utils/hash.ts
289
+ function hashKey(queryKey) {
290
+ const rawKeys = Array.isArray(queryKey) ? queryKey : [queryKey];
291
+ let hash = "";
292
+ for (const key of rawKeys) {
293
+ if (key != null) {
294
+ if (typeof key === "object") {
295
+ hash += JSON.stringify(key);
296
+ } else {
297
+ hash += key.toString();
298
+ }
319
299
  }
320
- ];
321
- }
322
- function fromSerializableResponse([body, init]) {
323
- return [body, new Response(body, init)];
300
+ }
301
+ return encodeURIComponent(hash);
324
302
  }
325
- var checkGraphQLErrors = (body, response) => !body?.errors && response.status < 400;
303
+
304
+ // src/cache/run-with-cache.ts
326
305
  var swrLock = /* @__PURE__ */ new Set();
327
306
  async function runWithCache(cacheKey, actionFn, {
328
307
  strategy = CacheShort(),
@@ -445,6 +424,22 @@ async function runWithCache(cacheKey, actionFn, {
445
424
  }
446
425
  return result;
447
426
  }
427
+
428
+ // src/cache/server-fetch.ts
429
+ function toSerializableResponse(body, response) {
430
+ return [
431
+ body,
432
+ {
433
+ status: response.status,
434
+ statusText: response.statusText,
435
+ headers: Array.from(response.headers.entries())
436
+ }
437
+ ];
438
+ }
439
+ function fromSerializableResponse([body, init]) {
440
+ return [body, new Response(body, init)];
441
+ }
442
+ var checkGraphQLErrors = (body, response) => !body?.errors && response.status < 400;
448
443
  async function fetchWithServerCache(url, requestInit, {
449
444
  cacheInstance,
450
445
  cache: cacheOptions,
@@ -483,20 +478,12 @@ async function fetchWithServerCache(url, requestInit, {
483
478
  ).then(fromSerializableResponse);
484
479
  }
485
480
 
486
- // src/version.ts
487
- var LIB_VERSION = "2024.4.7";
488
-
489
481
  // src/constants.ts
490
482
  var STOREFRONT_REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
491
483
  var STOREFRONT_ACCESS_TOKEN_HEADER = "X-Shopify-Storefront-Access-Token";
492
484
  var SDK_VARIANT_HEADER = "X-SDK-Variant";
493
485
  var SDK_VARIANT_SOURCE_HEADER = "X-SDK-Variant-Source";
494
486
  var SDK_VERSION_HEADER = "X-SDK-Version";
495
- var DEFAULT_CUSTOMER_API_VERSION = "2024-04";
496
- var USER_AGENT = `Shopify Hydrogen ${LIB_VERSION}`;
497
- var CUSTOMER_API_CLIENT_ID = "30243aa5-17c1-465a-8493-944bcc4e88aa";
498
- var CUSTOMER_ACCOUNT_SESSION_KEY = "customerAccount";
499
- var BUYER_SESSION_KEY = "buyer";
500
487
 
501
488
  // src/utils/uuid.ts
502
489
  function generateUUID() {
@@ -523,6 +510,9 @@ var errorOnce = (string) => {
523
510
  }
524
511
  };
525
512
 
513
+ // src/version.ts
514
+ var LIB_VERSION = "2024.7.2";
515
+
526
516
  // src/utils/graphql.ts
527
517
  function minifyQuery(string) {
528
518
  return string.replace(/\s*#.*$/gm, "").replace(/\s+/gm, " ").trim();
@@ -580,8 +570,7 @@ var GraphQLError = class extends Error {
580
570
  }
581
571
  });
582
572
  } catch {
583
- if (options.cause)
584
- this.cause = options.cause;
573
+ if (options.cause) this.cause = options.cause;
585
574
  }
586
575
  }
587
576
  get [Symbol.toStringTag]() {
@@ -625,12 +614,9 @@ var GraphQLError = class extends Error {
625
614
  {
626
615
  formatted.name = this.name;
627
616
  formatted.message = "Development: " + this.message;
628
- if (this.path)
629
- formatted.path = this.path;
630
- if (this.locations)
631
- formatted.locations = this.locations;
632
- if (this.extensions)
633
- formatted.extensions = this.extensions;
617
+ if (this.path) formatted.path = this.path;
618
+ if (this.locations) formatted.locations = this.locations;
619
+ if (this.extensions) formatted.extensions = this.extensions;
634
620
  }
635
621
  return formatted;
636
622
  }
@@ -671,15 +657,13 @@ function withSyncStack(promise, options = {}) {
671
657
  result.errors.forEach((error) => {
672
658
  if (error) {
673
659
  error.stack = getSyncStack(error.message, error.name);
674
- if (logErrors(error))
675
- console.error(error);
660
+ if (logErrors(error)) console.error(error);
676
661
  }
677
662
  });
678
663
  }
679
664
  return result;
680
665
  }).catch((error) => {
681
- if (error)
682
- error.stack = getSyncStack(error.message, error.name);
666
+ if (error) error.stack = getSyncStack(error.message, error.name);
683
667
  throw error;
684
668
  });
685
669
  }
@@ -733,8 +717,7 @@ function createStorefrontClient(options) {
733
717
  buyerIp: storefrontHeaders?.buyerIp || ""
734
718
  });
735
719
  defaultHeaders[STOREFRONT_REQUEST_GROUP_ID_HEADER] = storefrontHeaders?.requestGroupId || generateUUID();
736
- if (storefrontId)
737
- defaultHeaders[hydrogenReact.SHOPIFY_STOREFRONT_ID_HEADER] = storefrontId;
720
+ if (storefrontId) defaultHeaders[hydrogenReact.SHOPIFY_STOREFRONT_ID_HEADER] = storefrontId;
738
721
  defaultHeaders["user-agent"] = `Hydrogen ${LIB_VERSION}`;
739
722
  if (storefrontHeaders && storefrontHeaders.cookie) {
740
723
  const cookies = hydrogenReact.getShopifyCookies(storefrontHeaders.cookie ?? "");
@@ -927,7 +910,7 @@ function getDebugHeaders(request) {
927
910
  };
928
911
  }
929
912
 
930
- // src/with-cache.ts
913
+ // src/cache/create-with-cache.ts
931
914
  function createWithCache({
932
915
  cache,
933
916
  waitUntil,
@@ -981,8 +964,7 @@ var InMemoryCache = class {
981
964
  });
982
965
  }
983
966
  async match(request) {
984
- if (request.method !== "GET")
985
- return;
967
+ if (request.method !== "GET") return;
986
968
  const match = this.#store.get(request.url);
987
969
  if (!match) {
988
970
  return;
@@ -1032,8 +1014,7 @@ var InMemoryCache = class {
1032
1014
 
1033
1015
  // src/utils/get-redirect-url.ts
1034
1016
  function getRedirectUrl(requestUrl) {
1035
- if (!requestUrl)
1036
- return;
1017
+ if (!requestUrl) return;
1037
1018
  const { pathname, search } = new URL(requestUrl);
1038
1019
  const redirectFrom = pathname + search;
1039
1020
  const searchParams = new URLSearchParams(search);
@@ -1923,8 +1904,7 @@ function recursivelyInvokeOrReturn(value, ...rest) {
1923
1904
  function getSeoMeta(...seoInputs) {
1924
1905
  let tagResults = [];
1925
1906
  const dedupedSeoInput = seoInputs.reduce((acc, current) => {
1926
- if (!current)
1927
- return acc;
1907
+ if (!current) return acc;
1928
1908
  Object.keys(current).forEach(
1929
1909
  (key) => !current[key] && delete current[key]
1930
1910
  );
@@ -2310,6 +2290,13 @@ function getPaginationVariables(request, options = { pageBy: 20 }) {
2310
2290
  return variables;
2311
2291
  }
2312
2292
 
2293
+ // src/customer/constants.ts
2294
+ var DEFAULT_CUSTOMER_API_VERSION = "2024-07";
2295
+ var USER_AGENT = `Shopify Hydrogen ${LIB_VERSION}`;
2296
+ var CUSTOMER_API_CLIENT_ID = "30243aa5-17c1-465a-8493-944bcc4e88aa";
2297
+ var CUSTOMER_ACCOUNT_SESSION_KEY = "customerAccount";
2298
+ var BUYER_SESSION_KEY = "buyer";
2299
+
2313
2300
  // src/customer/BadRequest.ts
2314
2301
  var BadRequest = class extends Response {
2315
2302
  constructor(message, helpMessage, headers) {
@@ -2395,7 +2382,11 @@ async function refreshToken({
2395
2382
  }
2396
2383
  });
2397
2384
  }
2398
- const { access_token, expires_in, refresh_token } = await response.json();
2385
+ const {
2386
+ access_token,
2387
+ expires_in,
2388
+ refresh_token
2389
+ } = await response.json();
2399
2390
  const accessToken = await exchangeAccessToken(
2400
2391
  access_token,
2401
2392
  customerAccountId,
@@ -2446,10 +2437,7 @@ async function checkExpires({
2446
2437
  } else {
2447
2438
  throw new BadRequest(
2448
2439
  "Unauthorized",
2449
- "Login before querying the Customer Account API.",
2450
- {
2451
- "Set-Cookie": await session.commit()
2452
- }
2440
+ "Login before querying the Customer Account API."
2453
2441
  );
2454
2442
  }
2455
2443
  }
@@ -2564,8 +2552,7 @@ var DEFAULT_LOGIN_URL = "/account/login";
2564
2552
  var DEFAULT_AUTH_URL = "/account/authorize";
2565
2553
  var DEFAULT_REDIRECT_PATH = "/account";
2566
2554
  function defaultAuthStatusHandler(request) {
2567
- if (!request.url)
2568
- return DEFAULT_LOGIN_URL;
2555
+ if (!request.url) return DEFAULT_LOGIN_URL;
2569
2556
  const { pathname } = new URL(request.url);
2570
2557
  const redirectTo = DEFAULT_LOGIN_URL + `?${new URLSearchParams({ return_to: pathname }).toString()}`;
2571
2558
  return redirect(redirectTo);
@@ -2647,9 +2634,6 @@ function createCustomerAccountClient({
2647
2634
  if (response.status === 401) {
2648
2635
  clearSession(session);
2649
2636
  const authFailResponse = authStatusHandler();
2650
- if (authFailResponse instanceof Response) {
2651
- authFailResponse.headers.set("Set-Cookie", await session.commit());
2652
- }
2653
2637
  throw authFailResponse;
2654
2638
  }
2655
2639
  let errors2;
@@ -2678,13 +2662,11 @@ function createCustomerAccountClient({
2678
2662
  }
2679
2663
  }
2680
2664
  async function isLoggedIn() {
2681
- if (!customerAccountUrl || !customerAccountId)
2682
- return false;
2665
+ if (!customerAccountUrl || !customerAccountId) return false;
2683
2666
  const customerAccount = session.get(CUSTOMER_ACCOUNT_SESSION_KEY);
2684
2667
  const accessToken = customerAccount?.accessToken;
2685
2668
  const expiresAt = customerAccount?.expiresAt;
2686
- if (!accessToken || !expiresAt)
2687
- return false;
2669
+ if (!accessToken || !expiresAt) return false;
2688
2670
  const stackInfo = getCallerStackLine?.();
2689
2671
  try {
2690
2672
  await checkExpires({
@@ -2800,11 +2782,7 @@ function createCustomerAccountClient({
2800
2782
  });
2801
2783
  loginUrl.searchParams.append("code_challenge", challenge);
2802
2784
  loginUrl.searchParams.append("code_challenge_method", "S256");
2803
- return redirect(loginUrl.toString(), {
2804
- headers: {
2805
- "Set-Cookie": await session.commit()
2806
- }
2807
- });
2785
+ return redirect(loginUrl.toString());
2808
2786
  },
2809
2787
  logout: async (options) => {
2810
2788
  ifInvalidCredentialThrowError(customerAccountUrl, customerAccountId);
@@ -2821,11 +2799,7 @@ function createCustomerAccountClient({
2821
2799
  ]).toString()}`
2822
2800
  ).toString() : postLogoutRedirectUri;
2823
2801
  clearSession(session);
2824
- return redirect(logoutUrl, {
2825
- headers: {
2826
- "Set-Cookie": await session.commit()
2827
- }
2828
- });
2802
+ return redirect(logoutUrl);
2829
2803
  },
2830
2804
  isLoggedIn,
2831
2805
  handleAuthStatus,
@@ -2841,20 +2815,14 @@ function createCustomerAccountClient({
2841
2815
  clearSession(session);
2842
2816
  throw new BadRequest(
2843
2817
  "Unauthorized",
2844
- "No code or state parameter found in the redirect URL.",
2845
- {
2846
- "Set-Cookie": await session.commit()
2847
- }
2818
+ "No code or state parameter found in the redirect URL."
2848
2819
  );
2849
2820
  }
2850
2821
  if (session.get(CUSTOMER_ACCOUNT_SESSION_KEY)?.state !== state) {
2851
2822
  clearSession(session);
2852
2823
  throw new BadRequest(
2853
2824
  "Unauthorized",
2854
- "The session state does not match the state parameter. Make sure that the session is configured correctly and passed to `createCustomerAccountClient`.",
2855
- {
2856
- "Set-Cookie": await session.commit()
2857
- }
2825
+ "The session state does not match the state parameter. Make sure that the session is configured correctly and passed to `createCustomerAccountClient`."
2858
2826
  );
2859
2827
  }
2860
2828
  const clientId = customerAccountId;
@@ -2902,7 +2870,12 @@ function createCustomerAccountClient({
2902
2870
  }
2903
2871
  });
2904
2872
  }
2905
- const { access_token, expires_in, id_token, refresh_token } = await response.json();
2873
+ const {
2874
+ access_token,
2875
+ expires_in,
2876
+ id_token,
2877
+ refresh_token
2878
+ } = await response.json();
2906
2879
  const sessionNonce = session.get(CUSTOMER_ACCOUNT_SESSION_KEY)?.nonce;
2907
2880
  const responseNonce = await getNonce(id_token);
2908
2881
  if (sessionNonce !== responseNonce) {
@@ -2932,11 +2905,7 @@ function createCustomerAccountClient({
2932
2905
  idToken: id_token
2933
2906
  });
2934
2907
  await exchangeForStorefrontCustomerAccessToken();
2935
- return redirect(redirectPath || DEFAULT_REDIRECT_PATH, {
2936
- headers: {
2937
- "Set-Cookie": await session.commit()
2938
- }
2939
- });
2908
+ return redirect(redirectPath || DEFAULT_REDIRECT_PATH);
2940
2909
  },
2941
2910
  UNSTABLE_setBuyer: setBuyer,
2942
2911
  UNSTABLE_getBuyer: getBuyer
@@ -2944,8 +2913,7 @@ function createCustomerAccountClient({
2944
2913
  }
2945
2914
  function ifInvalidCredentialThrowError(customerAccountUrl, customerAccountId) {
2946
2915
  try {
2947
- if (!customerAccountUrl || !customerAccountId)
2948
- throw Error();
2916
+ if (!customerAccountUrl || !customerAccountId) throw Error();
2949
2917
  new URL(customerAccountUrl);
2950
2918
  } catch {
2951
2919
  console.error(
@@ -3087,32 +3055,22 @@ function cartGetDefault({
3087
3055
  }) {
3088
3056
  return async (cartInput) => {
3089
3057
  const cartId = getCartId();
3090
- if (!cartId)
3091
- return null;
3058
+ if (!cartId) return null;
3092
3059
  const [isCustomerLoggedIn, { cart, errors: errors2 }] = await Promise.all([
3093
3060
  customerAccount ? customerAccount.isLoggedIn() : false,
3094
3061
  storefront.query(CART_QUERY(cartFragment), {
3095
- variables: {
3096
- cartId,
3097
- ...cartInput
3098
- },
3062
+ variables: { cartId, ...cartInput },
3099
3063
  cache: storefront.CacheNone()
3100
3064
  })
3101
3065
  ]);
3102
- return formatAPIResult(
3103
- addCustomerLoggedInParam(isCustomerLoggedIn, cart),
3104
- errors2
3105
- );
3066
+ if (isCustomerLoggedIn && cart?.checkoutUrl) {
3067
+ const finalCheckoutUrl = new URL(cart.checkoutUrl);
3068
+ finalCheckoutUrl.searchParams.set("logged_in", "true");
3069
+ cart.checkoutUrl = finalCheckoutUrl.toString();
3070
+ }
3071
+ return cart || errors2 ? formatAPIResult(cart, errors2) : null;
3106
3072
  };
3107
3073
  }
3108
- function addCustomerLoggedInParam(isCustomerLoggedIn, cart) {
3109
- if (isCustomerLoggedIn && cart && cart.checkoutUrl) {
3110
- const finalCheckoutUrl = new URL(cart.checkoutUrl);
3111
- finalCheckoutUrl.searchParams.set("logged_in", "true");
3112
- cart.checkoutUrl = finalCheckoutUrl.toString();
3113
- }
3114
- return cart;
3115
- }
3116
3074
  var CART_QUERY = (cartFragment = DEFAULT_CART_FRAGMENT) => `#graphql
3117
3075
  query CartQuery(
3118
3076
  $cartId: ID!
@@ -3718,14 +3676,12 @@ function createCartHandler(options) {
3718
3676
  }
3719
3677
  function useOptimisticCart(cart) {
3720
3678
  const fetchers = react$1.useFetchers();
3721
- if (!fetchers || !fetchers.length)
3722
- return cart;
3679
+ if (!fetchers || !fetchers.length) return cart;
3723
3680
  const optimisticCart = cart?.lines ? structuredClone(cart) : { lines: { nodes: [] } };
3724
3681
  const cartLines = optimisticCart.lines.nodes;
3725
3682
  let isOptimistic = false;
3726
3683
  for (const { formData } of fetchers) {
3727
- if (!formData)
3728
- continue;
3684
+ if (!formData) continue;
3729
3685
  const cartFormData = CartForm.getFormInput(formData);
3730
3686
  if (cartFormData.action === CartForm.ACTIONS.LinesAdd) {
3731
3687
  for (const input of cartFormData.inputs.lines) {
@@ -3804,12 +3760,14 @@ function VariantSelector({
3804
3760
  options = [],
3805
3761
  variants: _variants = [],
3806
3762
  productPath = "products",
3763
+ waitForNavigation = false,
3807
3764
  children
3808
3765
  }) {
3809
3766
  const variants = _variants instanceof Array ? _variants : hydrogenReact.flattenConnection(_variants);
3810
3767
  const { searchParams, path, alreadyOnProductPage } = useVariantPath(
3811
3768
  handle,
3812
- productPath
3769
+ productPath,
3770
+ waitForNavigation
3813
3771
  );
3814
3772
  const optionsWithOnlyOneValue = options.filter(
3815
3773
  (option) => option?.values?.length === 1
@@ -3818,7 +3776,7 @@ function VariantSelector({
3818
3776
  react.Fragment,
3819
3777
  null,
3820
3778
  ...react.useMemo(() => {
3821
- return options.filter((option) => option?.values?.length > 1).map((option) => {
3779
+ return options.map((option) => {
3822
3780
  let activeValue;
3823
3781
  let availableValues = [];
3824
3782
  for (let value of option.values) {
@@ -3873,14 +3831,21 @@ var getSelectedProductOptions = (request) => {
3873
3831
  });
3874
3832
  return selectedOptions;
3875
3833
  };
3876
- function useVariantPath(handle, productPath) {
3834
+ function useVariantPath(handle, productPath, waitForNavigation) {
3877
3835
  const { pathname, search } = react$1.useLocation();
3836
+ const navigation = react$1.useNavigation();
3878
3837
  return react.useMemo(() => {
3879
3838
  const match = /(\/[a-zA-Z]{2}-[a-zA-Z]{2}\/)/g.exec(pathname);
3880
3839
  const isLocalePathname = match && match.length > 0;
3881
3840
  productPath = productPath.startsWith("/") ? productPath.substring(1) : productPath;
3882
3841
  const path = isLocalePathname ? `${match[0]}${productPath}/${handle}` : `/${productPath}/${handle}`;
3883
- const searchParams = new URLSearchParams(search);
3842
+ const searchParams = new URLSearchParams(
3843
+ // Remix doesn't update the location until pending loaders complete.
3844
+ // By default we use the destination search params to make selecting a variant
3845
+ // instant, but `waitForNavigation` makes the UI wait to update by only using
3846
+ // the active browser search params.
3847
+ waitForNavigation || navigation.state !== "loading" ? search : navigation.location.search
3848
+ );
3884
3849
  return {
3885
3850
  searchParams,
3886
3851
  // If the current pathname matches the product page, we need to make sure
@@ -3889,7 +3854,56 @@ function useVariantPath(handle, productPath) {
3889
3854
  alreadyOnProductPage: path === pathname,
3890
3855
  path
3891
3856
  };
3892
- }, [pathname, search, handle, productPath]);
3857
+ }, [pathname, search, waitForNavigation, handle, productPath, navigation]);
3858
+ }
3859
+ function useOptimisticVariant(selectedVariant, variants) {
3860
+ const navigation = react$1.useNavigation();
3861
+ const [resolvedVariants, setResolvedVariants] = react.useState([]);
3862
+ react.useEffect(() => {
3863
+ Promise.resolve(variants).then((productWithVariants) => {
3864
+ if (productWithVariants) {
3865
+ setResolvedVariants(
3866
+ productWithVariants instanceof Array ? productWithVariants : productWithVariants.product?.variants?.nodes || []
3867
+ );
3868
+ }
3869
+ }).catch((error) => {
3870
+ reportError(
3871
+ new Error(
3872
+ "[h2:error:useOptimisticVariant] An error occurred while resolving the variants for the optimistic product hook.",
3873
+ {
3874
+ cause: error
3875
+ }
3876
+ )
3877
+ );
3878
+ });
3879
+ }, [variants]);
3880
+ if (navigation.state === "loading") {
3881
+ const queryParams = new URLSearchParams(navigation.location.search);
3882
+ let reportedError = false;
3883
+ const matchingVariant = resolvedVariants.find((variant) => {
3884
+ if (!variant.selectedOptions) {
3885
+ if (!reportedError) {
3886
+ reportedError = true;
3887
+ reportError(
3888
+ new Error(
3889
+ "[h2:error:useOptimisticVariant] The optimistic product hook requires your product query to include variants with the selectedOptions field."
3890
+ )
3891
+ );
3892
+ }
3893
+ return false;
3894
+ }
3895
+ return variant.selectedOptions.every((option) => {
3896
+ return queryParams.get(option.name) === option.value;
3897
+ });
3898
+ });
3899
+ if (matchingVariant) {
3900
+ return {
3901
+ ...matchingVariant,
3902
+ isOptimistic: true
3903
+ };
3904
+ }
3905
+ }
3906
+ return selectedVariant;
3893
3907
  }
3894
3908
  var NonceContext = react.createContext(void 0);
3895
3909
  var NonceProvider = NonceContext.Provider;
@@ -4034,8 +4048,7 @@ function AnalyticsView(props) {
4034
4048
  shop
4035
4049
  };
4036
4050
  react.useEffect(() => {
4037
- if (!shop?.shopId)
4038
- return;
4051
+ if (!shop?.shopId) return;
4039
4052
  viewPayload2 = {
4040
4053
  ...viewPayload2,
4041
4054
  url: window.location.href
@@ -4119,14 +4132,11 @@ function useCustomerPrivacy(props) {
4119
4132
  };
4120
4133
  }, [onVisitorConsentCollected]);
4121
4134
  react.useEffect(() => {
4122
- if (scriptStatus !== "done" || loadedEvent.current)
4123
- return;
4135
+ if (scriptStatus !== "done" || loadedEvent.current) return;
4124
4136
  loadedEvent.current = true;
4125
4137
  const { checkoutDomain, storefrontAccessToken } = consentConfig;
4126
- if (!checkoutDomain)
4127
- logMissingConfig("checkoutDomain");
4128
- if (!storefrontAccessToken)
4129
- logMissingConfig("storefrontAccessToken");
4138
+ if (!checkoutDomain) logMissingConfig("checkoutDomain");
4139
+ if (!storefrontAccessToken) logMissingConfig("storefrontAccessToken");
4130
4140
  if (storefrontAccessToken.startsWith("shpat_") || storefrontAccessToken.length !== 32) {
4131
4141
  console.error(
4132
4142
  `[h2:error:useCustomerPrivacy] It looks like you passed a private access token, make sure to use the public token`
@@ -4154,8 +4164,7 @@ function useCustomerPrivacy(props) {
4154
4164
  if (withPrivacyBanner && window?.privacyBanner) {
4155
4165
  window.privacyBanner?.loadBanner(config);
4156
4166
  }
4157
- if (!window.Shopify?.customerPrivacy)
4158
- return;
4167
+ if (!window.Shopify?.customerPrivacy) return;
4159
4168
  const originalSetTrackingConsent = window.Shopify.customerPrivacy.setTrackingConsent;
4160
4169
  function overrideSetTrackingConsent(consent, callback) {
4161
4170
  originalSetTrackingConsent(
@@ -4270,11 +4279,9 @@ function prepareBasePageViewPayload(payload) {
4270
4279
  return eventPayload;
4271
4280
  }
4272
4281
  function prepareBaseCartPayload(payload, cart) {
4273
- if (cart === null)
4274
- return;
4282
+ if (cart === null) return;
4275
4283
  const pageViewPayload = prepareBasePageViewPayload(payload);
4276
- if (!pageViewPayload)
4277
- return;
4284
+ if (!pageViewPayload) return;
4278
4285
  const eventPayload = {
4279
4286
  ...pageViewPayload,
4280
4287
  cartId: cart.id
@@ -4284,8 +4291,7 @@ function prepareBaseCartPayload(payload, cart) {
4284
4291
  var viewPayload = {};
4285
4292
  function pageViewHandler(payload) {
4286
4293
  const eventPayload = prepareBasePageViewPayload(payload);
4287
- if (!eventPayload)
4288
- return;
4294
+ if (!eventPayload) return;
4289
4295
  hydrogenReact.sendShopifyAnalytics({
4290
4296
  eventName: hydrogenReact.AnalyticsEventName.PAGE_VIEW_2,
4291
4297
  payload: {
@@ -4319,8 +4325,7 @@ function productViewHandler(payload) {
4319
4325
  }
4320
4326
  function collectionViewHandler(payload) {
4321
4327
  let eventPayload = prepareBasePageViewPayload(payload);
4322
- if (!eventPayload)
4323
- return;
4328
+ if (!eventPayload) return;
4324
4329
  viewPayload = {
4325
4330
  pageType: hydrogenReact.AnalyticsPageType.collection,
4326
4331
  resourceId: payload.collection.id
@@ -4337,8 +4342,7 @@ function collectionViewHandler(payload) {
4337
4342
  }
4338
4343
  function searchViewHandler(payload) {
4339
4344
  let eventPayload = prepareBasePageViewPayload(payload);
4340
- if (!eventPayload)
4341
- return;
4345
+ if (!eventPayload) return;
4342
4346
  viewPayload = {
4343
4347
  pageType: hydrogenReact.AnalyticsPageType.search
4344
4348
  };
@@ -4355,8 +4359,7 @@ function searchViewHandler(payload) {
4355
4359
  function productAddedToCartHandler(payload) {
4356
4360
  const { cart, currentLine } = payload;
4357
4361
  const eventPayload = prepareBaseCartPayload(payload, cart);
4358
- if (!eventPayload || !currentLine?.id)
4359
- return;
4362
+ if (!eventPayload || !currentLine?.id) return;
4360
4363
  sendCartAnalytics({
4361
4364
  matchedLine: currentLine,
4362
4365
  eventPayload
@@ -4451,10 +4454,8 @@ function formatProduct(products) {
4451
4454
  quantity: product.quantity || 1,
4452
4455
  category: product.productType
4453
4456
  };
4454
- if (product.sku)
4455
- formattedProduct.sku = product.sku;
4456
- if (product.productType)
4457
- formattedProduct.category = product.productType;
4457
+ if (product.sku) formattedProduct.sku = product.sku;
4458
+ if (product.productType) formattedProduct.category = product.productType;
4458
4459
  return formattedProduct;
4459
4460
  });
4460
4461
  }
@@ -4470,8 +4471,7 @@ function CartAnalytics({
4470
4471
  const { publish: publish2, shop, customData, canTrack, cart, prevCart } = useAnalytics();
4471
4472
  const lastEventId = react.useRef(null);
4472
4473
  react.useEffect(() => {
4473
- if (!currentCart)
4474
- return;
4474
+ if (!currentCart) return;
4475
4475
  Promise.resolve(currentCart).then((updatedCart) => {
4476
4476
  if (updatedCart && updatedCart.lines) {
4477
4477
  if (!updatedCart.id) {
@@ -4484,19 +4484,15 @@ function CartAnalytics({
4484
4484
  }
4485
4485
  }
4486
4486
  setCarts(({ cart: cart2, prevCart: prevCart2 }) => {
4487
- if (updatedCart?.updatedAt !== cart2?.updatedAt)
4488
- return { cart: updatedCart, prevCart: cart2 };
4489
- return { cart: cart2, prevCart: prevCart2 };
4487
+ return updatedCart?.updatedAt !== cart2?.updatedAt ? { cart: updatedCart, prevCart: cart2 } : { cart: cart2, prevCart: prevCart2 };
4490
4488
  });
4491
4489
  });
4492
4490
  return () => {
4493
4491
  };
4494
4492
  }, [setCarts, currentCart]);
4495
4493
  react.useEffect(() => {
4496
- if (!cart || !cart?.updatedAt)
4497
- return;
4498
- if (cart?.updatedAt === prevCart?.updatedAt)
4499
- return;
4494
+ if (!cart || !cart?.updatedAt) return;
4495
+ if (cart?.updatedAt === prevCart?.updatedAt) return;
4500
4496
  let cartLastUpdatedAt;
4501
4497
  try {
4502
4498
  cartLastUpdatedAt = JSON.parse(
@@ -4514,8 +4510,7 @@ function CartAnalytics({
4514
4510
  shop,
4515
4511
  customData
4516
4512
  };
4517
- if (cart.updatedAt === lastEventId.current)
4518
- return;
4513
+ if (cart.updatedAt === lastEventId.current) return;
4519
4514
  lastEventId.current = cart.updatedAt;
4520
4515
  publish2("cart_updated", payload);
4521
4516
  localStorage.setItem(
@@ -4567,6 +4562,44 @@ function CartAnalytics({
4567
4562
  }, [cart, prevCart, publish2, shop, customData, canTrack]);
4568
4563
  return null;
4569
4564
  }
4565
+ var PERF_KIT_UNSTABLE = "https://cdn.shopify.com/shopifycloud/perf-kit/shopify-perf-kit-1bd852a.min.js";
4566
+ function PerfKit({ shop }) {
4567
+ const loadedEvent = react.useRef(false);
4568
+ const { subscribe: subscribe2, register: register2 } = useAnalytics();
4569
+ const { ready } = register2("Internal_Shopify_Perf_Kit");
4570
+ const scriptStatus = hydrogenReact.useLoadScript(PERF_KIT_UNSTABLE, {
4571
+ attributes: {
4572
+ id: "perfkit",
4573
+ "data-application": "hydrogen",
4574
+ "data-shop-id": hydrogenReact.parseGid(shop.shopId).id.toString(),
4575
+ "data-storefront-id": shop.hydrogenSubchannelId,
4576
+ "data-monorail-region": "global",
4577
+ "data-spa-mode": "true",
4578
+ "data-resource-timing-sampling-rate": "100"
4579
+ }
4580
+ });
4581
+ react.useEffect(() => {
4582
+ if (scriptStatus !== "done" || loadedEvent.current) return;
4583
+ loadedEvent.current = true;
4584
+ subscribe2(AnalyticsEvent.PAGE_VIEWED, () => {
4585
+ window.PerfKit?.navigate();
4586
+ });
4587
+ subscribe2(AnalyticsEvent.PRODUCT_VIEWED, () => {
4588
+ window.PerfKit?.setPageType("product");
4589
+ });
4590
+ subscribe2(AnalyticsEvent.COLLECTION_VIEWED, () => {
4591
+ window.PerfKit?.setPageType("collection");
4592
+ });
4593
+ subscribe2(AnalyticsEvent.SEARCH_VIEWED, () => {
4594
+ window.PerfKit?.setPageType("search");
4595
+ });
4596
+ subscribe2(AnalyticsEvent.CART_VIEWED, () => {
4597
+ window.PerfKit?.setPageType("cart");
4598
+ });
4599
+ ready();
4600
+ }, [subscribe2, ready, scriptStatus]);
4601
+ return null;
4602
+ }
4570
4603
  var defaultAnalyticsContext = {
4571
4604
  canTrack: () => false,
4572
4605
  cart: null,
@@ -4728,7 +4761,8 @@ function AnalyticsProvider({
4728
4761
  },
4729
4762
  domain: cookieDomain
4730
4763
  }
4731
- )
4764
+ ),
4765
+ !!shop && /* @__PURE__ */ jsxRuntime.jsx(PerfKit, { shop })
4732
4766
  ] });
4733
4767
  }
4734
4768
  function useAnalytics() {
@@ -4823,89 +4857,89 @@ var RichText = function(props) {
4823
4857
  //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartNoteUpdate
4824
4858
  //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartSelectedDeliveryOptionsUpdate
4825
4859
  //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartMetafieldsSet
4826
- //! @see https://shopify.dev/docs/api/storefront/2024-04/mutations/cartMetafieldDelete
4860
+ //! @see https://shopify.dev/docs/api/storefront/2024-07/mutations/cartMetafieldDelete
4827
4861
 
4828
- Object.defineProperty(exports, 'AnalyticsEventName', {
4862
+ Object.defineProperty(exports, "AnalyticsEventName", {
4829
4863
  enumerable: true,
4830
4864
  get: function () { return hydrogenReact.AnalyticsEventName; }
4831
4865
  });
4832
- Object.defineProperty(exports, 'AnalyticsPageType', {
4866
+ Object.defineProperty(exports, "AnalyticsPageType", {
4833
4867
  enumerable: true,
4834
4868
  get: function () { return hydrogenReact.AnalyticsPageType; }
4835
4869
  });
4836
- Object.defineProperty(exports, 'ExternalVideo', {
4870
+ Object.defineProperty(exports, "ExternalVideo", {
4837
4871
  enumerable: true,
4838
4872
  get: function () { return hydrogenReact.ExternalVideo; }
4839
4873
  });
4840
- Object.defineProperty(exports, 'IMAGE_FRAGMENT', {
4874
+ Object.defineProperty(exports, "IMAGE_FRAGMENT", {
4841
4875
  enumerable: true,
4842
4876
  get: function () { return hydrogenReact.IMAGE_FRAGMENT; }
4843
4877
  });
4844
- Object.defineProperty(exports, 'Image', {
4878
+ Object.defineProperty(exports, "Image", {
4845
4879
  enumerable: true,
4846
4880
  get: function () { return hydrogenReact.Image; }
4847
4881
  });
4848
- Object.defineProperty(exports, 'MediaFile', {
4882
+ Object.defineProperty(exports, "MediaFile", {
4849
4883
  enumerable: true,
4850
4884
  get: function () { return hydrogenReact.MediaFile; }
4851
4885
  });
4852
- Object.defineProperty(exports, 'ModelViewer', {
4886
+ Object.defineProperty(exports, "ModelViewer", {
4853
4887
  enumerable: true,
4854
4888
  get: function () { return hydrogenReact.ModelViewer; }
4855
4889
  });
4856
- Object.defineProperty(exports, 'Money', {
4890
+ Object.defineProperty(exports, "Money", {
4857
4891
  enumerable: true,
4858
4892
  get: function () { return hydrogenReact.Money; }
4859
4893
  });
4860
- Object.defineProperty(exports, 'ShopifySalesChannel', {
4894
+ Object.defineProperty(exports, "ShopifySalesChannel", {
4861
4895
  enumerable: true,
4862
4896
  get: function () { return hydrogenReact.ShopifySalesChannel; }
4863
4897
  });
4864
- Object.defineProperty(exports, 'Video', {
4898
+ Object.defineProperty(exports, "Video", {
4865
4899
  enumerable: true,
4866
4900
  get: function () { return hydrogenReact.Video; }
4867
4901
  });
4868
- Object.defineProperty(exports, 'customerAccountApiCustomScalars', {
4902
+ Object.defineProperty(exports, "customerAccountApiCustomScalars", {
4869
4903
  enumerable: true,
4870
4904
  get: function () { return hydrogenReact.customerAccountApiCustomScalars; }
4871
4905
  });
4872
- Object.defineProperty(exports, 'flattenConnection', {
4906
+ Object.defineProperty(exports, "flattenConnection", {
4873
4907
  enumerable: true,
4874
4908
  get: function () { return hydrogenReact.flattenConnection; }
4875
4909
  });
4876
- Object.defineProperty(exports, 'getClientBrowserParameters', {
4910
+ Object.defineProperty(exports, "getClientBrowserParameters", {
4877
4911
  enumerable: true,
4878
4912
  get: function () { return hydrogenReact.getClientBrowserParameters; }
4879
4913
  });
4880
- Object.defineProperty(exports, 'getShopifyCookies', {
4914
+ Object.defineProperty(exports, "getShopifyCookies", {
4881
4915
  enumerable: true,
4882
4916
  get: function () { return hydrogenReact.getShopifyCookies; }
4883
4917
  });
4884
- Object.defineProperty(exports, 'parseGid', {
4918
+ Object.defineProperty(exports, "parseGid", {
4885
4919
  enumerable: true,
4886
4920
  get: function () { return hydrogenReact.parseGid; }
4887
4921
  });
4888
- Object.defineProperty(exports, 'parseMetafield', {
4922
+ Object.defineProperty(exports, "parseMetafield", {
4889
4923
  enumerable: true,
4890
4924
  get: function () { return hydrogenReact.parseMetafield; }
4891
4925
  });
4892
- Object.defineProperty(exports, 'sendShopifyAnalytics', {
4926
+ Object.defineProperty(exports, "sendShopifyAnalytics", {
4893
4927
  enumerable: true,
4894
4928
  get: function () { return hydrogenReact.sendShopifyAnalytics; }
4895
4929
  });
4896
- Object.defineProperty(exports, 'storefrontApiCustomScalars', {
4930
+ Object.defineProperty(exports, "storefrontApiCustomScalars", {
4897
4931
  enumerable: true,
4898
4932
  get: function () { return hydrogenReact.storefrontApiCustomScalars; }
4899
4933
  });
4900
- Object.defineProperty(exports, 'useLoadScript', {
4934
+ Object.defineProperty(exports, "useLoadScript", {
4901
4935
  enumerable: true,
4902
4936
  get: function () { return hydrogenReact.useLoadScript; }
4903
4937
  });
4904
- Object.defineProperty(exports, 'useMoney', {
4938
+ Object.defineProperty(exports, "useMoney", {
4905
4939
  enumerable: true,
4906
4940
  get: function () { return hydrogenReact.useMoney; }
4907
4941
  });
4908
- Object.defineProperty(exports, 'useShopifyCookies', {
4942
+ Object.defineProperty(exports, "useShopifyCookies", {
4909
4943
  enumerable: true,
4910
4944
  get: function () { return hydrogenReact.useShopifyCookies; }
4911
4945
  });
@@ -4958,5 +4992,6 @@ exports.useCustomerPrivacy = useCustomerPrivacy;
4958
4992
  exports.useNonce = useNonce;
4959
4993
  exports.useOptimisticCart = useOptimisticCart;
4960
4994
  exports.useOptimisticData = useOptimisticData;
4995
+ exports.useOptimisticVariant = useOptimisticVariant;
4961
4996
  //# sourceMappingURL=out.js.map
4962
4997
  //# sourceMappingURL=index.cjs.map