@shopify/hydrogen 1.3.2 → 1.4.1

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.
Files changed (36) hide show
  1. package/dist/esnext/components/CartProvider/CartActions.client.d.ts +62 -0
  2. package/dist/esnext/components/CartProvider/CartActions.client.js +232 -0
  3. package/dist/esnext/components/CartProvider/CartProviderV2.client.d.ts +49 -0
  4. package/dist/esnext/components/CartProvider/CartProviderV2.client.js +370 -0
  5. package/dist/esnext/components/CartProvider/hooks.client.js +1 -1
  6. package/dist/esnext/components/CartProvider/types.d.ts +165 -0
  7. package/dist/esnext/components/CartProvider/useCartAPIStateMachine.client.d.ts +22 -0
  8. package/dist/esnext/components/CartProvider/useCartAPIStateMachine.client.js +251 -0
  9. package/dist/esnext/entry-client.js +26 -8
  10. package/dist/esnext/experimental.d.ts +1 -0
  11. package/dist/esnext/experimental.js +1 -0
  12. package/dist/esnext/foundation/HydrogenRequest/HydrogenRequest.server.d.ts +3 -2
  13. package/dist/esnext/foundation/HydrogenRequest/HydrogenRequest.server.js +1 -0
  14. package/dist/esnext/foundation/ShopifyProvider/ShopifyProvider.client.d.ts +3 -3
  15. package/dist/esnext/foundation/ShopifyProvider/ShopifyProvider.server.d.ts +1 -0
  16. package/dist/esnext/foundation/ShopifyProvider/ShopifyProvider.server.js +21 -6
  17. package/dist/esnext/foundation/ShopifyProvider/types.d.ts +5 -2
  18. package/dist/esnext/foundation/useShop/use-shop.d.ts +1 -1
  19. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-config.js +1 -0
  20. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-middleware.js +0 -8
  21. package/dist/esnext/hooks/useDelay/useDelay.d.ts +1 -0
  22. package/dist/esnext/hooks/useDelay/useDelay.js +17 -0
  23. package/dist/esnext/hooks/useShopQuery/hooks.js +4 -2
  24. package/dist/esnext/index.d.ts +1 -0
  25. package/dist/esnext/index.js +1 -0
  26. package/dist/esnext/shared-types.d.ts +2 -1
  27. package/dist/esnext/utilities/apiRoutes.js +4 -2
  28. package/dist/esnext/utilities/object.d.ts +1 -1
  29. package/dist/esnext/utilities/storefrontApi.d.ts +4 -2
  30. package/dist/esnext/utilities/storefrontApi.js +31 -6
  31. package/dist/esnext/version.d.ts +1 -1
  32. package/dist/esnext/version.js +1 -1
  33. package/dist/node/framework/plugins/vite-plugin-hydrogen-config.js +1 -0
  34. package/dist/node/framework/plugins/vite-plugin-hydrogen-middleware.js +0 -8
  35. package/dist/node/shared-types.d.ts +2 -1
  36. package/package.json +3 -1
