@shopify/hydrogen 2024.4.2 → 2024.4.4

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.
@@ -4,13 +4,12 @@ var hydrogenReact = require('@shopify/hydrogen-react');
4
4
  var react = require('react');
5
5
  var react$1 = require('@remix-run/react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
+ var cookie = require('worktop/cookie');
7
8
  var cspBuilder = require('content-security-policy-builder');
8
- var invariant = require('tiny-invariant');
9
9
 
10
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
 
12
12
  var cspBuilder__default = /*#__PURE__*/_interopDefault(cspBuilder);
13
- var invariant__default = /*#__PURE__*/_interopDefault(invariant);
14
13
 
15
14
  var __defProp = Object.defineProperty;
16
15
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -485,7 +484,7 @@ async function fetchWithServerCache(url, requestInit, {
485
484
  }
486
485
 
487
486
  // src/version.ts
488
- var LIB_VERSION = "2024.4.2";
487
+ var LIB_VERSION = "2024.4.4";
489
488
 
490
489
  // src/constants.ts
491
490
  var STOREFRONT_REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
@@ -516,6 +515,13 @@ var warnOnce = (string) => {
516
515
  warnings.add(string);
517
516
  }
518
517
  };
518
+ var errors = /* @__PURE__ */ new Set();
519
+ var errorOnce = (string) => {
520
+ if (!errors.has(string)) {
521
+ console.error(new Error(string));
522
+ errors.add(string);
523
+ }
524
+ };
519
525
 
520
526
  // src/utils/graphql.ts
521
527
  function minifyQuery(string) {
@@ -632,19 +638,19 @@ var GraphQLError = class extends Error {
632
638
  function throwErrorWithGqlLink({
633
639
  url,
634
640
  response,
635
- errors,
641
+ errors: errors2,
636
642
  type,
637
643
  query,
638
644
  queryVariables,
639
645
  ErrorConstructor = Error,
640
646
  client = "storefront"
641
647
  }) {
642
- const errorMessage = (typeof errors === "string" ? errors : errors?.map?.((error) => error.message).join("\n")) || `URL: ${url}
648
+ const errorMessage = (typeof errors2 === "string" ? errors2 : errors2?.map?.((error) => error.message).join("\n")) || `URL: ${url}
643
649
  API response error: ${response.status}`;
644
650
  const gqlError = new GraphQLError(errorMessage, {
645
651
  query,
646
652
  queryVariables,
647
- cause: { errors },
653
+ cause: { errors: errors2 },
648
654
  clientOperation: `${client}.${type}`,
649
655
  requestId: response.headers.get("x-request-id")
650
656
  });
@@ -806,16 +812,16 @@ function createStorefrontClient(options) {
806
812
  errors: void 0
807
813
  };
808
814
  if (!response.ok) {
809
- let errors2;
815
+ let errors3;
810
816
  try {
811
- errors2 = parseJSON(body);
817
+ errors3 = parseJSON(body);
812
818
  } catch (_e) {
813
- errors2 = [{ message: body }];
819
+ errors3 = [{ message: body }];
814
820
  }
815
- throwErrorWithGqlLink({ ...errorOptions, errors: errors2 });
821
+ throwErrorWithGqlLink({ ...errorOptions, errors: errors3 });
816
822
  }
817
- const { data, errors } = body;
818
- const gqlErrors = errors?.map(
823
+ const { data, errors: errors2 } = body;
824
+ const gqlErrors = errors2?.map(
819
825
  ({ message, ...rest }) => new GraphQLError(message, {
820
826
  ...rest,
821
827
  clientOperation: `storefront.${errorOptions.type}`,
@@ -902,10 +908,10 @@ var getStackOffset = (query) => {
902
908
  }
903
909
  return stackOffset;
904
910
  } ;
905
- function formatAPIResult(data, errors) {
911
+ function formatAPIResult(data, errors2) {
906
912
  return {
907
913
  ...data,
908
- ...errors && { errors }
914
+ ...errors2 && { errors: errors2 }
909
915
  };
910
916
  }
911
917
 
@@ -1104,7 +1110,9 @@ async function storefrontRedirect(options) {
1104
1110
  }
1105
1111
  try {
1106
1112
  const { urlRedirects } = await storefront.query(REDIRECT_QUERY, {
1107
- variables: { query: "path:" + redirectFrom }
1113
+ // The admin doesn't allow redirects to have a
1114
+ // trailing slash, so strip them all off
1115
+ variables: { query: "path:" + redirectFrom.replace(/\/+$/, "") }
1108
1116
  });
1109
1117
  const location = urlRedirects?.edges?.[0]?.node?.target;
1110
1118
  if (location) {
@@ -2350,6 +2358,7 @@ async function refreshToken({
2350
2358
  const newBody = new URLSearchParams();
2351
2359
  const customerAccount = session.get(CUSTOMER_ACCOUNT_SESSION_KEY);
2352
2360
  const refreshToken2 = customerAccount?.refreshToken;
2361
+ const idToken = customerAccount?.idToken;
2353
2362
  if (!refreshToken2)
2354
2363
  throw new BadRequest(
2355
2364
  "Unauthorized",
@@ -2386,7 +2395,7 @@ async function refreshToken({
2386
2395
  }
2387
2396
  });
2388
2397
  }
2389
- const { access_token, expires_in, id_token, refresh_token } = await response.json();
2398
+ const { access_token, expires_in, refresh_token } = await response.json();
2390
2399
  const accessToken = await exchangeAccessToken(
2391
2400
  access_token,
2392
2401
  customerAccountId,
@@ -2399,7 +2408,7 @@ async function refreshToken({
2399
2408
  // Store the date in future the token expires, separated by two minutes
2400
2409
  expiresAt: new Date((/* @__PURE__ */ new Date()).getTime() + (expires_in - 120) * 1e3).getTime() + "",
2401
2410
  refreshToken: refresh_token,
2402
- idToken: id_token
2411
+ idToken
2403
2412
  });
2404
2413
  await exchangeForStorefrontCustomerAccessToken();
2405
2414
  }
@@ -2643,18 +2652,18 @@ function createCustomerAccountClient({
2643
2652
  }
2644
2653
  throw authFailResponse;
2645
2654
  }
2646
- let errors;
2655
+ let errors2;
2647
2656
  try {
2648
- errors = parseJSON(body);
2657
+ errors2 = parseJSON(body);
2649
2658
  } catch (_e) {
2650
- errors = [{ message: body }];
2659
+ errors2 = [{ message: body }];
2651
2660
  }
2652
- throwErrorWithGqlLink({ ...errorOptions, errors });
2661
+ throwErrorWithGqlLink({ ...errorOptions, errors: errors2 });
2653
2662
  }
2654
2663
  try {
2655
2664
  const APIresponse = parseJSON(body);
2656
- const { errors } = APIresponse;
2657
- const gqlErrors = errors?.map(
2665
+ const { errors: errors2 } = APIresponse;
2666
+ const gqlErrors = errors2?.map(
2658
2667
  ({ message, ...rest }) => new GraphQLError(message, {
2659
2668
  ...rest,
2660
2669
  clientOperation: `customerAccount.${errorOptions.type}`,
@@ -2663,7 +2672,7 @@ function createCustomerAccountClient({
2663
2672
  query: query2
2664
2673
  })
2665
2674
  );
2666
- return { ...APIresponse, ...errors && { errors: gqlErrors } };
2675
+ return { ...APIresponse, ...errors2 && { errors: gqlErrors } };
2667
2676
  } catch (e) {
2668
2677
  throwErrorWithGqlLink({ ...errorOptions, errors: [{ message: body }] });
2669
2678
  }
@@ -2813,7 +2822,6 @@ function createCustomerAccountClient({
2813
2822
  ).toString() : postLogoutRedirectUri;
2814
2823
  clearSession(session);
2815
2824
  return redirect(logoutUrl, {
2816
- status: 302,
2817
2825
  headers: {
2818
2826
  "Set-Cookie": await session.commit()
2819
2827
  }
@@ -3035,7 +3043,7 @@ function cartCreateDefault(options) {
3035
3043
  const buyer = options.customerAccount ? await options.customerAccount.UNSTABLE_getBuyer() : void 0;
3036
3044
  const { cartId, ...restOfOptionalParams } = optionalParams || {};
3037
3045
  const { buyerIdentity, ...restOfInput } = input;
3038
- const { cartCreate, errors } = await options.storefront.mutate(CART_CREATE_MUTATION(options.cartFragment), {
3046
+ const { cartCreate, errors: errors2 } = await options.storefront.mutate(CART_CREATE_MUTATION(options.cartFragment), {
3039
3047
  variables: {
3040
3048
  input: {
3041
3049
  ...restOfInput,
@@ -3047,7 +3055,7 @@ function cartCreateDefault(options) {
3047
3055
  ...restOfOptionalParams
3048
3056
  }
3049
3057
  });
3050
- return formatAPIResult(cartCreate, errors);
3058
+ return formatAPIResult(cartCreate, errors2);
3051
3059
  };
3052
3060
  }
3053
3061
  var CART_CREATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3081,7 +3089,7 @@ function cartGetDefault({
3081
3089
  const cartId = getCartId();
3082
3090
  if (!cartId)
3083
3091
  return null;
3084
- const [isCustomerLoggedIn, { cart, errors }] = await Promise.all([
3092
+ const [isCustomerLoggedIn, { cart, errors: errors2 }] = await Promise.all([
3085
3093
  customerAccount ? customerAccount.isLoggedIn() : false,
3086
3094
  storefront.query(CART_QUERY(cartFragment), {
3087
3095
  variables: {
@@ -3093,7 +3101,7 @@ function cartGetDefault({
3093
3101
  ]);
3094
3102
  return formatAPIResult(
3095
3103
  addCustomerLoggedInParam(isCustomerLoggedIn, cart),
3096
- errors
3104
+ errors2
3097
3105
  );
3098
3106
  };
3099
3107
  }
@@ -3232,14 +3240,14 @@ var DEFAULT_CART_FRAGMENT = `#graphql
3232
3240
  // src/cart/queries/cartLinesAddDefault.ts
3233
3241
  function cartLinesAddDefault(options) {
3234
3242
  return async (lines, optionalParams) => {
3235
- const { cartLinesAdd, errors } = await options.storefront.mutate(CART_LINES_ADD_MUTATION(options.cartFragment), {
3243
+ const { cartLinesAdd, errors: errors2 } = await options.storefront.mutate(CART_LINES_ADD_MUTATION(options.cartFragment), {
3236
3244
  variables: {
3237
3245
  cartId: options.getCartId(),
3238
3246
  lines,
3239
3247
  ...optionalParams
3240
3248
  }
3241
3249
  });
3242
- return formatAPIResult(cartLinesAdd, errors);
3250
+ return formatAPIResult(cartLinesAdd, errors2);
3243
3251
  };
3244
3252
  }
3245
3253
  var CART_LINES_ADD_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3263,17 +3271,36 @@ var CART_LINES_ADD_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphq
3263
3271
  ${USER_ERROR_FRAGMENT}
3264
3272
  `;
3265
3273
 
3274
+ // src/cart/optimistic/optimistic-cart.helper.ts
3275
+ var PENDING_PREFIX = "__h_pending_";
3276
+ function getOptimisticLineId(variantId) {
3277
+ return PENDING_PREFIX + variantId;
3278
+ }
3279
+ function isOptimisticLineId(lineId) {
3280
+ return lineId.startsWith(PENDING_PREFIX);
3281
+ }
3282
+ function throwIfLinesAreOptimistic(type, lines) {
3283
+ if (lines.some(
3284
+ (line) => isOptimisticLineId(typeof line === "string" ? line : line.id)
3285
+ )) {
3286
+ throw new Error(
3287
+ `Tried to perform an action on an optimistic line. Make sure to disable your "${type}" CartForm action when the line is optimistic.`
3288
+ );
3289
+ }
3290
+ }
3291
+
3266
3292
  // src/cart/queries/cartLinesUpdateDefault.ts
3267
3293
  function cartLinesUpdateDefault(options) {
3268
3294
  return async (lines, optionalParams) => {
3269
- const { cartLinesUpdate, errors } = await options.storefront.mutate(CART_LINES_UPDATE_MUTATION(options.cartFragment), {
3295
+ throwIfLinesAreOptimistic("updateLines", lines);
3296
+ const { cartLinesUpdate, errors: errors2 } = await options.storefront.mutate(CART_LINES_UPDATE_MUTATION(options.cartFragment), {
3270
3297
  variables: {
3271
3298
  cartId: options.getCartId(),
3272
3299
  lines,
3273
3300
  ...optionalParams
3274
3301
  }
3275
3302
  });
3276
- return formatAPIResult(cartLinesUpdate, errors);
3303
+ return formatAPIResult(cartLinesUpdate, errors2);
3277
3304
  };
3278
3305
  }
3279
3306
  var CART_LINES_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3300,14 +3327,15 @@ var CART_LINES_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#gra
3300
3327
  // src/cart/queries/cartLinesRemoveDefault.ts
3301
3328
  function cartLinesRemoveDefault(options) {
3302
3329
  return async (lineIds, optionalParams) => {
3303
- const { cartLinesRemove, errors } = await options.storefront.mutate(CART_LINES_REMOVE_MUTATION(options.cartFragment), {
3330
+ throwIfLinesAreOptimistic("removeLines", lineIds);
3331
+ const { cartLinesRemove, errors: errors2 } = await options.storefront.mutate(CART_LINES_REMOVE_MUTATION(options.cartFragment), {
3304
3332
  variables: {
3305
3333
  cartId: options.getCartId(),
3306
3334
  lineIds,
3307
3335
  ...optionalParams
3308
3336
  }
3309
3337
  });
3310
- return formatAPIResult(cartLinesRemove, errors);
3338
+ return formatAPIResult(cartLinesRemove, errors2);
3311
3339
  };
3312
3340
  }
3313
3341
  var CART_LINES_REMOVE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3337,14 +3365,14 @@ function cartDiscountCodesUpdateDefault(options) {
3337
3365
  const uniqueCodes = discountCodes.filter((value, index, array) => {
3338
3366
  return array.indexOf(value) === index;
3339
3367
  });
3340
- const { cartDiscountCodesUpdate, errors } = await options.storefront.mutate(CART_DISCOUNT_CODE_UPDATE_MUTATION(options.cartFragment), {
3368
+ const { cartDiscountCodesUpdate, errors: errors2 } = await options.storefront.mutate(CART_DISCOUNT_CODE_UPDATE_MUTATION(options.cartFragment), {
3341
3369
  variables: {
3342
3370
  cartId: options.getCartId(),
3343
3371
  discountCodes: uniqueCodes,
3344
3372
  ...optionalParams
3345
3373
  }
3346
3374
  });
3347
- return formatAPIResult(cartDiscountCodesUpdate, errors);
3375
+ return formatAPIResult(cartDiscountCodesUpdate, errors2);
3348
3376
  };
3349
3377
  }
3350
3378
  var CART_DISCOUNT_CODE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3376,7 +3404,7 @@ function cartBuyerIdentityUpdateDefault(options) {
3376
3404
  });
3377
3405
  }
3378
3406
  const buyer = options.customerAccount ? await options.customerAccount.UNSTABLE_getBuyer() : void 0;
3379
- const { cartBuyerIdentityUpdate, errors } = await options.storefront.mutate(CART_BUYER_IDENTITY_UPDATE_MUTATION(options.cartFragment), {
3407
+ const { cartBuyerIdentityUpdate, errors: errors2 } = await options.storefront.mutate(CART_BUYER_IDENTITY_UPDATE_MUTATION(options.cartFragment), {
3380
3408
  variables: {
3381
3409
  cartId: options.getCartId(),
3382
3410
  buyerIdentity: {
@@ -3386,7 +3414,7 @@ function cartBuyerIdentityUpdateDefault(options) {
3386
3414
  ...optionalParams
3387
3415
  }
3388
3416
  });
3389
- return formatAPIResult(cartBuyerIdentityUpdate, errors);
3417
+ return formatAPIResult(cartBuyerIdentityUpdate, errors2);
3390
3418
  };
3391
3419
  }
3392
3420
  var CART_BUYER_IDENTITY_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3412,14 +3440,14 @@ var CART_BUYER_IDENTITY_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT)
3412
3440
  // src/cart/queries/cartNoteUpdateDefault.ts
3413
3441
  function cartNoteUpdateDefault(options) {
3414
3442
  return async (note, optionalParams) => {
3415
- const { cartNoteUpdate, errors } = await options.storefront.mutate(CART_NOTE_UPDATE_MUTATION(options.cartFragment), {
3443
+ const { cartNoteUpdate, errors: errors2 } = await options.storefront.mutate(CART_NOTE_UPDATE_MUTATION(options.cartFragment), {
3416
3444
  variables: {
3417
3445
  cartId: options.getCartId(),
3418
3446
  note,
3419
3447
  ...optionalParams
3420
3448
  }
3421
3449
  });
3422
- return formatAPIResult(cartNoteUpdate, errors);
3450
+ return formatAPIResult(cartNoteUpdate, errors2);
3423
3451
  };
3424
3452
  }
3425
3453
  var CART_NOTE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3445,14 +3473,14 @@ var CART_NOTE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#grap
3445
3473
  // src/cart/queries/cartSelectedDeliveryOptionsUpdateDefault.ts
3446
3474
  function cartSelectedDeliveryOptionsUpdateDefault(options) {
3447
3475
  return async (selectedDeliveryOptions, optionalParams) => {
3448
- const { cartSelectedDeliveryOptionsUpdate, errors } = await options.storefront.mutate(CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION(options.cartFragment), {
3476
+ const { cartSelectedDeliveryOptionsUpdate, errors: errors2 } = await options.storefront.mutate(CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION(options.cartFragment), {
3449
3477
  variables: {
3450
3478
  cartId: options.getCartId(),
3451
3479
  selectedDeliveryOptions,
3452
3480
  ...optionalParams
3453
3481
  }
3454
3482
  });
3455
- return formatAPIResult(cartSelectedDeliveryOptionsUpdate, errors);
3483
+ return formatAPIResult(cartSelectedDeliveryOptionsUpdate, errors2);
3456
3484
  };
3457
3485
  }
3458
3486
  var CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3478,13 +3506,13 @@ var CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION = (cartFragment = MINIMAL_CAR
3478
3506
  // src/cart/queries/cartAttributesUpdateDefault.ts
3479
3507
  function cartAttributesUpdateDefault(options) {
3480
3508
  return async (attributes, optionalParams) => {
3481
- const { cartAttributesUpdate, errors } = await options.storefront.mutate(CART_ATTRIBUTES_UPDATE_MUTATION(options.cartFragment), {
3509
+ const { cartAttributesUpdate, errors: errors2 } = await options.storefront.mutate(CART_ATTRIBUTES_UPDATE_MUTATION(options.cartFragment), {
3482
3510
  variables: {
3483
3511
  cartId: optionalParams?.cartId || options.getCartId(),
3484
3512
  attributes
3485
3513
  }
3486
3514
  });
3487
- return formatAPIResult(cartAttributesUpdate, errors);
3515
+ return formatAPIResult(cartAttributesUpdate, errors2);
3488
3516
  };
3489
3517
  }
3490
3518
  var CART_ATTRIBUTES_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3515,7 +3543,7 @@ function cartMetafieldsSetDefault(options) {
3515
3543
  ownerId
3516
3544
  })
3517
3545
  );
3518
- const { cartMetafieldsSet, errors } = await options.storefront.mutate(CART_METAFIELD_SET_MUTATION(), {
3546
+ const { cartMetafieldsSet, errors: errors2 } = await options.storefront.mutate(CART_METAFIELD_SET_MUTATION(), {
3519
3547
  variables: { metafields: metafieldsWithOwnerId }
3520
3548
  });
3521
3549
  return formatAPIResult(
@@ -3525,7 +3553,7 @@ function cartMetafieldsSetDefault(options) {
3525
3553
  },
3526
3554
  ...cartMetafieldsSet
3527
3555
  },
3528
- errors
3556
+ errors2
3529
3557
  );
3530
3558
  };
3531
3559
  }
@@ -3550,7 +3578,7 @@ var CART_METAFIELD_SET_MUTATION = () => `#graphql
3550
3578
  function cartMetafieldDeleteDefault(options) {
3551
3579
  return async (key, optionalParams) => {
3552
3580
  const ownerId = optionalParams?.cartId || options.getCartId();
3553
- const { cartMetafieldDelete, errors } = await options.storefront.mutate(CART_METAFIELD_DELETE_MUTATION(), {
3581
+ const { cartMetafieldDelete, errors: errors2 } = await options.storefront.mutate(CART_METAFIELD_DELETE_MUTATION(), {
3554
3582
  variables: {
3555
3583
  input: {
3556
3584
  ownerId,
@@ -3565,7 +3593,7 @@ function cartMetafieldDeleteDefault(options) {
3565
3593
  },
3566
3594
  ...cartMetafieldDelete
3567
3595
  },
3568
- errors
3596
+ errors2
3569
3597
  );
3570
3598
  };
3571
3599
  }
@@ -3582,51 +3610,18 @@ var CART_METAFIELD_DELETE_MUTATION = () => `#graphql
3582
3610
  }
3583
3611
  }
3584
3612
  `;
3585
-
3586
- // ../../node_modules/worktop/cookie/index.mjs
3587
- var g = /* @__PURE__ */ new Set([
3588
- "domain",
3589
- "path",
3590
- "max-age",
3591
- "expires",
3592
- "samesite",
3593
- "secure",
3594
- "httponly"
3595
- ]);
3596
- function u(a) {
3597
- let r = {}, e, t, n = 0, m = a.split(/;\s*/g), s, i;
3598
- for (; n < m.length; n++)
3599
- if (t = m[n], e = t.indexOf("="), ~e) {
3600
- if (s = t.substring(0, e++).trim(), i = t.substring(e).trim(), i[0] === '"' && (i = i.substring(1, i.length - 1)), ~i.indexOf("%"))
3601
- try {
3602
- i = decodeURIComponent(i);
3603
- } catch (f) {
3604
- }
3605
- g.has(t = s.toLowerCase()) ? t === "expires" ? r.expires = new Date(i) : t === "max-age" ? r.maxage = +i : r[t] = i : r[s] = i;
3606
- } else
3607
- (s = t.trim().toLowerCase()) && (s === "httponly" || s === "secure") && (r[s] = true);
3608
- return r;
3609
- }
3610
- function l(a, r, e = {}) {
3611
- let t = a + "=" + encodeURIComponent(r);
3612
- return e.expires && (t += "; Expires=" + new Date(e.expires).toUTCString()), e.maxage != null && e.maxage >= 0 && (t += "; Max-Age=" + (e.maxage | 0)), e.domain && (t += "; Domain=" + e.domain), e.path && (t += "; Path=" + e.path), e.samesite && (t += "; SameSite=" + e.samesite), (e.secure || e.samesite === "None") && (t += "; Secure"), e.httponly && (t += "; HttpOnly"), t;
3613
- }
3614
-
3615
- // src/cart/cartGetIdDefault.ts
3616
3613
  var cartGetIdDefault = (requestHeaders) => {
3617
- const cookies = u(requestHeaders.get("Cookie") || "");
3614
+ const cookies = cookie.parse(requestHeaders.get("Cookie") || "");
3618
3615
  return () => {
3619
3616
  return cookies.cart ? `gid://shopify/Cart/${cookies.cart}` : void 0;
3620
3617
  };
3621
3618
  };
3622
-
3623
- // src/cart/cartSetIdDefault.ts
3624
3619
  var cartSetIdDefault = (cookieOptions) => {
3625
3620
  return (cartId) => {
3626
3621
  const headers = new Headers();
3627
3622
  headers.append(
3628
3623
  "Set-Cookie",
3629
- l("cart", cartId.split("/").pop() || "", {
3624
+ cookie.stringify("cart", cartId.split("/").pop() || "", {
3630
3625
  path: "/",
3631
3626
  ...cookieOptions
3632
3627
  })
@@ -3669,7 +3664,15 @@ function createCartHandler(options) {
3669
3664
  getCartId,
3670
3665
  setCartId,
3671
3666
  create: cartCreate,
3672
- addLines: async (lines, optionalParams) => {
3667
+ addLines: async (linesWithOptimisticData, optionalParams) => {
3668
+ const lines = linesWithOptimisticData.map((line) => {
3669
+ return {
3670
+ attributes: line.attributes,
3671
+ quantity: line.quantity,
3672
+ merchandiseId: line.merchandiseId,
3673
+ sellingPlanId: line.sellingPlanId
3674
+ };
3675
+ });
3673
3676
  return cartId || optionalParams?.cartId ? await cartLinesAddDefault(mutateOptions)(lines, optionalParams) : await cartCreate({ lines }, optionalParams);
3674
3677
  },
3675
3678
  updateLines: cartLinesUpdateDefault(mutateOptions),
@@ -3713,6 +3716,89 @@ function createCartHandler(options) {
3713
3716
  return methods;
3714
3717
  }
3715
3718
  }
3719
+ function useOptimisticCart(cart) {
3720
+ const fetchers = react$1.useFetchers();
3721
+ if (!fetchers || !fetchers.length)
3722
+ return cart;
3723
+ const optimisticCart = cart?.lines ? structuredClone(cart) : { lines: { nodes: [] } };
3724
+ const cartLines = optimisticCart.lines.nodes;
3725
+ let isOptimistic = false;
3726
+ for (const { formData } of fetchers) {
3727
+ if (!formData)
3728
+ continue;
3729
+ const cartFormData = CartForm.getFormInput(formData);
3730
+ if (cartFormData.action === CartForm.ACTIONS.LinesAdd) {
3731
+ for (const input of cartFormData.inputs.lines) {
3732
+ if (!input.selectedVariant) {
3733
+ console.error(
3734
+ "[h2:error:useOptimisticCart] No selected variant was passed in the cart action. Make sure to pass the selected variant if you want to use an optimistic cart"
3735
+ );
3736
+ continue;
3737
+ }
3738
+ const existingLine = cartLines.find(
3739
+ (line) => line.merchandise.id === input.selectedVariant?.id
3740
+ );
3741
+ isOptimistic = true;
3742
+ if (existingLine) {
3743
+ existingLine.quantity = (existingLine.quantity || 1) + (input.quantity || 1);
3744
+ existingLine.isOptimistic = true;
3745
+ } else {
3746
+ cartLines.unshift({
3747
+ id: getOptimisticLineId(input.selectedVariant.id),
3748
+ merchandise: input.selectedVariant,
3749
+ isOptimistic: true,
3750
+ quantity: input.quantity || 1
3751
+ });
3752
+ }
3753
+ }
3754
+ } else if (cartFormData.action === CartForm.ACTIONS.LinesRemove) {
3755
+ for (const lineId of cartFormData.inputs.lineIds) {
3756
+ const index = cartLines.findIndex((line) => line.id === lineId);
3757
+ if (index !== -1) {
3758
+ if (isOptimisticLineId(cartLines[index].id)) {
3759
+ console.error(
3760
+ "[h2:error:useOptimisticCart] Tried to remove an optimistic line that has not been added to the cart yet"
3761
+ );
3762
+ continue;
3763
+ }
3764
+ cartLines.splice(index, 1);
3765
+ isOptimistic = true;
3766
+ } else {
3767
+ console.warn(
3768
+ `[h2:warn:useOptimisticCart] Tried to remove line '${lineId}' but it doesn't exist in the cart`
3769
+ );
3770
+ }
3771
+ }
3772
+ } else if (cartFormData.action === CartForm.ACTIONS.LinesUpdate) {
3773
+ for (const line of cartFormData.inputs.lines) {
3774
+ const index = cartLines.findIndex(
3775
+ (optimisticLine) => line.id === optimisticLine.id
3776
+ );
3777
+ if (index > -1) {
3778
+ if (isOptimisticLineId(cartLines[index].id)) {
3779
+ console.error(
3780
+ "[h2:error:useOptimisticCart] Tried to update an optimistic line that has not been added to the cart yet"
3781
+ );
3782
+ continue;
3783
+ }
3784
+ cartLines[index].quantity = line.quantity;
3785
+ if (cartLines[index].quantity === 0) {
3786
+ cartLines.splice(index, 1);
3787
+ }
3788
+ isOptimistic = true;
3789
+ } else {
3790
+ console.warn(
3791
+ `[h2:warn:useOptimisticCart] Tried to update line '${line.id}' but it doesn't exist in the cart`
3792
+ );
3793
+ }
3794
+ }
3795
+ }
3796
+ }
3797
+ if (isOptimistic) {
3798
+ optimisticCart.isOptimistic = isOptimistic;
3799
+ }
3800
+ return optimisticCart;
3801
+ }
3716
3802
  function VariantSelector({
3717
3803
  handle,
3718
3804
  options = [],
@@ -3762,7 +3848,8 @@ function VariantSelector({
3762
3848
  isAvailable: variant ? variant.availableForSale : true,
3763
3849
  to: path + searchString,
3764
3850
  search: searchString,
3765
- isActive: calculatedActiveValue
3851
+ isActive: calculatedActiveValue,
3852
+ variant
3766
3853
  });
3767
3854
  }
3768
3855
  return children({
@@ -3928,21 +4015,33 @@ function ShopPayButton(props) {
3928
4015
  function AnalyticsView(props) {
3929
4016
  const { type, data = {}, customData } = props;
3930
4017
  const location = react$1.useLocation();
3931
- const { publish: publish2, cart, prevCart, shop } = useAnalytics();
4018
+ const {
4019
+ publish: publish2,
4020
+ cart,
4021
+ prevCart,
4022
+ shop,
4023
+ customData: analyticProviderCustomData
4024
+ } = useAnalytics();
3932
4025
  const url = location.pathname + location.search;
4026
+ let viewPayload2 = {
4027
+ ...data,
4028
+ customData: {
4029
+ ...analyticProviderCustomData,
4030
+ ...customData
4031
+ },
4032
+ cart,
4033
+ prevCart,
4034
+ shop
4035
+ };
3933
4036
  react.useEffect(() => {
3934
- if (!shop)
4037
+ if (!shop?.shopId)
3935
4038
  return;
3936
- const viewPayload2 = {
3937
- ...data,
3938
- customData,
3939
- url: window.location.href,
3940
- cart,
3941
- prevCart,
3942
- shop
4039
+ viewPayload2 = {
4040
+ ...viewPayload2,
4041
+ url: window.location.href
3943
4042
  };
3944
4043
  publish2(type, viewPayload2);
3945
- }, [publish2, url, cart, prevCart, shop]);
4044
+ }, [publish2, url, shop?.shopId]);
3946
4045
  return null;
3947
4046
  }
3948
4047
  function AnalyticsPageView(props) {
@@ -3983,7 +4082,7 @@ var CONSENT_API = "https://cdn.shopify.com/shopifycloud/consent-tracking-api/v0.
3983
4082
  var CONSENT_API_WITH_BANNER = "https://cdn.shopify.com/shopifycloud/privacy-banner/storefront-banner.js";
3984
4083
  function logMissingConfig(fieldName) {
3985
4084
  console.error(
3986
- `[h2:error:useCustomerPrivacy] Unable to setup Customer Privacy API: Missing consent.${fieldName} consent configuration.`
4085
+ `[h2:error:useCustomerPrivacy] Unable to setup Customer Privacy API: Missing consent.${fieldName} configuration.`
3987
4086
  );
3988
4087
  }
3989
4088
  function useCustomerPrivacy(props) {
@@ -4002,10 +4101,6 @@ function useCustomerPrivacy(props) {
4002
4101
  }
4003
4102
  }
4004
4103
  );
4005
- if (!consentConfig.checkoutDomain)
4006
- logMissingConfig("checkoutDomain");
4007
- if (!consentConfig.storefrontAccessToken)
4008
- logMissingConfig("storefrontAccessToken");
4009
4104
  react.useEffect(() => {
4010
4105
  const consentCollectedHandler = (event) => {
4011
4106
  if (onVisitorConsentCollected) {
@@ -4027,29 +4122,37 @@ function useCustomerPrivacy(props) {
4027
4122
  if (scriptStatus !== "done" || loadedEvent.current)
4028
4123
  return;
4029
4124
  loadedEvent.current = true;
4125
+ if (!consentConfig.checkoutDomain)
4126
+ logMissingConfig("checkoutDomain");
4127
+ if (!consentConfig.storefrontAccessToken)
4128
+ logMissingConfig("storefrontAccessToken");
4129
+ if (consentConfig.storefrontAccessToken.startsWith("shpat_") || consentConfig.storefrontAccessToken.length !== 32) {
4130
+ console.error(
4131
+ `[h2:error:useCustomerPrivacy] It looks like you passed a private access token, make sure to use the public token`
4132
+ );
4133
+ }
4030
4134
  if (withPrivacyBanner && window?.privacyBanner) {
4031
4135
  window?.privacyBanner?.loadBanner({
4032
4136
  checkoutRootDomain: consentConfig.checkoutDomain,
4033
4137
  storefrontAccessToken: consentConfig.storefrontAccessToken
4034
4138
  });
4035
4139
  }
4036
- if (window.Shopify?.customerPrivacy) {
4037
- const originalSetTrackingConsent = window.Shopify.customerPrivacy.setTrackingConsent;
4038
- window.Shopify.customerPrivacy.setTrackingConsent = (consent, callback) => {
4039
- originalSetTrackingConsent(
4040
- {
4041
- ...consent,
4042
- headlessStorefront: true,
4043
- checkoutRootDomain: consentConfig.checkoutDomain,
4044
- storefrontAccessToken: consentConfig.storefrontAccessToken
4045
- },
4046
- callback
4047
- );
4048
- };
4049
- }
4050
- if (onReady && !withPrivacyBanner) {
4051
- onReady();
4140
+ if (!window.Shopify?.customerPrivacy)
4141
+ return;
4142
+ const originalSetTrackingConsent = window.Shopify.customerPrivacy.setTrackingConsent;
4143
+ function overrideSetTrackingConsent(consent, callback) {
4144
+ originalSetTrackingConsent(
4145
+ {
4146
+ ...consent,
4147
+ headlessStorefront: true,
4148
+ checkoutRootDomain: consentConfig.checkoutDomain,
4149
+ storefrontAccessToken: consentConfig.storefrontAccessToken
4150
+ },
4151
+ callback
4152
+ );
4052
4153
  }
4154
+ window.Shopify.customerPrivacy.setTrackingConsent = overrideSetTrackingConsent;
4155
+ onReady && onReady();
4053
4156
  }, [scriptStatus, withPrivacyBanner, consentConfig]);
4054
4157
  return;
4055
4158
  }
@@ -4071,27 +4174,38 @@ function getCustomerPrivacyRequired() {
4071
4174
  }
4072
4175
  function ShopifyAnalytics({
4073
4176
  consent,
4074
- onReady
4177
+ onReady,
4178
+ domain
4075
4179
  }) {
4076
4180
  const { subscribe: subscribe2, register: register2, canTrack } = useAnalytics();
4181
+ const [shopifyReady, setShopifyReady] = react.useState(false);
4182
+ const [privacyReady, setPrivacyReady] = react.useState(false);
4077
4183
  const { ready: shopifyAnalyticsReady } = register2("Internal_Shopify_Analytics");
4078
4184
  const { ready: customerPrivacyReady } = register2(
4079
4185
  "Internal_Shopify_CustomerPrivacy"
4080
4186
  );
4081
4187
  const analyticsReady = () => {
4188
+ shopifyReady && privacyReady && onReady();
4189
+ };
4190
+ const setCustomerPrivacyReady = () => {
4191
+ setPrivacyReady(true);
4082
4192
  customerPrivacyReady();
4083
- onReady();
4193
+ analyticsReady();
4084
4194
  };
4195
+ const { checkoutDomain, storefrontAccessToken, withPrivacyBanner } = consent;
4085
4196
  useCustomerPrivacy({
4086
- ...consent,
4087
- onVisitorConsentCollected: analyticsReady,
4197
+ checkoutDomain: !checkoutDomain ? "mock.shop" : checkoutDomain,
4198
+ storefrontAccessToken: !storefrontAccessToken ? "abcdefghijklmnopqrstuvwxyz123456" : storefrontAccessToken,
4199
+ withPrivacyBanner,
4200
+ onVisitorConsentCollected: setCustomerPrivacyReady,
4088
4201
  onReady: () => {
4089
- if (!consent.withPrivacyBanner) {
4090
- analyticsReady();
4091
- }
4202
+ setTimeout(setCustomerPrivacyReady, 3e3);
4092
4203
  }
4093
4204
  });
4094
- hydrogenReact.useShopifyCookies({ hasUserConsent: canTrack() });
4205
+ hydrogenReact.useShopifyCookies({
4206
+ hasUserConsent: canTrack(),
4207
+ domain
4208
+ });
4095
4209
  react.useEffect(() => {
4096
4210
  subscribe2(AnalyticsEvent.PAGE_VIEWED, pageViewHandler);
4097
4211
  subscribe2(AnalyticsEvent.PRODUCT_VIEWED, productViewHandler);
@@ -4099,6 +4213,8 @@ function ShopifyAnalytics({
4099
4213
  subscribe2(AnalyticsEvent.SEARCH_VIEWED, searchViewHandler);
4100
4214
  subscribe2(AnalyticsEvent.PRODUCT_ADD_TO_CART, productAddedToCartHandler);
4101
4215
  shopifyAnalyticsReady();
4216
+ setShopifyReady(true);
4217
+ analyticsReady();
4102
4218
  }, [subscribe2, shopifyAnalyticsReady]);
4103
4219
  return null;
4104
4220
  }
@@ -4517,8 +4633,8 @@ function shopifyCanTrack() {
4517
4633
  }
4518
4634
  return false;
4519
4635
  }
4520
- function messageOnError(field) {
4521
- return `[h2:error:Analytics.Provider] - ${field} is required`;
4636
+ function messageOnError(field, envVar) {
4637
+ return `[h2:error:Analytics.Provider] - ${field} is required. Make sure ${envVar} is defined in your environment variables. See https://h2o.fyi/analytics/consent to learn how to setup environment variables in the Shopify admin.`;
4522
4638
  }
4523
4639
  function AnalyticsProvider({
4524
4640
  canTrack: customCanTrack,
@@ -4527,24 +4643,9 @@ function AnalyticsProvider({
4527
4643
  consent,
4528
4644
  customData = {},
4529
4645
  shop: shopProp = null,
4530
- disableThrowOnError = false
4646
+ disableThrowOnError = false,
4647
+ cookieDomain
4531
4648
  }) {
4532
- if (!consent.checkoutDomain) {
4533
- const errorMsg = messageOnError("consent.checkoutDomain");
4534
- if (disableThrowOnError) {
4535
- console.error(errorMsg);
4536
- } else {
4537
- invariant__default.default(false, errorMsg);
4538
- }
4539
- }
4540
- if (!consent.storefrontAccessToken) {
4541
- const errorMsg = messageOnError("consent.storefrontAccessToken");
4542
- if (disableThrowOnError) {
4543
- console.error(errorMsg);
4544
- } else {
4545
- invariant__default.default(false, errorMsg);
4546
- }
4547
- }
4548
4649
  const listenerSet = react.useRef(false);
4549
4650
  const { shop } = useShopAnalytics(shopProp);
4550
4651
  const [consentLoaded, setConsentLoaded] = react.useState(
@@ -4554,6 +4655,28 @@ function AnalyticsProvider({
4554
4655
  const [canTrack, setCanTrack] = react.useState(
4555
4656
  customCanTrack ? () => customCanTrack : () => shopifyCanTrack
4556
4657
  );
4658
+ if (!!shop) {
4659
+ if (/\/68817551382$/.test(shop.shopId)) {
4660
+ warnOnce(
4661
+ "[h2:error:Analytics.Provider] - Mock shop is used. Analytics will not work properly."
4662
+ );
4663
+ } else {
4664
+ if (!consent.checkoutDomain) {
4665
+ const errorMsg = messageOnError(
4666
+ "consent.checkoutDomain",
4667
+ "PUBLIC_CHECKOUT_DOMAIN"
4668
+ );
4669
+ errorOnce(errorMsg);
4670
+ }
4671
+ if (!consent.storefrontAccessToken) {
4672
+ const errorMsg = messageOnError(
4673
+ "consent.storefrontAccessToken",
4674
+ "PUBLIC_STOREFRONT_API_TOKEN"
4675
+ );
4676
+ errorOnce(errorMsg);
4677
+ }
4678
+ }
4679
+ }
4557
4680
  const value = react.useMemo(() => {
4558
4681
  return {
4559
4682
  canTrack,
@@ -4584,7 +4707,7 @@ function AnalyticsProvider({
4584
4707
  children,
4585
4708
  !!shop && /* @__PURE__ */ jsxRuntime.jsx(AnalyticsPageView, {}),
4586
4709
  !!shop && !!currentCart && /* @__PURE__ */ jsxRuntime.jsx(CartAnalytics, { cart: currentCart, setCarts }),
4587
- !!shop && /* @__PURE__ */ jsxRuntime.jsx(
4710
+ !!shop && consent.checkoutDomain && /* @__PURE__ */ jsxRuntime.jsx(
4588
4711
  ShopifyAnalytics,
4589
4712
  {
4590
4713
  consent,
@@ -4592,7 +4715,8 @@ function AnalyticsProvider({
4592
4715
  listenerSet.current = true;
4593
4716
  setConsentLoaded(true);
4594
4717
  setCanTrack(() => shopifyCanTrack);
4595
- }
4718
+ },
4719
+ domain: cookieDomain
4596
4720
  }
4597
4721
  )
4598
4722
  ] });
@@ -4658,6 +4782,27 @@ var Analytics = {
4658
4782
  Provider: AnalyticsProvider,
4659
4783
  SearchView: AnalyticsSearchView
4660
4784
  };
4785
+ var RichText = function(props) {
4786
+ return /* @__PURE__ */ jsxRuntime.jsx(
4787
+ hydrogenReact.RichText,
4788
+ {
4789
+ ...props,
4790
+ components: {
4791
+ link: ({ node }) => /* @__PURE__ */ jsxRuntime.jsx(
4792
+ react$1.Link,
4793
+ {
4794
+ to: node.url,
4795
+ title: node.title,
4796
+ target: node.target,
4797
+ prefetch: "intent",
4798
+ children: node.children
4799
+ }
4800
+ ),
4801
+ ...props.components
4802
+ }
4803
+ }
4804
+ );
4805
+ };
4661
4806
  //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartCreate
4662
4807
  //! @see https://shopify.dev/docs/api/storefront/latest/queries/cart
4663
4808
  //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesAdd
@@ -4754,6 +4899,7 @@ Object.defineProperty(exports, 'useShopifyCookies', {
4754
4899
  enumerable: true,
4755
4900
  get: function () { return hydrogenReact.useShopifyCookies; }
4756
4901
  });
4902
+ exports.Analytics = Analytics;
4757
4903
  exports.AnalyticsEvent = AnalyticsEvent;
4758
4904
  exports.CacheCustom = CacheCustom;
4759
4905
  exports.CacheLong = CacheLong;
@@ -4763,10 +4909,10 @@ exports.CartForm = CartForm;
4763
4909
  exports.InMemoryCache = InMemoryCache;
4764
4910
  exports.OptimisticInput = OptimisticInput;
4765
4911
  exports.Pagination = Pagination;
4912
+ exports.RichText = RichText;
4766
4913
  exports.Script = Script;
4767
4914
  exports.Seo = Seo;
4768
4915
  exports.ShopPayButton = ShopPayButton;
4769
- exports.UNSTABLE_Analytics = Analytics;
4770
4916
  exports.VariantSelector = VariantSelector;
4771
4917
  exports.cartAttributesUpdateDefault = cartAttributesUpdateDefault;
4772
4918
  exports.cartBuyerIdentityUpdateDefault = cartBuyerIdentityUpdateDefault;
@@ -4797,9 +4943,10 @@ exports.getSeoMeta = getSeoMeta;
4797
4943
  exports.getShopAnalytics = getShopAnalytics;
4798
4944
  exports.graphiqlLoader = graphiqlLoader;
4799
4945
  exports.storefrontRedirect = storefrontRedirect;
4800
- exports.unstable_useAnalytics = useAnalytics;
4946
+ exports.useAnalytics = useAnalytics;
4801
4947
  exports.useCustomerPrivacy = useCustomerPrivacy;
4802
4948
  exports.useNonce = useNonce;
4949
+ exports.useOptimisticCart = useOptimisticCart;
4803
4950
  exports.useOptimisticData = useOptimisticData;
4804
4951
  //# sourceMappingURL=out.js.map
4805
4952
  //# sourceMappingURL=index.cjs.map