@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.
@@ -1,10 +1,10 @@
1
- import { createStorefrontClient as createStorefrontClient$1, SHOPIFY_STOREFRONT_ID_HEADER, getShopifyCookies, SHOPIFY_Y, SHOPIFY_STOREFRONT_Y_HEADER, SHOPIFY_S, SHOPIFY_STOREFRONT_S_HEADER, flattenConnection, ShopPayButton as ShopPayButton$1, useLoadScript, useShopifyCookies, sendShopifyAnalytics, AnalyticsEventName, AnalyticsPageType, getClientBrowserParameters } from '@shopify/hydrogen-react';
1
+ import { createStorefrontClient as createStorefrontClient$1, SHOPIFY_STOREFRONT_ID_HEADER, getShopifyCookies, SHOPIFY_Y, SHOPIFY_STOREFRONT_Y_HEADER, SHOPIFY_S, SHOPIFY_STOREFRONT_S_HEADER, flattenConnection, ShopPayButton as ShopPayButton$1, useLoadScript, RichText as RichText$1, useShopifyCookies, sendShopifyAnalytics, AnalyticsEventName, AnalyticsPageType, getClientBrowserParameters } from '@shopify/hydrogen-react';
2
2
  export { AnalyticsEventName, AnalyticsPageType, ExternalVideo, IMAGE_FRAGMENT, Image, MediaFile, ModelViewer, Money, ShopifySalesChannel, Video, customerAccountApiCustomScalars, flattenConnection, getClientBrowserParameters, getShopifyCookies, parseGid, parseMetafield, sendShopifyAnalytics, storefrontApiCustomScalars, useLoadScript, useMoney, useShopifyCookies } from '@shopify/hydrogen-react';
3
3
  import { lazy, createContext, forwardRef, useMemo, createElement, Suspense, Fragment, useRef, useEffect, useContext, useState } from 'react';
4
4
  import { useMatches, useLocation, useNavigation, Link, useNavigate, useFetcher, useFetchers } from '@remix-run/react';
5
5
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
6
+ import { parse, stringify } from 'worktop/cookie';
6
7
  import cspBuilder from 'content-security-policy-builder';
7
- import invariant from 'tiny-invariant';
8
8
 
9
9
  // src/storefront.ts
10
10
 
@@ -390,7 +390,7 @@ async function fetchWithServerCache(url, requestInit, {
390
390
  }
391
391
 
392
392
  // src/version.ts
393
- var LIB_VERSION = "2024.4.2";
393
+ var LIB_VERSION = "2024.4.4";
394
394
 
395
395
  // src/constants.ts
396
396
  var STOREFRONT_REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
@@ -421,6 +421,13 @@ var warnOnce = (string) => {
421
421
  warnings.add(string);
422
422
  }
423
423
  };
424
+ var errors = /* @__PURE__ */ new Set();
425
+ var errorOnce = (string) => {
426
+ if (!errors.has(string)) {
427
+ console.error(new Error(string));
428
+ errors.add(string);
429
+ }
430
+ };
424
431
 
425
432
  // src/utils/graphql.ts