@@ -0,0 +1,251 @@
1
+ import { useMachine } from '@xstate/react/fsm';
2
+ import { createMachine, assign } from '@xstate/fsm';
3
+ import { flattenConnection } from '../../utilities/flattenConnection/index.js';
4
+ import { useCartActions } from './CartActions.client.js';
5
+ import { useMemo } from 'react';
6
+ function invokeCart(action, options) {
7
+ return {
8
+ entry: [
9
+ ...(options?.entryActions || []),
10
+ 'onCartActionEntry',
11
+ 'onCartActionOptimisticUI',
12
+ action,
13
+ ],
14
+ on: {
15
+ RESOLVE: {
16
+ target: options?.resolveTarget || 'idle',
17
+ actions: [
18
+ assign({
19
+ prevCart: (context) => context?.cart,
20
+ cart: (_, event) => event?.payload?.cart,
21
+ rawCartResult: (_, event) => event?.payload?.rawCartResult,
22
+ errors: (_) => undefined,
23
+ }),
24
+ ],
25
+ },
26
+ ERROR: {
27
+ target: options?.errorTarget || 'error',
28
+ actions: [
29
+ assign({
30
+ prevCart: (context) => context?.cart,
31
+ cart: (context, _) => context?.lastValidCart,
32
+ errors: (_, event) => event?.payload?.errors,
33
+ }),
34
+ ],
35
+ },
36
+ CART_COMPLETED: {
37
+ target: 'cartCompleted',
38
+ actions: assign({
39
+ prevCart: (_) => undefined,
40
+ cart: (_) => undefined,
41
+ lastValidCart: (_) => undefined,
42
+ errors: (_) => undefined,
43
+ }),
44
+ },
45
+ },
46
+ exit: ['onCartActionComplete', ...(options?.exitActions || [])],
47
+ };
48
+ }
49
+ const INITIALIZING_CART_EVENTS = {
50
+ CART_FETCH: {
51
+ target: 'cartFetching',
52
+ },
53
+ CART_CREATE: {
54
+ target: 'cartCreating',
55
+ },
56
+ };
57
+ const UPDATING_CART_EVENTS = {
58
+ CARTLINE_ADD: {
59
+ target: 'cartLineAdding',
60
+ },
61
+ CARTLINE_UPDATE: {
62
+ target: 'cartLineUpdating',
63
+ },
64
+ CARTLINE_REMOVE: {
65
+ target: 'cartLineRemoving',
66
+ },
67
+ NOTE_UPDATE: {
68
+ target: 'noteUpdating',
69
+ },
70
+ BUYER_IDENTITY_UPDATE: {
71
+ target: 'buyerIdentityUpdating',
72
+ },
73
+ CART_ATTRIBUTES_UPDATE: {
74
+ target: 'cartAttributesUpdating',
75
+ },
76
+ DISCOUNT_CODES_UPDATE: {
77
+ target: 'discountCodesUpdating',
78
+ },
79
+ };
80
+ const cartMachine = createMachine({
81
+ id: 'Cart',
82
+ initial: 'uninitialized',
83
+ states: {
84
+ uninitialized: {
85
+ on: INITIALIZING_CART_EVENTS,
86
+ },
87
+ cartCompleted: {
88
+ on: INITIALIZING_CART_EVENTS,
89
+ },
90
+ initializationError: {
91
+ on: INITIALIZING_CART_EVENTS,
92
+ },
93
+ idle: {
94
+ on: UPDATING_CART_EVENTS,
95
+ },
96
+ error: {
97
+ on: UPDATING_CART_EVENTS,
98
+ },
99
+ cartFetching: invokeCart('cartFetchAction', {
100
+ errorTarget: 'initializationError',
101
+ }),
102
+ cartCreating: invokeCart('cartCreateAction', {
103
+ errorTarget: 'initializationError',
104
+ }),
105
+ cartLineRemoving: invokeCart('cartLineRemoveAction'),
106
+ cartLineUpdating: invokeCart('cartLineUpdateAction'),
107
+ cartLineAdding: invokeCart('cartLineAddAction'),
108
+ noteUpdating: invokeCart('noteUpdateAction'),
109
+ buyerIdentityUpdating: invokeCart('buyerIdentityUpdateAction'),
110
+ cartAttributesUpdating: invokeCart('cartAttributesUpdateAction'),
111
+ discountCodesUpdating: invokeCart('discountCodesUpdateAction'),
112
+ },
113
+ });
114
+ export function useCartAPIStateMachine({ numCartLines, onCartActionEntry, onCartActionOptimisticUI, onCartActionComplete, data: cart, cartFragment, countryCode, }) {
115
+ const { cartFetch, cartCreate, cartLineAdd, cartLineUpdate, cartLineRemove, noteUpdate, buyerIdentityUpdate, cartAttributesUpdate, discountCodesUpdate, } = useCartActions({
116
+ numCartLines,
117
+ cartFragment,
118
+ countryCode,
119
+ });
120
+ const [state, send, service] = useMachine(cartMachine, {
121
+ actions: {
122
+ cartFetchAction: async (_, event) => {
123
+ if (event.type !== 'CART_FETCH')
124
+ return;
125
+ const { data, errors } = await cartFetch(event?.payload?.cartId);
126
+ const resultEvent = eventFromFetchResult(event, data?.cart, errors);
127
+ send(resultEvent);
128
+ },
129
+ cartCreateAction: async (_, event) => {
130
+ if (event.type !== 'CART_CREATE')
131
+ return;
132
+ const { data, errors } = await cartCreate(event?.payload);
133
+ const resultEvent = eventFromFetchResult(event, data?.cartCreate?.cart, errors);
134
+ send(resultEvent);
135
+ },
136
+ cartLineAddAction: async (context, event) => {
137
+ if (event.type !== 'CARTLINE_ADD' || !context?.cart?.id)
138
+ return;
139
+ const { data, errors } = await cartLineAdd(context.cart.id, event.payload.lines);
140
+ const resultEvent = eventFromFetchResult(event, data?.cartLinesAdd?.cart, errors);
141
+ send(resultEvent);
142
+ },
143
+ cartLineUpdateAction: async (context, event) => {
144
+ if (event.type !== 'CARTLINE_UPDATE' || !context?.cart?.id)
145
+ return;
146
+ const { data, errors } = await cartLineUpdate(context.cart.id, event.payload.lines);
147
+ const resultEvent = eventFromFetchResult(event, data?.cartLinesUpdate?.cart, errors);
148
+ send(resultEvent);
149
+ },
150
+ cartLineRemoveAction: async (context, event) => {
151
+ if (event.type !== 'CARTLINE_REMOVE' || !context?.cart?.id)
152
+ return;
153
+ const { data, errors } = await cartLineRemove(context.cart.id, event.payload.lines);
154
+ const resultEvent = eventFromFetchResult(event, data?.cartLinesRemove?.cart, errors);
155
+ send(resultEvent);
156
+ },
157
+ noteUpdateAction: async (context, event) => {
158
+ if (event.type !== 'NOTE_UPDATE' || !context?.cart?.id)
159
+ return;
160
+ const { data, errors } = await noteUpdate(context.cart.id, event.payload.note);
161
+ const resultEvent = eventFromFetchResult(event, data?.cartNoteUpdate?.cart, errors);
162
+ send(resultEvent);
163
+ },
164
+ buyerIdentityUpdateAction: async (context, event) => {
165
+ if (event.type !== 'BUYER_IDENTITY_UPDATE' || !context?.cart?.id)
166
+ return;
167
+ const { data, errors } = await buyerIdentityUpdate(context.cart.id, event.payload.buyerIdentity);
168
+ const resultEvent = eventFromFetchResult(event, data?.cartBuyerIdentityUpdate?.cart, errors);
169
+ send(resultEvent);
170
+ },
171
+ cartAttributesUpdateAction: async (context, event) => {
172
+ if (event.type !== 'CART_ATTRIBUTES_UPDATE' || !context?.cart?.id)
173
+ return;
174
+ const { data, errors } = await cartAttributesUpdate(context.cart.id, event.payload.attributes);
175
+ const resultEvent = eventFromFetchResult(event, data?.cartAttributesUpdate?.cart, errors);
176
+ send(resultEvent);
177
+ },
178
+ discountCodesUpdateAction: async (context, event) => {
179
+ if (event.type !== 'DISCOUNT_CODES_UPDATE' || !context?.cart?.id)
180
+ return;
181
+ const { data, errors } = await discountCodesUpdate(context.cart.id, event.payload.discountCodes);
182
+ const resultEvent = eventFromFetchResult(event, data?.cartDiscountCodesUpdate?.cart, errors);
183
+ send(resultEvent);
184
+ },
185
+ ...(onCartActionEntry && {
186
+ onCartActionEntry: (context, event) => {
187
+ if (isCartActionEvent(event)) {
188
+ onCartActionEntry(context, event);
189
+ }
190
+ },
191
+ }),
192
+ ...(onCartActionOptimisticUI && {
193
+ onCartActionOptimisticUI: assign((context, event) => {
194
+ return onCartActionOptimisticUI(context, event);
195
+ }),
196
+ }),
197
+ ...(onCartActionComplete && {
198
+ onCartActionComplete: (context, event) => {
199
+ if (isCartFetchResultEvent(event)) {
200
+ onCartActionComplete(context, event);
201
+ }
202
+ },
203
+ }),
204
+ },
205
+ });
206
+ return useMemo(() => [state, send, service], [state, send, service]);
207
+ }
208
+ export function cartFromGraphQL(cart) {
209
+ return {
210
+ ...cart,
211
+ // @ts-expect-error While the cart still uses fragments, there will be a TS error here until we remove those fragments and get the type in-line
212
+ lines: flattenConnection(cart.lines),
213
+ note: cart.note ?? undefined,
214
+ };
215
+ }
216
+ function eventFromFetchResult(cartActionEvent, cart, errors) {
217
+ if (errors) {
218
+ return { type: 'ERROR', payload: { errors, cartActionEvent } };
219
+ }
220
+ if (!cart) {
221
+ return {
222
+ type: 'CART_COMPLETED',
223
+ payload: {
224
+ cartActionEvent,
225
+ },
226
+ };
227
+ }
228
+ return {
229
+ type: 'RESOLVE',
230
+ payload: {
231
+ cart: cartFromGraphQL(cart),
232
+ rawCartResult: cart,
233
+ cartActionEvent,
234
+ },
235
+ };
236
+ }
237
+ function isCartActionEvent(event) {
238
+ return (event.type === 'CART_CREATE' ||
239
+ event.type === 'CARTLINE_ADD' ||
240
+ event.type === 'CARTLINE_UPDATE' ||
241
+ event.type === 'CARTLINE_REMOVE' ||
242
+ event.type === 'NOTE_UPDATE' ||
243
+ event.type === 'BUYER_IDENTITY_UPDATE' ||
244
+ event.type === 'CART_ATTRIBUTES_UPDATE' ||
245
+ event.type === 'DISCOUNT_CODES_UPDATE');
246
+ }
247
+ function isCartFetchResultEvent(event) {
248
+ return (event.type === 'RESOLVE' ||
249
+ event.type === 'ERROR' ||
250
+ event.type === 'CART_COMPLETED');
251
+ }
@@ -1,4 +1,4 @@
1
- import React, { Suspense, useState, StrictMode, Fragment, useEffect, } from 'react';
1
+ import React, { Suspense, useState, StrictMode, Fragment, startTransition, useEffect, } from 'react';
2
2
  import { hydrateRoot } from 'react-dom/client';
