@shopify/hydrogen 2026.4.1 → 2026.4.3

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.
@@ -613,7 +613,7 @@ function getPrivacyBanner() {
613
613
  }
614
614
 
615
615
  // package.json
616
- var version = "2026.4.1";
616
+ var version = "2026.4.3";
617
617
 
618
618
  // src/analytics-manager/ShopifyAnalytics.tsx
619
619
  function getCustomerPrivacyRequired() {
@@ -2009,7 +2009,7 @@ function generateUUID() {
2009
2009
  }
2010
2010
 
2011
2011
  // src/version.ts
2012
- var LIB_VERSION = "2026.4.1";
2012
+ var LIB_VERSION = "2026.4.3";
2013
2013
 
2014
2014
  // src/utils/graphql.ts
2015
2015
  function minifyQuery(string) {
@@ -2559,7 +2559,7 @@ function cartGetDefault({
2559
2559
  cartFragment
2560
2560
  }) {
2561
2561
  return async (cartInput) => {
2562
- const cartId = getCartId();
2562
+ const cartId = cartInput?.cartId ?? getCartId();
2563
2563
  if (!cartId) return null;
2564
2564
  const includeVisitorConsent = shouldIncludeVisitorConsent(cartInput);
2565
2565
  const [isCustomerLoggedIn, { cart, errors: errors2 }] = await Promise.all([
@@ -2572,10 +2572,10 @@ function cartGetDefault({
2572
2572
  }
2573
2573
  )
2574
2574
  ]);
2575
- if (isCustomerLoggedIn && cart?.checkoutUrl) {
2575
+ if (isCustomerLoggedIn && cart && typeof cart === "object" && "checkoutUrl" in cart && typeof cart.checkoutUrl === "string") {
2576
2576
  const finalCheckoutUrl = new URL(cart.checkoutUrl);
2577
2577
  finalCheckoutUrl.searchParams.set("logged_in", "true");
2578
- cart.checkoutUrl = finalCheckoutUrl.toString();
2578
+ Object.assign(cart, { checkoutUrl: finalCheckoutUrl.toString() });
2579
2579
  }
2580
2580
  return cart || errors2 ? formatAPIResult(cart, errors2) : null;
2581
2581
  };
@@ -3528,7 +3528,12 @@ function createCartHandler(options) {
3528
3528
  ...args[0].buyerIdentity
3529
3529
  };
3530
3530
  const result = await _cartCreate(...args);
3531
- cartId = result?.cart?.id;
3531
+ if (result?.cart && (typeof result.cart !== "object" || !("id" in result.cart) || typeof result.cart.id !== "string")) {
3532
+ throw new Error(
3533
+ "[h2:error:createCartHandler] Cart created but response is missing a valid `id` field. Ensure your cart query fragment includes the `id` field."
3534
+ );
3535
+ }
3536
+ cartId = result?.cart && typeof result.cart === "object" && "id" in result.cart && typeof result.cart.id === "string" ? result.cart.id : void 0;
3532
3537
  return result;
3533
3538
  };
3534
3539
  const methods = {
@@ -3576,7 +3581,10 @@ function createCartHandler(options) {
3576
3581
  ) : await cartCreate({ buyerIdentity: buyerIdentity2 }, optionalParams);
3577
3582
  },
3578
3583
  updateNote: async (note, optionalParams) => {
3579
- return cartId || optionalParams?.cartId ? await cartNoteUpdateDefault(mutateOptions)(note, optionalParams) : await cartCreate({ note }, optionalParams);
3584
+ return cartId || optionalParams?.cartId ? await cartNoteUpdateDefault(mutateOptions)(
3585
+ note,
3586
+ optionalParams
3587
+ ) : await cartCreate({ note }, optionalParams);
3580
3588
  },
3581
3589
  updateSelectedDeliveryOption: cartSelectedDeliveryOptionsUpdateDefault(mutateOptions),
3582
3590
  updateAttributes: async (attributes, optionalParams) => {
@@ -4581,7 +4589,9 @@ function createHydrogenContext(options, additionalContext) {
4581
4589
  setCartId: cartOptions.setId || cartSetIdDefault(),
4582
4590
  cartQueryFragment: cartOptions.queryFragment,
4583
4591
  cartMutateFragment: cartOptions.mutateFragment,
4584
- customMethods: cartOptions.customMethods,
4592
+ ...cartOptions.customMethods && {
4593
+ customMethods: cartOptions.customMethods
4594
+ },
4585
4595
  buyerIdentity,
4586
4596
  // defaults
4587
4597
  storefront,
@@ -5307,9 +5317,9 @@ function hydrogenPreset() {
5307
5317
  v8_middleware: true,
5308
5318
  v8_splitRouteModules: true,
5309
5319
  v8_viteEnvironmentApi: false,
5310
- unstable_optimizeDeps: true,
5311
- unstable_subResourceIntegrity: false
5312
- }
5320
+ unstable_optimizeDeps: true
5321
+ },
5322
+ subResourceIntegrity: false
5313
5323
  }),
5314
5324
  reactRouterConfigResolved: ({ reactRouterConfig }) => {
5315
5325
  if (reactRouterConfig.basename && reactRouterConfig.basename !== "/") {
@@ -5332,9 +5342,9 @@ function hydrogenPreset() {
5332
5342
  "[Hydrogen Preset] buildEnd is not supported in Hydrogen 2025.7.0.\nReason: Hydrogen CLI bypasses React Router buildEnd hook execution.\nWorkaround: Use external build scripts or package.json post-build hooks."
5333
5343
  );
5334
5344
  }
5335
- if (reactRouterConfig.future?.unstable_subResourceIntegrity === true) {
5345
+ if (reactRouterConfig.subResourceIntegrity === true) {
5336
5346
  throw new Error(
5337
- "[Hydrogen Preset] unstable_subResourceIntegrity cannot be enabled.\nReason: Conflicts with Hydrogen CSP nonce-based authentication.\nImpact: Would break Content Security Policy and cause script execution failures."
5347
+ "[Hydrogen Preset] subResourceIntegrity cannot be enabled.\nReason: Conflicts with Hydrogen CSP nonce-based authentication.\nImpact: Would break Content Security Policy and cause script execution failures."
5338
5348
  );
5339
5349
  }
5340
5350
  }
@@ -5728,6 +5738,9 @@ var graphiqlLoader = async function graphiqlLoader2({
5728
5738
  };
5729
5739
 
5730
5740
  // src/routing/redirect.ts
5741
+ var SINGLE_FETCH_DATA_SUFFIX = ".data";
5742
+ var SINGLE_FETCH_ROOT_SEGMENT = "_root.data";
5743
+ var SINGLE_FETCH_TRAILING_SLASH_SEGMENT = "_.data";
5731
5744
  async function storefrontRedirect(options) {
5732
5745
  const {
5733
5746
  storefront,
@@ -5737,13 +5750,13 @@ async function storefrontRedirect(options) {
5737
5750
  response = new Response("Not Found", { status: 404 })
5738
5751
  } = options;
5739
5752
  const url = new URL(request.url);
5740
- const { pathname, searchParams } = url;
5741
- const isSoftNavigation = searchParams.has("_data");
5753
+ const { searchParams } = url;
5754
+ const { pathname, isSoftNavigation } = parseSingleFetchPathname(url.pathname);
5742
5755
  searchParams.delete("redirect");
5743
5756
  searchParams.delete("return_to");
5744
- searchParams.delete("_data");
5745
- const redirectFrom = (matchQueryParams ? url.toString().replace(url.origin, "") : pathname).toLowerCase();
5746
- if (url.pathname === "/admin" && !noAdminRedirect) {
5757
+ searchParams.delete("_routes");
5758
+ const redirectFrom = (matchQueryParams ? `${pathname}${url.search}` : pathname).toLowerCase();
5759
+ if (pathname === "/admin" && !noAdminRedirect) {
5747
5760
  return createRedirectResponse(
5748
5761
  `${storefront.getShopifyDomain()}/admin`,
5749
5762
  isSoftNavigation,
@@ -5793,7 +5806,7 @@ function createRedirectResponse(location, isSoftNavigation, searchParams, matchQ
5793
5806
  }
5794
5807
  if (isSoftNavigation) {
5795
5808
  return new Response(null, {
5796
- status: 200,
5809
+ status: 204,
5797
5810
  headers: {
5798
5811
  "X-Remix-Redirect": url.toString().replace(TEMP_DOMAIN, ""),
5799
5812
  "X-Remix-Status": "301"
@@ -5806,6 +5819,28 @@ function createRedirectResponse(location, isSoftNavigation, searchParams, matchQ
5806
5819
  });
5807
5820
  }
5808
5821
  }
5822
+ function parseSingleFetchPathname(rawPathname) {
5823
+ if (!rawPathname.endsWith(SINGLE_FETCH_DATA_SUFFIX)) {
5824
+ return { pathname: rawPathname, isSoftNavigation: false };
5825
+ }
5826
+ if (rawPathname.endsWith("/" + SINGLE_FETCH_TRAILING_SLASH_SEGMENT)) {
5827
+ return {
5828
+ pathname: rawPathname.slice(
5829
+ 0,
5830
+ -SINGLE_FETCH_TRAILING_SLASH_SEGMENT.length
5831
+ ),
5832
+ isSoftNavigation: true
5833
+ };
5834
+ }
5835
+ if (rawPathname === "/" + SINGLE_FETCH_ROOT_SEGMENT || rawPathname.endsWith("/" + SINGLE_FETCH_ROOT_SEGMENT)) {
5836
+ const prefix = rawPathname.slice(0, -SINGLE_FETCH_ROOT_SEGMENT.length);
5837
+ return { pathname: prefix || "/", isSoftNavigation: true };
5838
+ }
5839
+ return {
5840
+ pathname: rawPathname.slice(0, -SINGLE_FETCH_DATA_SUFFIX.length),
5841
+ isSoftNavigation: true
5842
+ };
5843
+ }
5809
5844
  var REDIRECT_QUERY = `#graphql
5810
5845
  query redirects($query: String) {
5811
5846
  urlRedirects(first: 1, query: $query) {