426
433
  function minifyQuery(string) {
@@ -537,19 +544,19 @@ var GraphQLError = class extends Error {
537
544
  function throwErrorWithGqlLink({
538
545
  url,
539
546
  response,
540
- errors,
547
+ errors: errors2,
541
548
  type,
542
549
  query,
543
550
  queryVariables,
544
551
  ErrorConstructor = Error,
545
552
  client = "storefront"
546
553
  }) {
547
- const errorMessage = (typeof errors === "string" ? errors : errors?.map?.((error) => error.message).join("\n")) || `URL: ${url}
554
+ const errorMessage = (typeof errors2 === "string" ? errors2 : errors2?.map?.((error) => error.message).join("\n")) || `URL: ${url}
548
555
  API response error: ${response.status}`;
549
556
  const gqlError = new GraphQLError(errorMessage, {
550
557
  query,
551
558
  queryVariables,
552
- cause: { errors },
559
+ cause: { errors: errors2 },
553
560
  clientOperation: `${client}.${type}`,
554
561
  requestId: response.headers.get("x-request-id")
555
562
  });
@@ -711,16 +718,16 @@ function createStorefrontClient(options) {
711
718
  errors: void 0
712
719
  };
713
720
  if (!response.ok) {
714
- let errors2;
721
+ let errors3;
715
722
  try {
716
- errors2 = parseJSON(body);
723
+ errors3 = parseJSON(body);
717
724
  } catch (_e) {
718
- errors2 = [{ message: body }];
725
+ errors3 = [{ message: body }];
719
726
  }
720
- throwErrorWithGqlLink({ ...errorOptions, errors: errors2 });
727
+ throwErrorWithGqlLink({ ...errorOptions, errors: errors3 });
721
728
  }
722
- const { data, errors } = body;
723
- const gqlErrors = errors?.map(
729
+ const { data, errors: errors2 } = body;
730
+ const gqlErrors = errors2?.map(
724
731
  ({ message, ...rest }) => new GraphQLError(message, {
725
732
  ...rest,
726
733
  clientOperation: `storefront.${errorOptions.type}`,
@@ -807,10 +814,10 @@ var getStackOffset = (query) => {
807
814
  }
808
815
  return stackOffset;
809
816
  } ;
810
- function formatAPIResult(data, errors) {
817
+ function formatAPIResult(data, errors2) {
811
818
  return {
812
819
  ...data,
813
- ...errors && { errors }
820
+ ...errors2 && { errors: errors2 }
814
821
  };
815
822
  }
816
823
 
@@ -1009,7 +1016,9 @@ async function storefrontRedirect(options) {
1009
1016
  }
1010
1017
  try {
1011
1018
  const { urlRedirects } = await storefront.query(REDIRECT_QUERY, {
1012
- variables: { query: "path:" + redirectFrom }
1019
+ // The admin doesn't allow redirects to have a
1020
+ // trailing slash, so strip them all off
1021
+ variables: { query: "path:" + redirectFrom.replace(/\/+$/, "") }
1013
1022
  });
1014
1023
  const location = urlRedirects?.edges?.[0]?.node?.target;
1015
1024
  if (location) {
@@ -2255,6 +2264,7 @@ async function refreshToken({
2255
2264
  const newBody = new URLSearchParams();
2256
2265
  const customerAccount = session.get(CUSTOMER_ACCOUNT_SESSION_KEY);
2257
2266
  const refreshToken2 = customerAccount?.refreshToken;
2267
+ const idToken = customerAccount?.idToken;
2258
2268
  if (!refreshToken2)
2259
2269
  throw new BadRequest(
2260
2270
  "Unauthorized",
@@ -2291,7 +2301,7 @@ async function refreshToken({
2291
2301
  }
2292
2302
  });
2293
2303
  }
2294
- const { access_token, expires_in, id_token, refresh_token } = await response.json();
2304
+ const { access_token, expires_in, refresh_token } = await response.json();
2295
2305
  const accessToken = await exchangeAccessToken(
2296
2306
  access_token,
2297
2307
  customerAccountId,
@@ -2304,7 +2314,7 @@ async function refreshToken({
2304
2314
  // Store the date in future the token expires, separated by two minutes
2305
2315
  expiresAt: new Date((/* @__PURE__ */ new Date()).getTime() + (expires_in - 120) * 1e3).getTime() + "",
2306
2316
  refreshToken: refresh_token,
2307
- idToken: id_token
2317
+ idToken
2308
2318
  });
2309
2319
  await exchangeForStorefrontCustomerAccessToken();
2310
2320
  }
@@ -2548,18 +2558,18 @@ function createCustomerAccountClient({
2548
2558
  }
2549
2559
  throw authFailResponse;
2550
2560
  }
2551
- let errors;
2561
+ let errors2;
2552
2562
  try {
2553
- errors = parseJSON(body);
2563
+ errors2 = parseJSON(body);
2554
2564
  } catch (_e) {
2555
- errors = [{ message: body }];
2565
+ errors2 = [{ message: body }];
2556
2566
  }
2557
- throwErrorWithGqlLink({ ...errorOptions, errors });
2567
+ throwErrorWithGqlLink({ ...errorOptions, errors: errors2 });
2558
2568
  }
2559
2569
  try {
2560
2570
  const APIresponse = parseJSON(body);
2561
- const { errors } = APIresponse;
2562
- const gqlErrors = errors?.map(
2571
+ const { errors: errors2 } = APIresponse;
2572
+ const gqlErrors = errors2?.map(
2563
2573
  ({ message, ...rest }) => new GraphQLError(message, {
2564
2574
  ...rest,
2565
2575
  clientOperation: `customerAccount.${errorOptions.type}`,
@@ -2568,7 +2578,7 @@ function createCustomerAccountClient({
2568
2578
  query: query2
2569
2579
  })
2570
2580
  );
2571
- return { ...APIresponse, ...errors && { errors: gqlErrors } };
2581
+ return { ...APIresponse, ...errors2 && { errors: gqlErrors } };
2572
2582
  } catch (e) {
2573
2583
  throwErrorWithGqlLink({ ...errorOptions, errors: [{ message: body }] });
2574
2584
  }
@@ -2718,7 +2728,6 @@ function createCustomerAccountClient({
2718
2728
  ).toString() : postLogoutRedirectUri;
2719
2729
  clearSession(session);
2720
2730
  return redirect(logoutUrl, {
2721
- status: 302,
2722
2731
  headers: {
2723
2732
  "Set-Cookie": await session.commit()
2724
2733
  }
@@ -2940,7 +2949,7 @@ function cartCreateDefault(options) {
2940
2949
  const buyer = options.customerAccount ? await options.customerAccount.UNSTABLE_getBuyer() : void 0;
2941
2950
  const { cartId, ...restOfOptionalParams } = optionalParams || {};
2942
2951
  const { buyerIdentity, ...restOfInput } = input;
2943
- const { cartCreate, errors } = await options.storefront.mutate(CART_CREATE_MUTATION(options.cartFragment), {
2952
+ const { cartCreate, errors: errors2 } = await options.storefront.mutate(CART_CREATE_MUTATION(options.cartFragment), {
2944
2953
  variables: {
2945
2954
  input: {
2946
2955
  ...restOfInput,
@@ -2952,7 +2961,7 @@ function cartCreateDefault(options) {
2952
2961
  ...restOfOptionalParams
2953
2962
  }
2954
2963
  });
2955
- return formatAPIResult(cartCreate, errors);
2964
+ return formatAPIResult(cartCreate, errors2);
2956
2965
  };
2957
2966
  }
2958
2967
  var CART_CREATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -2986,7 +2995,7 @@ function cartGetDefault({
2986
2995
  const cartId = getCartId();
2987
2996
  if (!cartId)
2988
2997
  return null;
2989
- const [isCustomerLoggedIn, { cart, errors }] = await Promise.all([
2998
+ const [isCustomerLoggedIn, { cart, errors: errors2 }] = await Promise.all([
2990
2999
  customerAccount ? customerAccount.isLoggedIn() : false,
2991
3000
  storefront.query(CART_QUERY(cartFragment), {
2992
3001
  variables: {
@@ -2998,7 +3007,7 @@ function cartGetDefault({
2998
3007
  ]);
2999
3008
  return formatAPIResult(
3000
3009
  addCustomerLoggedInParam(isCustomerLoggedIn, cart),
3001
- errors
3010
+ errors2
3002
3011
  );
3003
3012
  };
3004
3013
  }
@@ -3137,14 +3146,14 @@ var DEFAULT_CART_FRAGMENT = `#graphql
3137
3146
  // src/cart/queries/cartLinesAddDefault.ts
3138
3147
  function cartLinesAddDefault(options) {
3139
3148
  return async (lines, optionalParams) => {
3140
- const { cartLinesAdd, errors } = await options.storefront.mutate(CART_LINES_ADD_MUTATION(options.cartFragment), {
3149
+ const { cartLinesAdd, errors: errors2 } = await options.storefront.mutate(CART_LINES_ADD_MUTATION(options.cartFragment), {
3141
3150
  variables: {
3142
3151
  cartId: options.getCartId(),
3143
3152
  lines,
3144
3153
  ...optionalParams
3145
3154
  }
3146
3155
  });
3147
- return formatAPIResult(cartLinesAdd, errors);
3156
+ return formatAPIResult(cartLinesAdd, errors2);
3148
3157
  };
3149
3158
  }
3150
3159
  var CART_LINES_ADD_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3168,17 +3177,36 @@ var CART_LINES_ADD_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphq
3168
3177
  ${USER_ERROR_FRAGMENT}
3169
3178
  `;
3170
3179
 
3180
+ // src/cart/optimistic/optimistic-cart.helper.ts
3181
+ var PENDING_PREFIX = "__h_pending_";
3182
+ function getOptimisticLineId(variantId) {
3183
+ return PENDING_PREFIX + variantId;
3184
+ }
3185
+ function isOptimisticLineId(lineId) {
3186
+ return lineId.startsWith(PENDING_PREFIX);
3187
+ }
3188
+ function throwIfLinesAreOptimistic(type, lines) {
3189
+ if (lines.some(
3190
+ (line) => isOptimisticLineId(typeof line === "string" ? line : line.id)
3191
+ )) {
3192
+ throw new Error(
3193
+ `Tried to perform an action on an optimistic line. Make sure to disable your "${type}" CartForm action when the line is optimistic.`
3194
+ );
3195
+ }
3196
+ }
3197
+
3171
3198
  // src/cart/queries/cartLinesUpdateDefault.ts
3172
3199
  function cartLinesUpdateDefault(options) {
3173
3200
  return async (lines, optionalParams) => {
3174
- const { cartLinesUpdate, errors } = await options.storefront.mutate(CART_LINES_UPDATE_MUTATION(options.cartFragment), {
3201
+ throwIfLinesAreOptimistic("updateLines", lines);
3202
+ const { cartLinesUpdate, errors: errors2 } = await options.storefront.mutate(CART_LINES_UPDATE_MUTATION(options.cartFragment), {
3175
3203
  variables: {
3176
3204
  cartId: options.getCartId(),
3177
3205
  lines,
3178
3206
  ...optionalParams
3179
3207
  }
3180
3208
  });
3181
- return formatAPIResult(cartLinesUpdate, errors);
3209
+ return formatAPIResult(cartLinesUpdate, errors2);
3182
3210
  };
3183
3211
  }
3184
3212
  var CART_LINES_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3205,14 +3233,15 @@ var CART_LINES_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#gra
3205
3233
  // src/cart/queries/cartLinesRemoveDefault.ts
3206
3234
  function cartLinesRemoveDefault(options) {
3207
3235
  return async (lineIds, optionalParams) => {
3208
- const { cartLinesRemove, errors } = await options.storefront.mutate(CART_LINES_REMOVE_MUTATION(options.cartFragment), {
3236
+ throwIfLinesAreOptimistic("removeLines", lineIds);
3237
+ const { cartLinesRemove, errors: errors2 } = await options.storefront.mutate(CART_LINES_REMOVE_MUTATION(options.cartFragment), {
3209
3238
  variables: {
3210
3239
  cartId: options.getCartId(),
3211
3240
  lineIds,
3212
3241
  ...optionalParams
3213
3242
  }
3214
3243
  });
3215
- return formatAPIResult(cartLinesRemove, errors);
3244
+ return formatAPIResult(cartLinesRemove, errors2);
3216
3245
  };
3217
3246
  }
3218
3247
  var CART_LINES_REMOVE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3242,14 +3271,14 @@ function cartDiscountCodesUpdateDefault(options) {
3242
3271
  const uniqueCodes = discountCodes.filter((value, index, array) => {
3243
3272
  return array.indexOf(value) === index;
3244
3273
  });
3245
- const { cartDiscountCodesUpdate, errors } = await options.storefront.mutate(CART_DISCOUNT_CODE_UPDATE_MUTATION(options.cartFragment), {
3274
+ const { cartDiscountCodesUpdate, errors: errors2 } = await options.storefront.mutate(CART_DISCOUNT_CODE_UPDATE_MUTATION(options.cartFragment), {
3246
3275
  variables: {
3247
3276
  cartId: options.getCartId(),
3248
3277
  discountCodes: uniqueCodes,
3249
3278
  ...optionalParams
3250
3279
  }
3251
3280
  });
3252
- return formatAPIResult(cartDiscountCodesUpdate, errors);
3281
+ return formatAPIResult(cartDiscountCodesUpdate, errors2);
3253
3282
  };
3254
3283
  }
3255
3284
  var CART_DISCOUNT_CODE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3281,7 +3310,7 @@ function cartBuyerIdentityUpdateDefault(options) {
3281
3310
  });
3282
3311
  }
3283
3312
  const buyer = options.customerAccount ? await options.customerAccount.UNSTABLE_getBuyer() : void 0;
3284
- const { cartBuyerIdentityUpdate, errors } = await options.storefront.mutate(CART_BUYER_IDENTITY_UPDATE_MUTATION(options.cartFragment), {
3313
+ const { cartBuyerIdentityUpdate, errors: errors2 } = await options.storefront.mutate(CART_BUYER_IDENTITY_UPDATE_MUTATION(options.cartFragment), {
3285
3314
  variables: {
3286
3315
  cartId: options.getCartId(),
3287
3316
  buyerIdentity: {
@@ -3291,7 +3320,7 @@ function cartBuyerIdentityUpdateDefault(options) {
3291
3320
  ...optionalParams
3292
3321
  }
3293
3322
  });
3294
- return formatAPIResult(cartBuyerIdentityUpdate, errors);
3323
+ return formatAPIResult(cartBuyerIdentityUpdate, errors2);
3295
3324
  };
3296
3325
  }
3297
3326
  var CART_BUYER_IDENTITY_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3317,14 +3346,14 @@ var CART_BUYER_IDENTITY_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT)
3317
3346
  // src/cart/queries/cartNoteUpdateDefault.ts
3318
3347
  function cartNoteUpdateDefault(options) {
3319
3348
  return async (note, optionalParams) => {
3320
- const { cartNoteUpdate, errors } = await options.storefront.mutate(CART_NOTE_UPDATE_MUTATION(options.cartFragment), {
3349
+ const { cartNoteUpdate, errors: errors2 } = await options.storefront.mutate(CART_NOTE_UPDATE_MUTATION(options.cartFragment), {
3321
3350
  variables: {
3322
3351
  cartId: options.getCartId(),
3323
3352
  note,
3324
3353
  ...optionalParams
3325
3354
  }
3326
3355
  });
3327
- return formatAPIResult(cartNoteUpdate, errors);
3356
+ return formatAPIResult(cartNoteUpdate, errors2);
3328
3357
  };
3329
3358
  }
3330
3359
  var CART_NOTE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3350,14 +3379,14 @@ var CART_NOTE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#grap
3350
3379
  // src/cart/queries/cartSelectedDeliveryOptionsUpdateDefault.ts
3351
3380
  function cartSelectedDeliveryOptionsUpdateDefault(options) {
3352
3381
  return async (selectedDeliveryOptions, optionalParams) => {
3353
- const { cartSelectedDeliveryOptionsUpdate, errors } = await options.storefront.mutate(CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION(options.cartFragment), {
3382
+ const { cartSelectedDeliveryOptionsUpdate, errors: errors2 } = await options.storefront.mutate(CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION(options.cartFragment), {
3354
3383
  variables: {
3355
3384
  cartId: options.getCartId(),
3356
3385
  selectedDeliveryOptions,
3357
3386
  ...optionalParams
3358
3387
  }
3359
3388
  });
3360
- return formatAPIResult(cartSelectedDeliveryOptionsUpdate, errors);
3389
+ return formatAPIResult(cartSelectedDeliveryOptionsUpdate, errors2);
3361
3390
  };
3362
3391
  }
3363
3392
  var CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3383,13 +3412,13 @@ var CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION = (cartFragment = MINIMAL_CAR
3383
3412
  // src/cart/queries/cartAttributesUpdateDefault.ts
3384
3413
  function cartAttributesUpdateDefault(options) {
3385
3414
  return async (attributes, optionalParams) => {
3386
- const { cartAttributesUpdate, errors } = await options.storefront.mutate(CART_ATTRIBUTES_UPDATE_MUTATION(options.cartFragment), {
3415
+ const { cartAttributesUpdate, errors: errors2 } = await options.storefront.mutate(CART_ATTRIBUTES_UPDATE_MUTATION(options.cartFragment), {
3387
3416
  variables: {
3388
3417
  cartId: optionalParams?.cartId || options.getCartId(),
3389
3418
  attributes
3390
3419
  }
3391
3420
  });
3392
- return formatAPIResult(cartAttributesUpdate, errors);
3421
+ return formatAPIResult(cartAttributesUpdate, errors2);
3393
3422
  };
3394
3423
  }
3395
3424
  var CART_ATTRIBUTES_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
@@ -3420,7 +3449,7 @@ function cartMetafieldsSetDefault(options) {
3420
3449
  ownerId
3421
3450
  })
3422
3451
  );
3423
- const { cartMetafieldsSet, errors } = await options.storefront.mutate(CART_METAFIELD_SET_MUTATION(), {
3452
+ const { cartMetafieldsSet, errors: errors2 } = await options.storefront.mutate(CART_METAFIELD_SET_MUTATION(), {
3424
3453
  variables: { metafields: metafieldsWithOwnerId }
3425
3454
  });
3426
3455
  return formatAPIResult(
@@ -3430,7 +3459,7 @@ function cartMetafieldsSetDefault(options) {
3430
3459
  },
3431
3460
  ...cartMetafieldsSet
3432
3461
  },
3433
- errors
3462
+ errors2
3434
3463
  );
3435
3464
  };
3436
3465
  }
@@ -3455,7 +3484,7 @@ var CART_METAFIELD_SET_MUTATION = () => `#graphql
3455
3484
  function cartMetafieldDeleteDefault(options) {
3456
3485
  return async (key, optionalParams) => {
3457
3486
  const ownerId = optionalParams?.cartId || options.getCartId();
3458
- const { cartMetafieldDelete, errors } = await options.storefront.mutate(CART_METAFIELD_DELETE_MUTATION(), {
3487
+ const { cartMetafieldDelete, errors: errors2 } = await options.storefront.mutate(CART_METAFIELD_DELETE_MUTATION(), {
3459
3488
  variables: {
3460
3489
  input: {
3461
3490
  ownerId,
@@ -3470,7 +3499,7 @@ function cartMetafieldDeleteDefault(options) {
3470
3499
  },
3471
3500
  ...cartMetafieldDelete
3472
3501
  },
3473
- errors
3502
+ errors2
3474
3503
  );
3475
3504
  };
3476
3505
  }
@@ -3487,51 +3516,18 @@ var CART_METAFIELD_DELETE_MUTATION = () => `#graphql
3487
3516
  }
3488
3517
  }
3489
3518
  `;
3490
-
3491
- // ../../node_modules/worktop/cookie/index.mjs
3492
- var g = /* @__PURE__ */ new Set([
3493
- "domain",
3494
- "path",
3495
- "max-age",
3496
- "expires",
3497
- "samesite",
3498
- "secure",
3499
- "httponly"
3500
- ]);
3501
- function u(a) {
3502
- let r = {}, e, t, n = 0, m = a.split(/;\s*/g), s, i;
3503
- for (; n < m.length; n++)
3504
- if (t = m[n], e = t.indexOf("="), ~e) {
3505
- if (s = t.substring(0, e++).trim(), i = t.substring(e).trim(), i[0] === '"' && (i = i.substring(1, i.length - 1)), ~i.indexOf("%"))
3506
- try {
3507
- i = decodeURIComponent(i);
3508
- } catch (f) {
3509
- }
3510
- g.has(t = s.toLowerCase()) ? t === "expires" ? r.expires = new Date(i) : t === "max-age" ? r.maxage = +i : r[t] = i : r[s] = i;
3511
- } else
3512
- (s = t.trim().toLowerCase()) && (s === "httponly" || s === "secure") && (r[s] = true);
3513
- return r;
3514
- }
3515
- function l(a, r, e = {}) {
3516
- let t = a + "=" + encodeURIComponent(r);
3517
- 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;
3518
- }
3519
-
3520
- // src/cart/cartGetIdDefault.ts
3521
3519
  var cartGetIdDefault = (requestHeaders) => {
3522
- const cookies = u(requestHeaders.get("Cookie") || "");
3520
+ const cookies = parse(requestHeaders.get("Cookie") || "");
3523
3521
  return () => {
3524
3522
  return cookies.cart ? `gid://shopify/Cart/${cookies.cart}` : void 0;
3525
3523
  };
3526
3524
  };
3527
-
3528
- // src/cart/cartSetIdDefault.ts
3529
3525
  var cartSetIdDefault = (cookieOptions) => {
3530
3526
  return (cartId) => {
3531
3527
  const headers = new Headers();
3532
3528
  headers.append(
3533
3529
  "Set-Cookie",
3534
- l("cart", cartId.split("/").pop() || "", {
3530
+ stringify("cart", cartId.split("/").pop() || "", {
3535
3531
  path: "/",
3536
3532
  ...cookieOptions
3537
3533
  })
@@ -3574,7 +3570,15 @@ function createCartHandler(options) {
3574
3570
  getCartId,
3575
3571
  setCartId,
3576
3572
  create: cartCreate,
3577
- addLines: async (lines, optionalParams) => {
3573
+ addLines: async (linesWithOptimisticData, optionalParams) => {
3574
+ const lines = linesWithOptimisticData.map((line) => {
3575
+ return {
3576
+ attributes: line.attributes,
3577
+ quantity: line.quantity,
3578
+ merchandiseId: line.merchandiseId,
3579
+ sellingPlanId: line.sellingPlanId
3580
+ };
3581
+ });
3578
3582
  return cartId || optionalParams?.cartId ? await cartLinesAddDefault(mutateOptions)(lines, optionalParams) : await cartCreate({ lines }, optionalParams);
3579
3583
  },
3580
3584
  updateLines: cartLinesUpdateDefault(mutateOptions),
@@ -3618,6 +3622,89 @@ function createCartHandler(options) {
3618
3622
  return methods;
3619
3623
  }
3620
3624
  }
3625
+ function useOptimisticCart(cart) {
3626
+ const fetchers = useFetchers();
3627
+ if (!fetchers || !fetchers.length)
3628
+ return cart;
3629
+ const optimisticCart = cart?.lines ? structuredClone(cart) : { lines: { nodes: [] } };
3630
+ const cartLines = optimisticCart.lines.nodes;
3631
+ let isOptimistic = false;
3632
+ for (const { formData } of fetchers) {
3633
+ if (!formData)
3634
+ continue;
3635
+ const cartFormData = CartForm.getFormInput(formData);
3636
+ if (cartFormData.action === CartForm.ACTIONS.LinesAdd) {
3637
+ for (const input of cartFormData.inputs.lines) {
3638
+ if (!input.selectedVariant) {
3639
+ console.error(
3640
+ "[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"
3641
+ );
3642
+ continue;
3643
+ }
3644
+ const existingLine = cartLines.find(
3645
+ (line) => line.merchandise.id === input.selectedVariant?.id
3646
+ );
3647
+ isOptimistic = true;
3648
+ if (existingLine) {
3649
+ existingLine.quantity = (existingLine.quantity || 1) + (input.quantity || 1);
3650
+ existingLine.isOptimistic = true;
3651
+ } else {
3652
+ cartLines.unshift({
3653
+ id: getOptimisticLineId(input.selectedVariant.id),
3654
+ merchandise: input.selectedVariant,
3655
+ isOptimistic: true,
3656
+ quantity: input.quantity || 1
3657
+ });
3658
+ }
3659
+ }
3660
+ } else if (cartFormData.action === CartForm.ACTIONS.LinesRemove) {
3661
+ for (const lineId of cartFormData.inputs.lineIds) {
3662
+ const index = cartLines.findIndex((line) => line.id === lineId);
3663
+ if (index !== -1) {
3664
+ if (isOptimisticLineId(cartLines[index].id)) {
3665
+ console.error(
3666
+ "[h2:error:useOptimisticCart] Tried to remove an optimistic line that has not been added to the cart yet"
3667
+ );
3668
+ continue;
3669
+ }
3670
+ cartLines.splice(index, 1);
3671
+ isOptimistic = true;
3672
+ } else {
3673
+ console.warn(
3674
+ `[h2:warn:useOptimisticCart] Tried to remove line '${lineId}' but it doesn't exist in the cart`
3675
+ );
3676
+ }
3677
+ }
3678
+ } else if (cartFormData.action === CartForm.ACTIONS.LinesUpdate) {
3679
+ for (const line of cartFormData.inputs.lines) {
3680
+ const index = cartLines.findIndex(
3681
+ (optimisticLine) => line.id === optimisticLine.id
3682
+ );
3683
+ if (index > -1) {
3684
+ if (isOptimisticLineId(cartLines[index].id)) {
3685
+ console.error(
3686
+ "[h2:error:useOptimisticCart] Tried to update an optimistic line that has not been added to the cart yet"
3687
+ );
3688
+ continue;
3689
+ }
3690
+ cartLines[index].quantity = line.quantity;
3691
+ if (cartLines[index].quantity === 0) {
3692
+ cartLines.splice(index, 1);
3693
+ }
3694
+ isOptimistic = true;
3695
+ } else {
3696
+ console.warn(
3697
+ `[h2:warn:useOptimisticCart] Tried to update line '${line.id}' but it doesn't exist in the cart`
3698
+ );
3699
+ }
3700
+ }
3701
+ }
3702
+ }
3703
+ if (isOptimistic) {
3704
+ optimisticCart.isOptimistic = isOptimistic;
3705
+ }
3706
+ return optimisticCart;
3707
+ }
3621
3708
  function VariantSelector({
3622
3709
  handle,
3623
3710
  options = [],
@@ -3667,7 +3754,8 @@ function VariantSelector({
3667
3754
  isAvailable: variant ? variant.availableForSale : true,
3668
3755
  to: path + searchString,
3669
3756
  search: searchString,
3670
- isActive: calculatedActiveValue
3757
+ isActive: calculatedActiveValue,
3758
+ variant
3671
3759
  });
3672
3760
  }
3673
3761
  return children({
@@ -3833,21 +3921,33 @@ function ShopPayButton(props) {
3833
3921
  function AnalyticsView(props) {
3834
3922
  const { type, data = {}, customData } = props;
3835
3923
  const location = useLocation();
3836
- const { publish: publish2, cart, prevCart, shop } = useAnalytics();
3924
+ const {
3925
+ publish: publish2,
3926
+ cart,
3927
+ prevCart,
3928
+ shop,
3929
+ customData: analyticProviderCustomData
3930
+ } = useAnalytics();
3837
3931
  const url = location.pathname + location.search;
3932
+ let viewPayload2 = {
3933
+ ...data,
3934
+ customData: {
3935
+ ...analyticProviderCustomData,
3936
+ ...customData
3937
+ },
3938
+ cart,
3939
+ prevCart,
3940
+ shop
3941
+ };
3838
3942
  useEffect(() => {
3839
- if (!shop)
3943
+ if (!shop?.shopId)
3840
3944
  return;
3841
- const viewPayload2 = {
3842
- ...data,
3843
- customData,
3844
- url: window.location.href,
3845
- cart,
3846
- prevCart,
3847
- shop
3945
+ viewPayload2 = {
3946
+ ...viewPayload2,
3947
+ url: window.location.href
3848
3948
  };
3849
3949
  publish2(type, viewPayload2);
3850
- }, [publish2, url, cart, prevCart, shop]);
3950
+ }, [publish2, url, shop?.shopId]);
3851
3951
  return null;
3852
3952
  }
3853
3953
  function AnalyticsPageView(props) {
@@ -3888,7 +3988,7 @@ var CONSENT_API = "https://cdn.shopify.com/shopifycloud/consent-tracking-api/v0.
3888
3988
  var CONSENT_API_WITH_BANNER = "https://cdn.shopify.com/shopifycloud/privacy-banner/storefront-banner.js";
3889
3989
  function logMissingConfig(fieldName) {
3890
3990
  console.error(
3891
- `[h2:error:useCustomerPrivacy] Unable to setup Customer Privacy API: Missing consent.${fieldName} consent configuration.`
3991
+ `[h2:error:useCustomerPrivacy] Unable to setup Customer Privacy API: Missing consent.${fieldName} configuration.`
3892
3992
  );
3893
3993
  }
3894
3994
  function useCustomerPrivacy(props) {
@@ -3907,10 +4007,6 @@ function useCustomerPrivacy(props) {
3907
4007
  }
3908
4008
  }
3909
4009
  );
3910
- if (!consentConfig.checkoutDomain)
3911
- logMissingConfig("checkoutDomain");
3912
- if (!consentConfig.storefrontAccessToken)
3913
- logMissingConfig("storefrontAccessToken");
3914
4010
  useEffect(() => {
3915
4011
  const consentCollectedHandler = (event) => {
3916
4012
  if (onVisitorConsentCollected) {
@@ -3932,29 +4028,37 @@ function useCustomerPrivacy(props) {
3932
4028
  if (scriptStatus !== "done" || loadedEvent.current)
3933
4029
  return;
3934
4030
  loadedEvent.current = true;
4031
+ if (!consentConfig.checkoutDomain)
4032
+ logMissingConfig("checkoutDomain");
4033
+ if (!consentConfig.storefrontAccessToken)
4034
+ logMissingConfig("storefrontAccessToken");
4035
+ if (consentConfig.storefrontAccessToken.startsWith("shpat_") || consentConfig.storefrontAccessToken.length !== 32) {
4036
+ console.error(
4037
+ `[h2:error:useCustomerPrivacy] It looks like you passed a private access token, make sure to use the public token`
4038
+ );
4039
+ }
3935
4040
  if (withPrivacyBanner && window?.privacyBanner) {
3936
4041
  window?.privacyBanner?.loadBanner({
3937
4042
  checkoutRootDomain: consentConfig.checkoutDomain,
3938
4043
  storefrontAccessToken: consentConfig.storefrontAccessToken
3939
4044
  });
3940
4045
  }
3941
- if (window.Shopify?.customerPrivacy) {
3942
- const originalSetTrackingConsent = window.Shopify.customerPrivacy.setTrackingConsent;
3943
- window.Shopify.customerPrivacy.setTrackingConsent = (consent, callback) => {
3944
- originalSetTrackingConsent(
3945
- {
3946
- ...consent,
3947
- headlessStorefront: true,
3948
- checkoutRootDomain: consentConfig.checkoutDomain,
3949
- storefrontAccessToken: consentConfig.storefrontAccessToken
3950
- },
3951
- callback
3952
- );
3953
- };
3954
- }
3955
- if (onReady && !withPrivacyBanner) {
3956
- onReady();
4046
+ if (!window.Shopify?.customerPrivacy)
4047
+ return;
4048
+ const originalSetTrackingConsent = window.Shopify.customerPrivacy.setTrackingConsent;
4049
+ function overrideSetTrackingConsent(consent, callback) {
4050
+ originalSetTrackingConsent(
4051
+ {
4052
+ ...consent,
4053
+ headlessStorefront: true,
4054
+ checkoutRootDomain: consentConfig.checkoutDomain,
4055
+ storefrontAccessToken: consentConfig.storefrontAccessToken
4056
+ },
4057
+ callback
4058
+ );
3957
4059
  }
4060
+ window.Shopify.customerPrivacy.setTrackingConsent = overrideSetTrackingConsent;
4061
+ onReady && onReady();
3958
4062
  }, [scriptStatus, withPrivacyBanner, consentConfig]);
3959
4063
  return;
3960
4064
  }
@@ -3976,27 +4080,38 @@ function getCustomerPrivacyRequired() {
3976
4080
  }
3977
4081
  function ShopifyAnalytics({
3978
4082
  consent,
3979
- onReady
4083
+ onReady,
4084
+ domain
3980
4085
  }) {
3981
4086
  const { subscribe: subscribe2, register: register2, canTrack } = useAnalytics();
4087
+ const [shopifyReady, setShopifyReady] = useState(false);
4088
+ const [privacyReady, setPrivacyReady] = useState(false);
3982
4089
  const { ready: shopifyAnalyticsReady } = register2("Internal_Shopify_Analytics");
3983
4090
  const { ready: customerPrivacyReady } = register2(
3984
4091
  "Internal_Shopify_CustomerPrivacy"
3985
4092
  );
3986
4093
  const analyticsReady = () => {
4094
+ shopifyReady && privacyReady && onReady();
4095
+ };
4096
+ const setCustomerPrivacyReady = () => {
4097
+ setPrivacyReady(true);
3987
4098
  customerPrivacyReady();
3988
- onReady();
4099
+ analyticsReady();
3989
4100
  };
4101
+ const { checkoutDomain, storefrontAccessToken, withPrivacyBanner } = consent;
3990
4102
  useCustomerPrivacy({
3991
- ...consent,
3992
- onVisitorConsentCollected: analyticsReady,
4103
+ checkoutDomain: !checkoutDomain ? "mock.shop" : checkoutDomain,
4104
+ storefrontAccessToken: !storefrontAccessToken ? "abcdefghijklmnopqrstuvwxyz123456" : storefrontAccessToken,
4105
+ withPrivacyBanner,
4106
+ onVisitorConsentCollected: setCustomerPrivacyReady,
3993
4107
  onReady: () => {
3994
- if (!consent.withPrivacyBanner) {
3995
- analyticsReady();
3996
- }
4108
+ setTimeout(setCustomerPrivacyReady, 3e3);
3997
4109
  }
3998
4110
  });
3999
- useShopifyCookies({ hasUserConsent: canTrack() });
4111
+ useShopifyCookies({
4112
+ hasUserConsent: canTrack(),
4113
+ domain
4114
+ });
4000
4115
  useEffect(() => {
4001
4116
  subscribe2(AnalyticsEvent.PAGE_VIEWED, pageViewHandler);
4002
4117
  subscribe2(AnalyticsEvent.PRODUCT_VIEWED, productViewHandler);
@@ -4004,6 +4119,8 @@ function ShopifyAnalytics({
4004
4119
  subscribe2(AnalyticsEvent.SEARCH_VIEWED, searchViewHandler);
4005
4120
  subscribe2(AnalyticsEvent.PRODUCT_ADD_TO_CART, productAddedToCartHandler);
4006
4121
  shopifyAnalyticsReady();
4122
+ setShopifyReady(true);
4123
+ analyticsReady();
4007
4124
  }, [subscribe2, shopifyAnalyticsReady]);
4008
4125
  return null;
4009
4126
  }
@@ -4422,8 +4539,8 @@ function shopifyCanTrack() {
4422
4539
  }
4423
4540
  return false;
4424
4541
  }
4425
- function messageOnError(field) {
4426
- return `[h2:error:Analytics.Provider] - ${field} is required`;
4542
+ function messageOnError(field, envVar) {
4543
+ 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.`;
4427
4544
  }
4428
4545
  function AnalyticsProvider({
4429
4546
  canTrack: customCanTrack,
@@ -4432,24 +4549,9 @@ function AnalyticsProvider({
4432
4549
  consent,
4433
4550
  customData = {},
4434
4551
  shop: shopProp = null,
4435
- disableThrowOnError = false
4552
+ disableThrowOnError = false,
4553
+ cookieDomain
4436
4554
  }) {
4437
- if (!consent.checkoutDomain) {
4438
- const errorMsg = messageOnError("consent.checkoutDomain");
4439
- if (disableThrowOnError) {
4440
- console.error(errorMsg);
4441
- } else {
4442
- invariant(false, errorMsg);
4443
- }
4444
- }
4445
- if (!consent.storefrontAccessToken) {
4446
- const errorMsg = messageOnError("consent.storefrontAccessToken");
4447
- if (disableThrowOnError) {
4448
- console.error(errorMsg);
4449
- } else {
4450
- invariant(false, errorMsg);
4451
- }
4452
- }
4453
4555
  const listenerSet = useRef(false);
4454
4556
  const { shop } = useShopAnalytics(shopProp);
4455
4557
  const [consentLoaded, setConsentLoaded] = useState(
@@ -4459,6 +4561,28 @@ function AnalyticsProvider({
4459
4561
  const [canTrack, setCanTrack] = useState(
4460
4562
  customCanTrack ? () => customCanTrack : () => shopifyCanTrack
4461
4563
  );
4564
+ if (!!shop) {
4565
+ if (/\/68817551382$/.test(shop.shopId)) {
4566
+ warnOnce(
4567
+ "[h2:error:Analytics.Provider] - Mock shop is used. Analytics will not work properly."
4568
+ );
4569
+ } else {
4570
+ if (!consent.checkoutDomain) {
4571
+ const errorMsg = messageOnError(
4572
+ "consent.checkoutDomain",
4573
+ "PUBLIC_CHECKOUT_DOMAIN"
4574
+ );
4575
+ errorOnce(errorMsg);
4576
+ }
4577
+ if (!consent.storefrontAccessToken) {
4578
+ const errorMsg = messageOnError(
4579
+ "consent.storefrontAccessToken",
4580
+ "PUBLIC_STOREFRONT_API_TOKEN"
4581
+ );
4582
+ errorOnce(errorMsg);
4583
+ }
4584
+ }
4585
+ }
4462
4586
  const value = useMemo(() => {
4463
4587
  return {
4464
4588
  canTrack,
@@ -4489,7 +4613,7 @@ function AnalyticsProvider({
4489
4613
  children,
4490
4614
  !!shop && /* @__PURE__ */ jsx(AnalyticsPageView, {}),
4491
4615
  !!shop && !!currentCart && /* @__PURE__ */ jsx(CartAnalytics, { cart: currentCart, setCarts }),
4492
- !!shop && /* @__PURE__ */ jsx(
4616
+ !!shop && consent.checkoutDomain && /* @__PURE__ */ jsx(
4493
4617
  ShopifyAnalytics,
4494
4618
  {
4495
4619
  consent,
@@ -4497,7 +4621,8 @@ function AnalyticsProvider({
4497
4621
  listenerSet.current = true;
4498
4622
  setConsentLoaded(true);
4499
4623
  setCanTrack(() => shopifyCanTrack);
4500
- }
4624
+ },
4625
+ domain: cookieDomain
4501
4626
  }
4502
4627
  )
4503
4628
  ] });
@@ -4563,6 +4688,27 @@ var Analytics = {
4563
4688
  Provider: AnalyticsProvider,
4564
4689
  SearchView: AnalyticsSearchView
4565
4690
  };
4691
+ var RichText = function(props) {
4692
+ return /* @__PURE__ */ jsx(
4693
+ RichText$1,
4694
+ {
4695
+ ...props,
4696
+ components: {
4697
+ link: ({ node }) => /* @__PURE__ */ jsx(
4698
+ Link,
4699
+ {
4700
+ to: node.url,
4701
+ title: node.title,
4702
+ target: node.target,
4703
+ prefetch: "intent",
4704
+ children: node.children
4705
+ }
4706
+ ),
4707
+ ...props.components
4708
+ }
4709
+ }
4710
+ );
4711
+ };
4566
4712
  //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartCreate
4567
4713
  //! @see https://shopify.dev/docs/api/storefront/latest/queries/cart
4568
4714
  //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesAdd
@@ -4575,6 +4721,6 @@ var Analytics = {
4575
4721
  //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartMetafieldsSet
4576
4722
  //! @see https://shopify.dev/docs/api/storefront/2024-04/mutations/cartMetafieldDelete
4577
4723
 
4578
- export { AnalyticsEvent, CacheCustom, CacheLong, CacheNone, CacheShort, CartForm, InMemoryCache, OptimisticInput, Pagination, Script, Seo, ShopPayButton, Analytics as UNSTABLE_Analytics, VariantSelector, cartAttributesUpdateDefault, cartBuyerIdentityUpdateDefault, cartCreateDefault, cartDiscountCodesUpdateDefault, cartGetDefault, cartGetIdDefault, cartLinesAddDefault, cartLinesRemoveDefault, cartLinesUpdateDefault, cartMetafieldDeleteDefault, cartMetafieldsSetDefault, cartNoteUpdateDefault, cartSelectedDeliveryOptionsUpdateDefault, cartSetIdDefault, changelogHandler, createCartHandler, createContentSecurityPolicy, createCustomerAccountClient, createStorefrontClient, createWithCache, formatAPIResult, generateCacheControlHeader, getCustomerPrivacy, getPaginationVariables, getSelectedProductOptions, getSeoMeta, getShopAnalytics, graphiqlLoader, storefrontRedirect, useAnalytics as unstable_useAnalytics, useCustomerPrivacy, useNonce, useOptimisticData };
4724
+ export { Analytics, AnalyticsEvent, CacheCustom, CacheLong, CacheNone, CacheShort, CartForm, InMemoryCache, OptimisticInput, Pagination, RichText, Script, Seo, ShopPayButton, VariantSelector, cartAttributesUpdateDefault, cartBuyerIdentityUpdateDefault, cartCreateDefault, cartDiscountCodesUpdateDefault, cartGetDefault, cartGetIdDefault, cartLinesAddDefault, cartLinesRemoveDefault, cartLinesUpdateDefault, cartMetafieldDeleteDefault, cartMetafieldsSetDefault, cartNoteUpdateDefault, cartSelectedDeliveryOptionsUpdateDefault, cartSetIdDefault, changelogHandler, createCartHandler, createContentSecurityPolicy, createCustomerAccountClient, createStorefrontClient, createWithCache, formatAPIResult, generateCacheControlHeader, getCustomerPrivacy, getPaginationVariables, getSelectedProductOptions, getSeoMeta, getShopAnalytics, graphiqlLoader, storefrontRedirect, useAnalytics, useCustomerPrivacy, useNonce, useOptimisticCart, useOptimisticData };
4579
4725
  //# sourceMappingURL=out.js.map
4580
4726
  //# sourceMappingURL=index.js.map