3
3
  import { ErrorBoundary } from 'react-error-boundary/dist/react-error-boundary.esm';
4
4
  import { createFromFetch, createFromReadableStream,
@@ -14,6 +14,20 @@ const cache = new Map();
14
14
  // Hydrate an SSR response from <meta> tags placed in the DOM.
15
15
  const flightChunks = [];
16
16
  const FLIGHT_ATTRIBUTE = 'data-flight';
17
+ const requestIdleCallbackHydrogen = (typeof self !== 'undefined' &&
18
+ self.requestIdleCallback &&
19
+ self.requestIdleCallback.bind(window)) ||
20
+ function (cb) {
21
+ const start = Date.now();
22
+ return setTimeout(function () {
23
+ cb({
24
+ didTimeout: false,
25
+ timeRemaining() {
26
+ return Math.max(0, 50 - (Date.now() - start));
27
+ },
28
+ });
29
+ }, 1);
30
+ };
17
31
  function addElementToFlightChunks(el) {
18
32
  // We don't need to decode, because `.getAttribute` already decodes
19
33
  const chunk = el.getAttribute(FLIGHT_ATTRIBUTE);
@@ -93,13 +107,17 @@ const renderHydrogen = async (ClientWrapper) => {
93
107
  config.strictMode !== false ? StrictMode : Fragment;
94
108
  // Fixes hydration in `useId`: https://github.com/Shopify/hydrogen/issues/1589
95
109
  const ServerRequestProviderMock = () => null;
96
- hydrateRoot(root, React.createElement(RootComponent, null,
97
- React.createElement(ServerRequestProviderMock, null),
98
- React.createElement(ErrorBoundary, { FallbackComponent: CustomErrorPage
99
- ? ({ error }) => (React.createElement(CustomErrorWrapper, { error: error, errorPage: CustomErrorPage }))
100
- : DefaultError },
101
- React.createElement(Suspense, { fallback: null },
102
- React.createElement(Content, { clientWrapper: ClientWrapper })))));
110
+ requestIdleCallbackHydrogen(() => {
111
+ startTransition(() => {
112
+ hydrateRoot(root, React.createElement(RootComponent, null,
113
+ React.createElement(ServerRequestProviderMock, null),
114
+ React.createElement(ErrorBoundary, { FallbackComponent: CustomErrorPage
115
+ ? ({ error }) => (React.createElement(CustomErrorWrapper, { error: error, errorPage: CustomErrorPage }))
116
+ : DefaultError },
117
+ React.createElement(Suspense, { fallback: null },
118
+ React.createElement(Content, { clientWrapper: ClientWrapper })))));
119
+ });
120
+ });
103
121
  };
104
122
  export default renderHydrogen;
105
123
  function Content({ clientWrapper: ClientWrapper = ({ children }) => children, }) {
@@ -1,2 +1,3 @@
1
1
  export { Form } from './foundation/Form/Form.client.js';
2
2
  export { useFlashSession } from './foundation/useSession/useSession.js';
3
+ export { CartProviderV2 } from './components/CartProvider/CartProviderV2.client.js';
@@ -1,2 +1,3 @@
1
1
  export { Form } from './foundation/Form/Form.client.js';
2
2
  export { useFlashSession } from './foundation/useSession/useSession.js';
3
+ export { CartProviderV2 } from './components/CartProvider/CartProviderV2.client.js';
@@ -1,4 +1,4 @@
1
- import type { ShopifyContextValue, LocalizationContextValue } from '../ShopifyProvider/types.js';
1
+ import type { ShopifyContextServerValue, LocalizationContextValue } from '../ShopifyProvider/types.js';
2
2
  import type { QueryCacheControlHeaders } from '../../utilities/log/log-cache-header.js';
3
3
  import type { QueryTiming } from '../../utilities/log/log-query-timeline.js';
4
4
  import type { ResolvedHydrogenConfig, PreloadOptions, QueryKey, RuntimeContext } from '../../types.js';
@@ -38,7 +38,7 @@ export declare class HydrogenRequest extends Request {
38
38
  cache: Map<string, any>;
39
39
  head: HeadData;
40
40
  hydrogenConfig?: ResolvedHydrogenConfig;
41
- shopifyConfig?: ShopifyContextValue;
41
+ shopifyConfig?: ShopifyContextServerValue;
42
42
  queryCacheControl: Array<QueryCacheControlHeaders>;
43
43
  queryTimings: Array<QueryTiming>;
44
44
  preloadQueries: PreloadQueriesByURL;
@@ -51,6 +51,7 @@ export declare class HydrogenRequest extends Request {
51
51
  scopes: Map<string, Record<string, any>>;
52
52
  localization?: LocalizationContextValue;
53
53
  [key: string]: any;
54
+ throttledRequests: Record<string, any>;
54
55
  };
55
56
  constructor(input: any);
56
57
  constructor(input: RequestInfo, init?: RequestInit);
@@ -61,6 +61,7 @@ export class HydrogenRequest extends Request {
61
61
  preloadQueries: new Map(),
62
62
  scopes: new Map(),
63
63
  flashSession: {},
64
+ throttledRequests: {},
64
65
  };
65
66
  this.cookies = this.parseCookies();
66
67
  }
@@ -1,9 +1,9 @@
1
- import type { ShopifyContextValue, LocalizationContextValue } from './types.js';
1
+ import type { ShopifyContextClientValue, LocalizationContextValue } from './types.js';
2
2
  import React, { ReactNode } from 'react';
3
- export declare const ShopifyContext: React.Context<ShopifyContextValue | null>;
3
+ export declare const ShopifyContext: React.Context<ShopifyContextClientValue | null>;
4
4
  export declare const LocalizationContext: React.Context<LocalizationContextValue | null>;
5
5
  export declare function ShopifyProviderClient({ children, shopifyConfig, localization, }: {
6
6
  children: ReactNode;
7
- shopifyConfig: ShopifyContextValue;
7
+ shopifyConfig: ShopifyContextClientValue;
8
8
  localization: LocalizationContextValue;
9
9
  }): JSX.Element;
@@ -1,5 +1,6 @@
1
1
  import type { ShopifyProviderProps, LocalizationContextValue } from './types.js';
2
2
  import type { CountryCode, LanguageCode } from '../../storefront-api-types.js';
3
+ export declare const CLIENT_CONTEXT_ALLOW_LIST: readonly ["defaultCountryCode", "defaultLanguageCode", "storeDomain", "storefrontToken", "storefrontApiVersion", "storefrontId"];
3
4
  export declare const SHOPIFY_PROVIDER_CONTEXT_KEY: unique symbol;
4
5
  /**
5
6
  * The `ShopifyProvider` component wraps your entire app and provides support for hooks.
@@ -5,18 +5,33 @@ import { useRequestCacheData, useServerRequest, } from '../ServerRequestProvider
5
5
  import { getOxygenVariable } from '../../utilities/storefrontApi.js';
6
6
  import { SHOPIFY_STOREFRONT_ID_VARIABLE } from '../../constants.js';
7
7
  import { getLocale } from '../../utilities/locale/index.js';
8
+ export const CLIENT_CONTEXT_ALLOW_LIST = [
9
+ 'defaultCountryCode',
10
+ 'defaultLanguageCode',
11
+ 'storeDomain',
12
+ 'storefrontToken',
13
+ 'storefrontApiVersion',
14
+ 'storefrontId',
15
+ ];
8
16
  function makeShopifyContext(shopifyConfig) {
9
17
  const countryCode = shopifyConfig.defaultCountryCode ?? DEFAULT_COUNTRY;
10
18
  const languageCode = shopifyConfig.defaultLanguageCode ?? DEFAULT_LANGUAGE;
11
19
  const storefrontId = getOxygenVariable(SHOPIFY_STOREFRONT_ID_VARIABLE);
12
- return {
20
+ const shopifyProviderServerValue = {
13
21
  defaultCountryCode: countryCode.toUpperCase(),
14
22
  defaultLanguageCode: languageCode.toUpperCase(),
15
23
  storeDomain: shopifyConfig?.storeDomain?.replace(/^https?:\/\//, ''),
16
24
  storefrontToken: shopifyConfig.storefrontToken,
17
25
  storefrontApiVersion: shopifyConfig.storefrontApiVersion,
18
- multipassSecret: shopifyConfig.multipassSecret,
19
26
  storefrontId,
27
+ privateStorefrontToken: shopifyConfig.privateStorefrontToken,
28
+ };
29
+ return {
30
+ shopifyProviderServerValue,
31
+ shopifyProviderClientValue: CLIENT_CONTEXT_ALLOW_LIST.reduce((clientConfigValue, key) => {
32
+ clientConfigValue[key] = shopifyProviderServerValue[key];
33
+ return clientConfigValue;
34
+ }, {}),
20
35
  };
21
36
  }
22
37
  export const SHOPIFY_PROVIDER_CONTEXT_KEY = Symbol.for('SHOPIFY_PROVIDER_RSC');
@@ -55,11 +70,11 @@ children, }) {
55
70
  else {
56
71
  actualShopifyConfig = shopifyConfig;
57
72
  }
58
- const shopifyProviderValue = useMemo(() => makeShopifyContext(actualShopifyConfig), [actualShopifyConfig]);
59
- const localization = getLocalizationContextValue(shopifyProviderValue.defaultLanguageCode, shopifyProviderValue.defaultCountryCode, languageCode, countryCode);
73
+ const { shopifyProviderServerValue, shopifyProviderClientValue } = useMemo(() => makeShopifyContext(actualShopifyConfig), [actualShopifyConfig]);
74
+ const localization = getLocalizationContextValue(shopifyProviderServerValue.defaultLanguageCode, shopifyProviderServerValue.defaultCountryCode, languageCode, countryCode);
60
75
  request.ctx.localization = localization;
61
- request.ctx.shopifyConfig = shopifyProviderValue;
62
- return (React.createElement(ShopifyProviderClient, { shopifyConfig: shopifyProviderValue, localization: localization }, children));
76
+ request.ctx.shopifyConfig = shopifyProviderServerValue;
77
+ return (React.createElement(ShopifyProviderClient, { shopifyConfig: shopifyProviderClientValue, localization: localization }, children));
63
78
  }
64
79
  export function getLocalizationContextValue(defaultLanguageCode, defaultCountryCode, languageCode, countryCode) {
65
80
  return useMemo(() => {
@@ -1,11 +1,13 @@
1
1
  import type { CountryCode, LanguageCode } from '../../storefront-api-types.js';
2
2
  import type { ReactNode } from 'react';
3
3
  import type { ShopifyConfigFetcher, ShopifyConfig } from '../../types.js';
4
- export interface ShopifyContextValue extends Omit<ShopifyConfig, 'defaultLanguageCode' | 'defaultCountryCode'> {
4
+ import type { CLIENT_CONTEXT_ALLOW_LIST } from './ShopifyProvider.server.js';
5
+ export interface ShopifyContextServerValue extends Omit<ShopifyConfig, 'defaultLanguageCode' | 'defaultCountryCode'> {
5
6
  defaultLanguageCode: `${LanguageCode}`;
6
7
  defaultCountryCode: `${CountryCode}`;
7
- storefrontId: string | null;
8
8
  }
9
+ declare type CLIENT_KEYS = typeof CLIENT_CONTEXT_ALLOW_LIST[number];
10
+ export declare type ShopifyContextClientValue = Pick<ShopifyContextServerValue, CLIENT_KEYS>;
9
11
  export declare type Locale = string;
10
12
  export interface LocalizationContextValue {
11
13
  country: {
@@ -30,3 +32,4 @@ export declare type ShopifyProviderProps = {
30
32
  */
31
33
  languageCode?: `${LanguageCode}`;
32
34
  };
35
+ export {};
@@ -3,4 +3,4 @@
3
3
  * [the `shopify` property in the `hydrogen.config.js` file](https://shopify.dev/custom-storefronts/hydrogen/framework/hydrogen-config).
4
4
  * The `useShop` hook must be a descendent of a `ShopifyProvider` component.
5
5
  */
6
- export declare function useShop(): import("../ShopifyProvider/types.js").ShopifyContextValue;
6
+ export declare function useShop(): import("../ShopifyProvider/types.js").ShopifyContextServerValue;
@@ -98,6 +98,7 @@ export default (pluginOptions) => {
98
98
  // https://github.com/nfriedly/set-cookie-parser/issues/50
99
99
  'set-cookie-parser',
100
100
  'undici',
101
+ '@xstate/react/fsm',
101
102
  ],
102
103
  },
103
104
  define: {
@@ -56,13 +56,5 @@ export default (pluginOptions) => {
56
56
  };
57
57
  async function polyfillOxygenEnv(config) {
58
58
  const env = await loadEnv(config.mode, config.root, '');
59
- const publicPrefixes = Array.isArray(config.envPrefix)
60
- ? config.envPrefix
61
- : [config.envPrefix || ''];
62
- for (const key of Object.keys(env)) {
63
- if (publicPrefixes.some((prefix) => key.startsWith(prefix))) {
64
- delete env[key];
65
- }
66
- }
67
59
  globalThis.Oxygen = { env };
68
60
  }
@@ -0,0 +1 @@
1
+ export declare const useDelay: (data: unknown, time: number) => unknown;
@@ -0,0 +1,17 @@
1
+ import { useServerRequest } from '../../foundation/ServerRequestProvider/index.js';
2
+ import { wrapPromise } from '../../utilities/index.js';
3
+ import { log } from '../../utilities/log/log.js';
4
+ export const useDelay = function (data, time) {
5
+ if (!__HYDROGEN_DEV__) {
6
+ log.warn(new Error('The `useDelay` hook introduces an artificial delay that should not be used in production!').stack);
7
+ return data;
8
+ }
9
+ // eslint-disable-next-line react-hooks/rules-of-hooks
10
+ const serverRequest = useServerRequest();
11
+ const key = JSON.stringify(data);
12
+ if (!serverRequest.ctx.throttledRequests[key]) {
13
+ serverRequest.ctx.throttledRequests[key] = wrapPromise(new Promise((resolve) => setTimeout(resolve, time)));
14
+ }
15
+ serverRequest.ctx.throttledRequests[key].read();
16
+ return data;
17
+ };
@@ -145,12 +145,14 @@ export function useShopQuery({ query, variables = {}, cache, preload = false, })
145
145
  return data;
146
146
  }
147
147
  function useCreateShopRequest(body) {
148
- const { storeDomain, storefrontToken, storefrontApiVersion } = useShop();
148
+ const { storeDomain, storefrontToken, storefrontApiVersion, storefrontId, privateStorefrontToken, } = useShop();
149
149
  const request = useServerRequest();
150
150
  const buyerIp = request.getBuyerIp();
151
151
  const extraHeaders = getStorefrontApiRequestHeaders({
152
152
  buyerIp,
153
- storefrontToken,
153
+ publicStorefrontToken: storefrontToken,
154
+ privateStorefrontToken,
155
+ storefrontId,
154
156
  });
155
157
  return {
156
158
  key: [storeDomain, storefrontApiVersion, body],
@@ -28,6 +28,7 @@ export { ShopifyAnalytics } from './foundation/Analytics/connectors/Shopify/Shop
28
28
  export { ShopifyAnalyticsConstants } from './foundation/Analytics/connectors/Shopify/const.js';
29
29
  export { useSession } from './foundation/useSession/useSession.js';
30
30
  export { Cookie } from './foundation/Cookie/Cookie.js';
31
+ export { useDelay } from './hooks/useDelay/useDelay.js';
31
32
  /**
32
33
  * Export server-only CartQuery here instead of `CartProvider.client` to prevent
33
34
  * it from being bundled with other client components
@@ -28,6 +28,7 @@ export { ShopifyAnalytics } from './foundation/Analytics/connectors/Shopify/Shop
28
28
  export { ShopifyAnalyticsConstants } from './foundation/Analytics/connectors/Shopify/const.js';
29
29
  export { useSession } from './foundation/useSession/useSession.js';
30
30
  export { Cookie } from './foundation/Cookie/Cookie.js';
31
+ export { useDelay } from './hooks/useDelay/useDelay.js';
31
32
  /**
32
33
  * Export server-only CartQuery here instead of `CartProvider.client` to prevent
33
34
  * it from being bundled with other client components
@@ -26,5 +26,6 @@ export declare type ShopifyConfig = {
26
26
  storeDomain: string;
27
27
  storefrontToken: string;
28
28
  storefrontApiVersion: string;
29
- multipassSecret?: string;
29
+ privateStorefrontToken?: string;
30
+ storefrontId?: string;
30
31
  };
@@ -81,11 +81,13 @@ function queryShopBuilder(shopifyConfigGetter, request) {
81
81
  if (!shopifyConfig) {
82
82
  throw new Error('Shopify connection info was not found in Hydrogen config');
83
83
  }
84
- const { storeDomain, storefrontApiVersion, storefrontToken } = shopifyConfig;
84
+ const { storeDomain, storefrontApiVersion, storefrontToken, privateStorefrontToken, storefrontId, } = shopifyConfig;
85
85
  const buyerIp = request.getBuyerIp();
86
86
  const extraHeaders = getStorefrontApiRequestHeaders({
87
87
  buyerIp,
88
- storefrontToken,
88
+ publicStorefrontToken: storefrontToken,
89
+ privateStorefrontToken,
90
+ storefrontId,
89
91
  });
90
92
  const fetcher = fetchBuilder(`https://${storeDomain}/api/${storefrontApiVersion}/graphql.json`, {
91
93
  method: 'POST',
@@ -1,5 +1,5 @@
1
1
  declare type Descriptor = Parameters<typeof Object.defineProperty>[2];
2
- export declare function createObject<T = object>(properties: T, { prototype, ...descriptor }?: {
2
+ export declare function createObject<T extends {} = object>(properties: T, { prototype, ...descriptor }?: {
3
3
  prototype?: any;
4
4
  } & Exclude<Descriptor, 'value'>): T;
5
5
  export {};
@@ -1,5 +1,7 @@
1
- export declare function getStorefrontApiRequestHeaders({ buyerIp, storefrontToken, }: {
1
+ export declare function getStorefrontApiRequestHeaders({ buyerIp, publicStorefrontToken, privateStorefrontToken, storefrontId, }: {
2
2
  buyerIp?: string | null;
3
- storefrontToken: string;
3
+ publicStorefrontToken: string;
4
+ privateStorefrontToken: string | undefined;
5
+ storefrontId: string | undefined;
4
6
  }): Record<string, any>;
5
7
  export declare function getOxygenVariable(key: string): any;
@@ -1,17 +1,42 @@
1
1
  /* global Oxygen */
2
2
  import { OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE, STOREFRONT_API_SECRET_TOKEN_HEADER, STOREFRONT_API_PUBLIC_TOKEN_HEADER, STOREFRONT_API_BUYER_IP_HEADER, SHOPIFY_STOREFRONT_ID_VARIABLE, SHOPIFY_STOREFRONT_ID_HEADER, } from '../constants.js';
3
- export function getStorefrontApiRequestHeaders({ buyerIp, storefrontToken, }) {
3
+ import { log } from './log/log.js';
4
+ let secretTokenWarned = false;
5
+ let storefrontIdWarned = false;
6
+ export function getStorefrontApiRequestHeaders({ buyerIp, publicStorefrontToken, privateStorefrontToken, storefrontId, }) {
4
7
  const headers = {};
5
- const secretToken = getOxygenVariable(OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE);
6
- const storefrontId = getOxygenVariable(SHOPIFY_STOREFRONT_ID_VARIABLE);
8
+ if (!privateStorefrontToken) {
9
+ privateStorefrontToken = getOxygenVariable(OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE);
10
+ if (!secretTokenWarned) {
11
+ secretTokenWarned = true;
12
+ if (!privateStorefrontToken && !__HYDROGEN_DEV__) {
13
+ log.error('No secret Shopify storefront API token was defined. This means your app will be rate limited!\nSee how to add the token: ');
14
+ }
15
+ else if (privateStorefrontToken) {
16
+ log.warn('The private shopify storefront API token was loaded implicitly by an environment variable. This is deprecated, and instead the variable should be defined directly in the Hydrogen Config.\nFor more information: ');
17
+ }
18
+ }
19
+ }
20
+ if (!storefrontId) {
21
+ storefrontId = getOxygenVariable(SHOPIFY_STOREFRONT_ID_VARIABLE);
22
+ if (!storefrontIdWarned) {
23
+ storefrontIdWarned = true;
24
+ if (!storefrontId && !__HYDROGEN_DEV__) {
25
+ log.warn('No storefrontId was defined. This means the analytics on your admin dashboard will be broken!\nSee how to fix it: ');
26
+ }
27
+ else if (storefrontId) {
28
+ log.warn('The storefrontId was loaded implicitly by an environment variable. This is deprecated, and instead the variable should be defined directly in the Hydrogen Config.\nFor more information: ');
29
+ }
30
+ }
31
+ }
7
32
  /**
8
33
  * Only pass one type of storefront token at a time.
9
34
  */
10
- if (secretToken) {
11
- headers[STOREFRONT_API_SECRET_TOKEN_HEADER] = secretToken;
35
+ if (privateStorefrontToken) {
36
+ headers[STOREFRONT_API_SECRET_TOKEN_HEADER] = privateStorefrontToken;
12
37
  }
13
38
  else {
14
- headers[STOREFRONT_API_PUBLIC_TOKEN_HEADER] = storefrontToken;
39
+ headers[STOREFRONT_API_PUBLIC_TOKEN_HEADER] = publicStorefrontToken;
15
40
  }
16
41
  if (buyerIp) {
17
42
  headers[STOREFRONT_API_BUYER_IP_HEADER] = buyerIp;
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "1.3.2";
1
+ export declare const LIB_VERSION = "1.4.1";
@@ -1 +1 @@
1
- export const LIB_VERSION = '1.3.2';
1
+ export const LIB_VERSION = '1.4.1';
@@ -103,6 +103,7 @@ exports.default = (pluginOptions) => {
103
103
  // https://github.com/nfriedly/set-cookie-parser/issues/50
104
104
  'set-cookie-parser',
105
105
  'undici',
106
+ '@xstate/react/fsm',
106
107
  ],
107
108
  },
108
109
  define: {