@shopify/hydrogen 2024.4.0 → 2024.4.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.
@@ -4,6 +4,7 @@ import { lazy, createContext, forwardRef, useMemo, createElement, Suspense, Frag
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
6
  import cspBuilder from 'content-security-policy-builder';
7
+ import invariant from 'tiny-invariant';
7
8
 
8
9
  // src/storefront.ts
9
10
 
@@ -241,9 +242,10 @@ async function runWithCache(cacheKey, actionFn, {
241
242
  // '__HYDROGEN_CACHE_ID__', // TODO purgeQueryCacheOnBuild
242
243
  ...typeof cacheKey === "string" ? [cacheKey] : cacheKey
243
244
  ]);
244
- let debugData;
245
+ let cachedDebugInfo;
246
+ let userDebugInfo;
245
247
  const addDebugData = (info) => {
246
- debugData = {
248
+ userDebugInfo = {
247
249
  displayName: info.displayName,
248
250
  url: info.response?.url,
249
251
  responseInit: {
@@ -253,26 +255,31 @@ async function runWithCache(cacheKey, actionFn, {
253
255
  }
254
256
  };
255
257
  };
258
+ const mergeDebugInfo = () => ({
259
+ ...cachedDebugInfo,
260
+ ...debugInfo,
261
+ url: userDebugInfo?.url || debugInfo?.url || cachedDebugInfo?.url || getKeyUrl(key),
262
+ displayName: debugInfo?.displayName || userDebugInfo?.displayName || cachedDebugInfo?.displayName
263
+ });
256
264
  const logSubRequestEvent2 = ({
257
265
  result: result2,
258
266
  cacheStatus,
259
267
  overrideStartTime
260
268
  }) => {
261
269
  globalThis.__H2O_LOG_EVENT?.({
270
+ ...mergeDebugInfo(),
262
271
  eventType: "subrequest",
263
272
  startTime: overrideStartTime || startTime,
273
+ endTime: Date.now(),
264
274
  cacheStatus,
265
275
  responsePayload: result2 && result2[0] || result2,
266
- responseInit: result2 && result2[1] || debugData?.responseInit,
276
+ responseInit: result2 && result2[1] || userDebugInfo?.responseInit,
267
277
  cache: {
268
278
  status: cacheStatus,
269
279
  strategy: generateCacheControlHeader(strategy || {}),
270
280
  key
271
281
  },
272
- waitUntil,
273
- ...debugInfo,
274
- url: debugData?.url || debugInfo?.url || getKeyUrl(key),
275
- displayName: debugInfo?.displayName || debugData?.displayName
282
+ waitUntil
276
283
  });
277
284
  } ;
278
285
  if (!cacheInstance || !strategy || strategy.mode === NO_STORE) {
@@ -280,9 +287,19 @@ async function runWithCache(cacheKey, actionFn, {
280
287
  logSubRequestEvent2?.({ result: result2 });
281
288
  return result2;
282
289
  }
290
+ const storeInCache = (value) => setItemInCache(
291
+ cacheInstance,
292
+ key,
293
+ {
294
+ value,
295
+ debugInfo: mergeDebugInfo()
296
+ },
297
+ strategy
298
+ );
283
299
  const cachedItem = await getItemFromCache(cacheInstance, key);
284
- if (cachedItem) {
285
- const [cachedResult, cacheInfo] = cachedItem;
300
+ if (cachedItem && typeof cachedItem[0] !== "string") {
301
+ const [{ value: cachedResult, debugInfo: debugInfo2 }, cacheInfo] = cachedItem;
302
+ cachedDebugInfo = debugInfo2;
286
303
  const cacheStatus = isStale2(key, cacheInfo) ? "STALE" : "HIT";
287
304
  if (!swrLock.has(key) && cacheStatus === "STALE") {
288
305
  swrLock.add(key);
@@ -291,7 +308,7 @@ async function runWithCache(cacheKey, actionFn, {
291
308
  try {
292
309
  const result2 = await actionFn({ addDebugData });
293
310
  if (shouldCacheResult(result2)) {
294
- await setItemInCache(cacheInstance, key, result2, strategy);
311
+ await storeInCache(result2);
295
312
  logSubRequestEvent2?.({
296
313
  result: result2,
297
314
  cacheStatus: "PUT",
@@ -321,16 +338,16 @@ async function runWithCache(cacheKey, actionFn, {
321
338
  cacheStatus: "MISS"
322
339
  });
323
340
  if (shouldCacheResult(result)) {
324
- const setItemInCachePromise = Promise.resolve().then(async () => {
341
+ const cacheStoringPromise = Promise.resolve().then(async () => {
325
342
  const putStartTime = Date.now();
326
- await setItemInCache(cacheInstance, key, result, strategy);
343
+ await storeInCache(result);
327
344
  logSubRequestEvent2?.({
328
345
  result,
329
346
  cacheStatus: "PUT",
330
347
  overrideStartTime: putStartTime
331
348
  });
332
349
  });
333
- waitUntil?.(setItemInCachePromise);
350
+ waitUntil?.(cacheStoringPromise);
334
351
  }
335
352
  return result;
336
353
  }
@@ -372,12 +389,20 @@ async function fetchWithServerCache(url, requestInit, {
372
389
  ).then(fromSerializableResponse);
373
390
  }
374
391
 
392
+ // src/version.ts
393
+ var LIB_VERSION = "2024.4.2";
394
+
375
395
  // src/constants.ts
376
396
  var STOREFRONT_REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
377
397
  var STOREFRONT_ACCESS_TOKEN_HEADER = "X-Shopify-Storefront-Access-Token";
378
398
  var SDK_VARIANT_HEADER = "X-SDK-Variant";
379
399
  var SDK_VARIANT_SOURCE_HEADER = "X-SDK-Variant-Source";
380
400
  var SDK_VERSION_HEADER = "X-SDK-Version";
401
+ var DEFAULT_CUSTOMER_API_VERSION = "2024-04";
402
+ var USER_AGENT = `Shopify Hydrogen ${LIB_VERSION}`;
403
+ var CUSTOMER_API_CLIENT_ID = "30243aa5-17c1-465a-8493-944bcc4e88aa";
404
+ var CUSTOMER_ACCOUNT_SESSION_KEY = "customerAccount";
405
+ var BUYER_SESSION_KEY = "buyer";
381
406
 
382
407
  // src/utils/uuid.ts
383
408
  function generateUUID() {
@@ -397,9 +422,6 @@ var warnOnce = (string) => {
397
422
  }
398
423
  };
399
424
 
400
- // src/version.ts
401
- var LIB_VERSION = "2024.4.0";
402
-
403
425
  // src/utils/graphql.ts
404
426
  function minifyQuery(string) {
405
427
  return string.replace(/\s*#.*$/gm, "").replace(/\s+/gm, " ").trim();
@@ -672,12 +694,12 @@ function createStorefrontClient(options) {
672
694
  shouldCacheResponse: checkGraphQLErrors,
673
695
  waitUntil,
674
696
  debugInfo: {
675
- url,
676
- graphql: graphqlData,
677
697
  requestId: requestInit.headers[STOREFRONT_REQUEST_GROUP_ID_HEADER],
678
- purpose: storefrontHeaders?.purpose,
698
+ displayName,
699
+ url,
679
700
  stackInfo,
680
- displayName
701
+ graphql: graphqlData,
702
+ purpose: storefrontHeaders?.purpose
681
703
  }
682
704
  });
683
705
  const errorOptions = {
@@ -1708,6 +1730,9 @@ var SeoLogger = lazy(() => import('./log-seo-tags-IG37ONQ2.js'));
1708
1730
  function Seo({ debug }) {
1709
1731
  const matches = useMatches();
1710
1732
  const location = useLocation();
1733
+ console.warn(
1734
+ "[h2:warn:Seo] The `<Seo/>` component is deprecated. Use `getSeoMeta` instead.\nSee: https://shopify.dev/docs/api/hydrogen/2024-01/utilities/getseometa"
1735
+ );
1711
1736
  const seoConfig = useMemo(() => {
1712
1737
  return matches.flatMap((match) => {
1713
1738
  const { handle, ...routeMatch } = match;
@@ -2182,12 +2207,6 @@ function getPaginationVariables(request, options = { pageBy: 20 }) {
2182
2207
  return variables;
2183
2208
  }
2184
2209
 
2185
- // src/customer/constants.ts
2186
- var DEFAULT_CUSTOMER_API_VERSION = "2024-04";
2187
- var USER_AGENT = `Shopify Hydrogen ${LIB_VERSION}`;
2188
- var CUSTOMER_API_CLIENT_ID = "30243aa5-17c1-465a-8493-944bcc4e88aa";
2189
- var CUSTOMER_ACCOUNT_SESSION_KEY = "customerAccount";
2190
-
2191
2210
  // src/customer/BadRequest.ts
2192
2211
  var BadRequest = class extends Response {
2193
2212
  constructor(message, helpMessage, headers) {
@@ -2230,7 +2249,8 @@ async function refreshToken({
2230
2249
  customerAccountId,
2231
2250
  customerAccountUrl,
2232
2251
  httpsOrigin,
2233
- debugInfo
2252
+ debugInfo,
2253
+ exchangeForStorefrontCustomerAccessToken
2234
2254
  }) {
2235
2255
  const newBody = new URLSearchParams();
2236
2256
  const customerAccount = session.get(CUSTOMER_ACCOUNT_SESSION_KEY);
@@ -2286,9 +2306,11 @@ async function refreshToken({
2286
2306
  refreshToken: refresh_token,
2287
2307
  idToken: id_token
2288
2308
  });
2309
+ await exchangeForStorefrontCustomerAccessToken();
2289
2310
  }
2290
2311
  function clearSession(session) {
2291
2312
  session.unset(CUSTOMER_ACCOUNT_SESSION_KEY);
2313
+ session.unset(BUYER_SESSION_KEY);
2292
2314
  }
2293
2315
  async function checkExpires({
2294
2316
  locks,
@@ -2297,7 +2319,8 @@ async function checkExpires({
2297
2319
  customerAccountId,
2298
2320
  customerAccountUrl,
2299
2321
  httpsOrigin,
2300
- debugInfo
2322
+ debugInfo,
2323
+ exchangeForStorefrontCustomerAccessToken
2301
2324
  }) {
2302
2325
  if (parseInt(expiresAt, 10) - 1e3 < (/* @__PURE__ */ new Date()).getTime()) {
2303
2326
  try {
@@ -2307,7 +2330,8 @@ async function checkExpires({
2307
2330
  customerAccountId,
2308
2331
  customerAccountUrl,
2309
2332
  httpsOrigin,
2310
- debugInfo
2333
+ debugInfo,
2334
+ exchangeForStorefrontCustomerAccessToken
2311
2335
  });
2312
2336
  await locks.refresh;
2313
2337
  delete locks.refresh;
@@ -2451,7 +2475,8 @@ function createCustomerAccountClient({
2451
2475
  waitUntil,
2452
2476
  authUrl,
2453
2477
  customAuthStatusHandler,
2454
- logErrors = true
2478
+ logErrors = true,
2479
+ unstableB2b = false
2455
2480
  }) {
2456
2481
  if (customerApiVersion !== DEFAULT_CUSTOMER_API_VERSION) {
2457
2482
  console.warn(
@@ -2474,7 +2499,7 @@ function createCustomerAccountClient({
2474
2499
  const customerAccountApiUrl = `${customerAccountUrl}/account/customer/api/${customerApiVersion}/graphql`;
2475
2500
  const locks = {};
2476
2501
  async function fetchCustomerAPI({
2477
- query,
2502
+ query: query2,
2478
2503
  type,
2479
2504
  variables = {}
2480
2505
  }) {
@@ -2492,7 +2517,7 @@ function createCustomerAccountClient({
2492
2517
  Origin: httpsOrigin,
2493
2518
  Authorization: accessToken
2494
2519
  },
2495
- body: JSON.stringify({ query, variables })
2520
+ body: JSON.stringify({ query: query2, variables })
2496
2521
  });
2497
2522
  logSubRequestEvent?.({
2498
2523
  url: customerAccountApiUrl,
@@ -2500,7 +2525,7 @@ function createCustomerAccountClient({
2500
2525
  response,
2501
2526
  waitUntil,
2502
2527
  stackInfo,
2503
- query,
2528
+ query: query2,
2504
2529
  variables,
2505
2530
  ...getDebugHeaders(request)
2506
2531
  });
@@ -2509,7 +2534,7 @@ function createCustomerAccountClient({
2509
2534
  url: customerAccountApiUrl,
2510
2535
  response,
2511
2536
  type,
2512
- query,
2537
+ query: query2,
2513
2538
  queryVariables: variables,
2514
2539
  errors: void 0,
2515
2540
  client: "customer"
@@ -2540,7 +2565,7 @@ function createCustomerAccountClient({
2540
2565
  clientOperation: `customerAccount.${errorOptions.type}`,
2541
2566
  requestId: response.headers.get("x-request-id"),
2542
2567
  queryVariables: variables,
2543
- query
2568
+ query: query2
2544
2569
  })
2545
2570
  );
2546
2571
  return { ...APIresponse, ...errors && { errors: gqlErrors } };
@@ -2569,7 +2594,8 @@ function createCustomerAccountClient({
2569
2594
  waitUntil,
2570
2595
  stackInfo,
2571
2596
  ...getDebugHeaders(request)
2572
- }
2597
+ },
2598
+ exchangeForStorefrontCustomerAccessToken
2573
2599
  });
2574
2600
  } catch {
2575
2601
  return false;
@@ -2586,6 +2612,55 @@ function createCustomerAccountClient({
2586
2612
  if (hasAccessToken)
2587
2613
  return session.get(CUSTOMER_ACCOUNT_SESSION_KEY)?.accessToken;
2588
2614
  }
2615
+ async function mutate(mutation, options) {
2616
+ ifInvalidCredentialThrowError(customerAccountUrl, customerAccountId);
2617
+ mutation = minifyQuery(mutation);
2618
+ assertMutation(mutation, "customer.mutate");
2619
+ return withSyncStack(
2620
+ fetchCustomerAPI({ query: mutation, type: "mutation", ...options }),
2621
+ { logErrors }
2622
+ );
2623
+ }
2624
+ async function query(query2, options) {
2625
+ ifInvalidCredentialThrowError(customerAccountUrl, customerAccountId);
2626
+ query2 = minifyQuery(query2);
2627
+ assertQuery(query2, "customer.query");
2628
+ return withSyncStack(fetchCustomerAPI({ query: query2, type: "query", ...options }), {
2629
+ logErrors
2630
+ });
2631
+ }
2632
+ function setBuyer(buyer) {
2633
+ session.set(BUYER_SESSION_KEY, {
2634
+ ...session.get(BUYER_SESSION_KEY),
2635
+ ...buyer
2636
+ });
2637
+ }
2638
+ async function getBuyer() {
2639
+ const hasAccessToken = await isLoggedIn();
2640
+ if (!hasAccessToken) {
2641
+ return;
2642
+ }
2643
+ return session.get(BUYER_SESSION_KEY);
2644
+ }
2645
+ async function exchangeForStorefrontCustomerAccessToken() {
2646
+ if (!unstableB2b) {
2647
+ return;
2648
+ }
2649
+ const STOREFRONT_CUSTOMER_ACCOUNT_TOKEN_CREATE = `#graphql
2650
+ mutation storefrontCustomerAccessTokenCreate {
2651
+ storefrontCustomerAccessTokenCreate {
2652
+ customerAccessToken
2653
+ }
2654
+ }
2655
+ `;
2656
+ const { data } = await mutate(STOREFRONT_CUSTOMER_ACCOUNT_TOKEN_CREATE);
2657
+ const customerAccessToken = data?.storefrontCustomerAccessTokenCreate?.customerAccessToken;
2658
+ if (customerAccessToken) {
2659
+ setBuyer({
2660
+ customerAccessToken
2661
+ });
2662
+ }
2663
+ }
2589
2664
  return {
2590
2665
  login: async (options) => {
2591
2666
  ifInvalidCredentialThrowError(customerAccountUrl, customerAccountId);
@@ -2653,24 +2728,8 @@ function createCustomerAccountClient({
2653
2728
  handleAuthStatus,
2654
2729
  getAccessToken,
2655
2730
  getApiUrl: () => customerAccountApiUrl,
2656
- mutate(mutation, options) {
2657
- ifInvalidCredentialThrowError(customerAccountUrl, customerAccountId);
2658
- mutation = minifyQuery(mutation);
2659
- assertMutation(mutation, "customer.mutate");
2660
- return withSyncStack(
2661
- fetchCustomerAPI({ query: mutation, type: "mutation", ...options }),
2662
- { logErrors }
2663
- );
2664
- },
2665
- query(query, options) {
2666
- ifInvalidCredentialThrowError(customerAccountUrl, customerAccountId);
2667
- query = minifyQuery(query);
2668
- assertQuery(query, "customer.query");
2669
- return withSyncStack(
2670
- fetchCustomerAPI({ query, type: "query", ...options }),
2671
- { logErrors }
2672
- );
2673
- },
2731
+ mutate,
2732
+ query,
2674
2733
  authorize: async () => {
2675
2734
  ifInvalidCredentialThrowError(customerAccountUrl, customerAccountId);
2676
2735
  const code = requestUrl.searchParams.get("code");
@@ -2765,19 +2824,19 @@ function createCustomerAccountClient({
2765
2824
  )?.redirectPath;
2766
2825
  session.set(CUSTOMER_ACCOUNT_SESSION_KEY, {
2767
2826
  accessToken: customerAccessToken,
2768
- expiresAt: new Date(
2769
- (/* @__PURE__ */ new Date()).getTime() + (expires_in - 120) * 1e3
2770
- ).getTime() + "",
2827
+ expiresAt: new Date((/* @__PURE__ */ new Date()).getTime() + (expires_in - 120) * 1e3).getTime() + "",
2771
2828
  refreshToken: refresh_token,
2772
- idToken: id_token,
2773
- redirectPath: void 0
2829
+ idToken: id_token
2774
2830
  });
2831
+ await exchangeForStorefrontCustomerAccessToken();
2775
2832
  return redirect(redirectPath || DEFAULT_REDIRECT_PATH, {
2776
2833
  headers: {
2777
2834
  "Set-Cookie": await session.commit()
2778
2835
  }
2779
2836
  });
2780
- }
2837
+ },
2838
+ UNSTABLE_setBuyer: setBuyer,
2839
+ UNSTABLE_getBuyer: getBuyer
2781
2840
  };
2782
2841
  }
2783
2842
  function ifInvalidCredentialThrowError(customerAccountUrl, customerAccountId) {
@@ -2878,10 +2937,18 @@ var MINIMAL_CART_FRAGMENT = `#graphql
2878
2937
  // src/cart/queries/cartCreateDefault.ts
2879
2938
  function cartCreateDefault(options) {
2880
2939
  return async (input, optionalParams) => {
2940
+ const buyer = options.customerAccount ? await options.customerAccount.UNSTABLE_getBuyer() : void 0;
2881
2941
  const { cartId, ...restOfOptionalParams } = optionalParams || {};
2942
+ const { buyerIdentity, ...restOfInput } = input;
2882
2943
  const { cartCreate, errors } = await options.storefront.mutate(CART_CREATE_MUTATION(options.cartFragment), {
2883
2944
  variables: {
2884
- input,
2945
+ input: {
2946
+ ...restOfInput,
2947
+ buyerIdentity: {
2948
+ ...buyer,
2949
+ ...buyerIdentity
2950
+ }
2951
+ },
2885
2952
  ...restOfOptionalParams
2886
2953
  }
2887
2954
  });
@@ -3208,10 +3275,19 @@ var CART_DISCOUNT_CODE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT)
3208
3275
  // src/cart/queries/cartBuyerIdentityUpdateDefault.ts
3209
3276
  function cartBuyerIdentityUpdateDefault(options) {
3210
3277
  return async (buyerIdentity, optionalParams) => {
3278
+ if (buyerIdentity.companyLocationId && options.customerAccount) {
3279
+ options.customerAccount.UNSTABLE_setBuyer({
3280
+ companyLocationId: buyerIdentity.companyLocationId
3281
+ });
3282
+ }
3283
+ const buyer = options.customerAccount ? await options.customerAccount.UNSTABLE_getBuyer() : void 0;
3211
3284
  const { cartBuyerIdentityUpdate, errors } = await options.storefront.mutate(CART_BUYER_IDENTITY_UPDATE_MUTATION(options.cartFragment), {
3212
3285
  variables: {
3213
3286
  cartId: options.getCartId(),
3214
- buyerIdentity,
3287
+ buyerIdentity: {
3288
+ ...buyer,
3289
+ ...buyerIdentity
3290
+ },
3215
3291
  ...optionalParams
3216
3292
  }
3217
3293
  });
@@ -3254,7 +3330,7 @@ function cartNoteUpdateDefault(options) {
3254
3330
  var CART_NOTE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
3255
3331
  mutation cartNoteUpdate(
3256
3332
  $cartId: ID!
3257
- $note: String
3333
+ $note: String!
3258
3334
  $language: LanguageCode
3259
3335
  $country: CountryCode
3260
3336
  ) @inContext(country: $country, language: $language) {
@@ -3479,7 +3555,8 @@ function createCartHandler(options) {
3479
3555
  const mutateOptions = {
3480
3556
  storefront,
3481
3557
  getCartId,
3482
- cartFragment: cartMutateFragment
3558
+ cartFragment: cartMutateFragment,
3559
+ customerAccount
3483
3560
  };
3484
3561
  const _cartCreate = cartCreateDefault(mutateOptions);
3485
3562
  const cartCreate = async function(...args) {
@@ -3706,7 +3783,11 @@ function createCSPHeader(nonce, props) {
3706
3783
  function addCspDirective(currentValue, value) {
3707
3784
  const normalizedValue = typeof value === "string" ? [value] : value;
3708
3785
  const normalizedCurrentValue = Array.isArray(currentValue) ? currentValue : [String(currentValue)];
3709
- const newValue = Array.isArray(normalizedValue) ? [...normalizedCurrentValue, ...normalizedValue] : normalizedValue;
3786
+ const newValue = Array.isArray(normalizedValue) ? (
3787
+ // If the default directive is `none`, don't
3788
+ // merge the override with the default value.
3789
+ normalizedValue.every((a) => a === `'none'`) ? normalizedCurrentValue : [...normalizedCurrentValue, ...normalizedValue]
3790
+ ) : normalizedValue;
3710
3791
  return newValue;
3711
3792
  }
3712
3793
  var Script = forwardRef(
@@ -3814,6 +3895,7 @@ function useCustomerPrivacy(props) {
3814
3895
  const {
3815
3896
  withPrivacyBanner = true,
3816
3897
  onVisitorConsentCollected,
3898
+ onReady,
3817
3899
  ...consentConfig
3818
3900
  } = props;
3819
3901
  const loadedEvent = useRef(false);
@@ -3870,6 +3952,9 @@ function useCustomerPrivacy(props) {
3870
3952
  );
3871
3953
  };
3872
3954
  }
3955
+ if (onReady && !withPrivacyBanner) {
3956
+ onReady();
3957
+ }
3873
3958
  }, [scriptStatus, withPrivacyBanner, consentConfig]);
3874
3959
  return;
3875
3960
  }
@@ -3890,17 +3975,26 @@ function getCustomerPrivacyRequired() {
3890
3975
  return customerPrivacy;
3891
3976
  }
3892
3977
  function ShopifyAnalytics({
3893
- consent
3978
+ consent,
3979
+ onReady
3894
3980
  }) {
3895
3981
  const { subscribe: subscribe2, register: register2, canTrack } = useAnalytics();
3896
3982
  const { ready: shopifyAnalyticsReady } = register2("Internal_Shopify_Analytics");
3897
3983
  const { ready: customerPrivacyReady } = register2(
3898
3984
  "Internal_Shopify_CustomerPrivacy"
3899
3985
  );
3900
- const { checkoutDomain, storefrontAccessToken } = consent;
3901
- checkoutDomain && storefrontAccessToken && useCustomerPrivacy({
3986
+ const analyticsReady = () => {
3987
+ customerPrivacyReady();
3988
+ onReady();
3989
+ };
3990
+ useCustomerPrivacy({
3902
3991
  ...consent,
3903
- onVisitorConsentCollected: customerPrivacyReady
3992
+ onVisitorConsentCollected: analyticsReady,
3993
+ onReady: () => {
3994
+ if (!consent.withPrivacyBanner) {
3995
+ analyticsReady();
3996
+ }
3997
+ }
3904
3998
  });
3905
3999
  useShopifyCookies({ hasUserConsent: canTrack() });
3906
4000
  useEffect(() => {
@@ -3920,7 +4014,7 @@ function logMissingConfig2(fieldName) {
3920
4014
  }
3921
4015
  function prepareBasePageViewPayload(payload) {
3922
4016
  const customerPrivacy = getCustomerPrivacyRequired();
3923
- const hasUserConsent = customerPrivacy.userCanBeTracked();
4017
+ const hasUserConsent = customerPrivacy.analyticsProcessingAllowed();
3924
4018
  if (!payload?.shop?.shopId) {
3925
4019
  logMissingConfig2("shopId");
3926
4020
  return;
@@ -4210,8 +4304,10 @@ function CartAnalytics({
4210
4304
  updatedAt: cart.updatedAt
4211
4305
  })
4212
4306
  );
4213
- prevCart?.lines?.nodes?.forEach((prevLine) => {
4214
- const matchedLineId = cart?.lines.nodes.filter(
4307
+ const previousCartLines = prevCart?.lines ? flattenConnection(prevCart?.lines) : [];
4308
+ const currentCartLines = cart.lines ? flattenConnection(cart.lines) : [];
4309
+ previousCartLines?.forEach((prevLine) => {
4310
+ const matchedLineId = currentCartLines.filter(
4215
4311
  (line) => prevLine.id === line.id
4216
4312
  );
4217
4313
  if (matchedLineId?.length === 1) {
@@ -4236,8 +4332,8 @@ function CartAnalytics({
4236
4332
  });
4237
4333
  }
4238
4334
  });
4239
- cart?.lines?.nodes?.forEach((line) => {
4240
- const matchedLineId = prevCart?.lines.nodes.filter(
4335
+ currentCartLines?.forEach((line) => {
4336
+ const matchedLineId = previousCartLines.filter(
4241
4337
  (previousLine) => line.id === previousLine.id
4242
4338
  );
4243
4339
  if (!matchedLineId || matchedLineId.length === 0) {
@@ -4320,19 +4416,40 @@ function register(key) {
4320
4416
  };
4321
4417
  }
4322
4418
  function shopifyCanTrack() {
4323
- if (typeof window !== "undefined" && typeof window?.Shopify === "object" && typeof window?.Shopify?.customerPrivacy === "object" && typeof window?.Shopify?.customerPrivacy?.userCanBeTracked === "function") {
4324
- return window.Shopify.customerPrivacy.userCanBeTracked();
4419
+ try {
4420
+ return window.Shopify.customerPrivacy.analyticsProcessingAllowed();
4421
+ } catch (e) {
4325
4422
  }
4326
4423
  return false;
4327
4424
  }
4425
+ function messageOnError(field) {
4426
+ return `[h2:error:Analytics.Provider] - ${field} is required`;
4427
+ }
4328
4428
  function AnalyticsProvider({
4329
4429
  canTrack: customCanTrack,
4330
4430
  cart: currentCart,
4331
4431
  children,
4332
4432
  consent,
4333
4433
  customData = {},
4334
- shop: shopProp = null
4434
+ shop: shopProp = null,
4435
+ disableThrowOnError = false
4335
4436
  }) {
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
+ }
4336
4453
  const listenerSet = useRef(false);
4337
4454
  const { shop } = useShopAnalytics(shopProp);
4338
4455
  const [consentLoaded, setConsentLoaded] = useState(
@@ -4342,17 +4459,6 @@ function AnalyticsProvider({
4342
4459
  const [canTrack, setCanTrack] = useState(
4343
4460
  customCanTrack ? () => customCanTrack : () => shopifyCanTrack
4344
4461
  );
4345
- useEffect(() => {
4346
- if (customCanTrack)
4347
- return;
4348
- if (listenerSet.current)
4349
- return;
4350
- listenerSet.current = true;
4351
- document.addEventListener("visitorConsentCollected", () => {
4352
- setConsentLoaded(true);
4353
- setCanTrack(() => shopifyCanTrack);
4354
- });
4355
- }, [setConsentLoaded, setCanTrack, customCanTrack]);
4356
4462
  const value = useMemo(() => {
4357
4463
  return {
4358
4464
  canTrack,
@@ -4381,9 +4487,19 @@ function AnalyticsProvider({
4381
4487
  ]);
4382
4488
  return /* @__PURE__ */ jsxs(AnalyticsContext.Provider, { value, children: [
4383
4489
  children,
4384
- shop && /* @__PURE__ */ jsx(AnalyticsPageView, {}),
4385
- shop && currentCart && /* @__PURE__ */ jsx(CartAnalytics, { cart: currentCart, setCarts }),
4386
- shop && consent && /* @__PURE__ */ jsx(ShopifyAnalytics, { consent })
4490
+ !!shop && /* @__PURE__ */ jsx(AnalyticsPageView, {}),
4491
+ !!shop && !!currentCart && /* @__PURE__ */ jsx(CartAnalytics, { cart: currentCart, setCarts }),
4492
+ !!shop && /* @__PURE__ */ jsx(
4493
+ ShopifyAnalytics,
4494
+ {
4495
+ consent,
4496
+ onReady: () => {
4497
+ listenerSet.current = true;
4498
+ setConsentLoaded(true);
4499
+ setCanTrack(() => shopifyCanTrack);
4500
+ }
4501
+ }
4502
+ )
4387
4503
  ] });
4388
4504
  }
4389
4505
  function useAnalytics() {