@shopify/hydrogen 0.6.1 → 0.7.0

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 (147) hide show
  1. package/dist/esnext/components/AddToCartButton/AddToCartButton.client.d.ts +1 -1
  2. package/dist/esnext/components/AddToCartButton/AddToCartButton.client.js +3 -3
  3. package/dist/esnext/components/BuyNowButton/BuyNowButton.client.js +1 -5
  4. package/dist/esnext/components/CartProvider/hooks.js +4 -4
  5. package/dist/esnext/components/Link/Link.client.d.ts +1 -0
  6. package/dist/esnext/components/Link/Link.client.js +1 -0
  7. package/dist/esnext/components/Link/index.d.ts +1 -0
  8. package/dist/esnext/components/Link/index.js +1 -0
  9. package/dist/esnext/components/LocalizationProvider/LocalizationContext.client.d.ts +1 -8
  10. package/dist/esnext/components/LocalizationProvider/LocalizationContext.client.js +1 -3
  11. package/dist/esnext/components/Metafield/Metafield.client.js +8 -0
  12. package/dist/esnext/components/Metafield/MetafieldFragment.d.ts +16 -1
  13. package/dist/esnext/components/Model3D/Model3D.client.d.ts +2 -0
  14. package/dist/esnext/components/Model3D/Model3D.client.js +1 -1
  15. package/dist/esnext/components/ProductProvider/context.d.ts +2 -2
  16. package/dist/esnext/components/ProductProvider/types.d.ts +2 -2
  17. package/dist/esnext/components/SelectedVariantAddToCartButton/SelectedVariantAddToCartButton.client.d.ts +1 -1
  18. package/dist/esnext/components/SelectedVariantAddToCartButton/SelectedVariantAddToCartButton.client.js +1 -1
  19. package/dist/esnext/components/index.d.ts +1 -1
  20. package/dist/esnext/components/index.js +1 -1
  21. package/dist/esnext/entry-server.js +50 -20
  22. package/dist/esnext/foundation/Router/ServerStateRouter.client.js +5 -3
  23. package/dist/esnext/foundation/ServerStateProvider/ServerStateProvider.client.d.ts +19 -18
  24. package/dist/esnext/foundation/ServerStateProvider/ServerStateProvider.client.js +22 -22
  25. package/dist/esnext/foundation/ServerStateProvider/index.d.ts +1 -1
  26. package/dist/esnext/foundation/ShopifyProvider/ShopifyProvider.d.ts +1 -1
  27. package/dist/esnext/foundation/ShopifyProvider/ShopifyProvider.js +2 -1
  28. package/dist/esnext/foundation/ShopifyProvider/ShopifyServerProvider.server.js +1 -1
  29. package/dist/esnext/foundation/ShopifyProvider/consts.d.ts +1 -0
  30. package/dist/esnext/foundation/ShopifyProvider/consts.js +1 -0
  31. package/dist/esnext/{hooks → foundation}/useQuery/QueryProvider.d.ts +0 -0
  32. package/dist/esnext/{hooks → foundation}/useQuery/QueryProvider.js +0 -0
  33. package/dist/esnext/{hooks → foundation}/useQuery/hooks.d.ts +0 -0
  34. package/dist/esnext/{hooks → foundation}/useQuery/hooks.js +13 -8
  35. package/dist/esnext/{hooks → foundation}/useQuery/index.d.ts +0 -0
  36. package/dist/esnext/{hooks → foundation}/useQuery/index.js +0 -0
  37. package/dist/esnext/foundation/useServerState/use-server-state.d.ts +1 -1
  38. package/dist/esnext/framework/ClientMarker/ClientMarker.d.ts +2 -3
  39. package/dist/esnext/framework/ClientMarker/ClientMarker.js +24 -1
  40. package/dist/esnext/framework/Hydration/Cache.client.js +5 -2
  41. package/dist/esnext/framework/Hydration/ServerComponentResponse.server.d.ts +10 -0
  42. package/dist/esnext/framework/Hydration/ServerComponentResponse.server.js +13 -0
  43. package/dist/esnext/framework/Hydration/client-imports.d.ts +1 -0
  44. package/dist/esnext/framework/Hydration/client-imports.js +25 -0
  45. package/dist/esnext/framework/Hydration/react-utils.js +1 -0
  46. package/dist/esnext/framework/Hydration/wire.server.js +1 -1
  47. package/dist/esnext/framework/graphiql.js +1 -1
  48. package/dist/esnext/framework/middleware.d.ts +5 -1
  49. package/dist/esnext/framework/middleware.js +12 -8
  50. package/dist/esnext/framework/plugins/resolver.d.ts +1 -0
  51. package/dist/esnext/framework/plugins/resolver.js +3 -0
  52. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-config.js +4 -1
  53. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-middleware.js +8 -2
  54. package/dist/esnext/framework/plugins/vite-plugin-react-server-components-shim.js +41 -75
  55. package/dist/esnext/framework/server-components.d.ts +2 -10
  56. package/dist/esnext/framework/server-components.js +64 -68
  57. package/dist/esnext/graphql/graphql-constants.d.ts +51 -14
  58. package/dist/esnext/graphql/graphql-constants.js +89 -15
  59. package/dist/esnext/handle-event.js +8 -4
  60. package/dist/esnext/hooks/index.d.ts +1 -1
  61. package/dist/esnext/hooks/index.js +1 -1
  62. package/dist/esnext/hooks/useAvailableCountries/useAvailableCountries.js +1 -1
  63. package/dist/esnext/hooks/useCountry/useCountry.js +1 -1
  64. package/dist/esnext/hooks/useMoney/hooks.js +2 -16
  65. package/dist/esnext/hooks/useParsedMetafields/useParsedMetafields.d.ts +2 -3
  66. package/dist/esnext/hooks/useProductOptions/types.d.ts +2 -3
  67. package/dist/esnext/hooks/useShopQuery/hooks.js +1 -1
  68. package/dist/esnext/types.d.ts +19 -3
  69. package/dist/esnext/utilities/flattenConnection/flattenConnection.d.ts +0 -7
  70. package/dist/esnext/utilities/flattenConnection/flattenConnection.js +0 -7
  71. package/dist/esnext/utilities/isClient/isClient.d.ts +0 -4
  72. package/dist/esnext/utilities/isClient/isClient.js +0 -4
  73. package/dist/esnext/utilities/isServer/isServer.d.ts +0 -4
  74. package/dist/esnext/utilities/isServer/isServer.js +0 -4
  75. package/dist/esnext/utilities/parseMetafieldValue/parseMetafieldValue.d.ts +2 -33
  76. package/dist/esnext/utilities/parseMetafieldValue/parseMetafieldValue.js +0 -31
  77. package/dist/esnext/version.d.ts +1 -1
  78. package/dist/esnext/version.js +1 -1
  79. package/dist/node/foundation/ShopifyProvider/consts.d.ts +1 -0
  80. package/dist/node/foundation/ShopifyProvider/consts.js +4 -0
  81. package/dist/node/framework/ClientMarker/ClientMarker.d.ts +2 -3
  82. package/dist/node/framework/ClientMarker/ClientMarker.js +26 -3
  83. package/dist/node/framework/Hydration/Cache.client.js +7 -20
  84. package/dist/node/framework/Hydration/ServerComponentResponse.server.d.ts +10 -0
  85. package/dist/node/framework/Hydration/ServerComponentResponse.server.js +13 -0
  86. package/dist/node/framework/Hydration/client-imports.d.ts +1 -0
  87. package/dist/node/framework/Hydration/client-imports.js +28 -0
  88. package/dist/node/framework/Hydration/react-utils.js +1 -0
  89. package/dist/node/framework/Hydration/wire.server.js +1 -1
  90. package/dist/node/framework/graphiql.js +2 -2
  91. package/dist/node/framework/middleware.d.ts +5 -1
  92. package/dist/node/framework/middleware.js +15 -9
  93. package/dist/node/framework/plugins/resolver.d.ts +1 -0
  94. package/dist/node/framework/plugins/resolver.js +7 -0
  95. package/dist/node/framework/plugins/vite-plugin-hydrogen-config.js +4 -1
  96. package/dist/node/framework/plugins/vite-plugin-hydrogen-middleware.js +8 -2
  97. package/dist/node/framework/plugins/vite-plugin-react-server-components-shim.js +41 -75
  98. package/dist/node/framework/server-components.d.ts +2 -10
  99. package/dist/node/framework/server-components.js +69 -71
  100. package/dist/node/handle-event.js +8 -4
  101. package/dist/node/types.d.ts +19 -3
  102. package/dist/node/utilities/flattenConnection/flattenConnection.d.ts +0 -7
  103. package/dist/node/utilities/flattenConnection/flattenConnection.js +0 -7
  104. package/dist/node/utilities/isClient/isClient.d.ts +0 -4
  105. package/dist/node/utilities/isClient/isClient.js +0 -4
  106. package/dist/node/utilities/isServer/isServer.d.ts +0 -4
  107. package/dist/node/utilities/isServer/isServer.js +0 -4
  108. package/dist/node/utilities/parseMetafieldValue/parseMetafieldValue.d.ts +2 -33
  109. package/dist/node/utilities/parseMetafieldValue/parseMetafieldValue.js +0 -31
  110. package/dist/node/version.d.ts +1 -1
  111. package/dist/node/version.js +1 -1
  112. package/dist/worker/framework/Hydration/ServerComponentResponse.server.d.ts +10 -0
  113. package/dist/worker/framework/Hydration/ServerComponentResponse.server.js +13 -0
  114. package/dist/worker/handle-event.js +8 -4
  115. package/dist/worker/types.d.ts +19 -3
  116. package/marker.js +1 -1
  117. package/package.json +11 -7
  118. package/dist/node/foundation/Router/DefaultRoutes.d.ts +0 -20
  119. package/dist/node/foundation/Router/DefaultRoutes.js +0 -78
  120. package/dist/node/foundation/Router/index.d.ts +0 -1
  121. package/dist/node/foundation/Router/index.js +0 -5
  122. package/dist/node/foundation/ServerStateProvider/ServerStateProvider.client.d.ts +0 -24
  123. package/dist/node/foundation/ServerStateProvider/ServerStateProvider.client.js +0 -66
  124. package/dist/node/foundation/ServerStateProvider/index.d.ts +0 -1
  125. package/dist/node/foundation/ServerStateProvider/index.js +0 -6
  126. package/dist/node/foundation/ShopifyProvider/ShopifyContext.d.ts +0 -1
  127. package/dist/node/foundation/ShopifyProvider/ShopifyContext.js +0 -5
  128. package/dist/node/foundation/ShopifyProvider/ShopifyProvider.d.ts +0 -7
  129. package/dist/node/foundation/ShopifyProvider/ShopifyProvider.js +0 -42
  130. package/dist/node/foundation/ShopifyProvider/ShopifyServerProvider.server.d.ts +0 -8
  131. package/dist/node/foundation/ShopifyProvider/ShopifyServerProvider.server.js +0 -14
  132. package/dist/node/foundation/ShopifyProvider/index.d.ts +0 -1
  133. package/dist/node/foundation/ShopifyProvider/index.js +0 -6
  134. package/dist/node/foundation/ShopifyProvider/types.d.ts +0 -14
  135. package/dist/node/foundation/ShopifyProvider/types.js +0 -2
  136. package/dist/node/foundation/index.d.ts +0 -5
  137. package/dist/node/foundation/index.js +0 -23
  138. package/dist/node/foundation/useShop/index.d.ts +0 -1
  139. package/dist/node/foundation/useShop/index.js +0 -5
  140. package/dist/node/foundation/useShop/use-shop.d.ts +0 -5
  141. package/dist/node/foundation/useShop/use-shop.js +0 -16
  142. package/dist/node/hooks/useQuery/QueryProvider.d.ts +0 -6
  143. package/dist/node/hooks/useQuery/QueryProvider.js +0 -20
  144. package/dist/node/hooks/useQuery/hooks.d.ts +0 -15
  145. package/dist/node/hooks/useQuery/hooks.js +0 -67
  146. package/dist/node/hooks/useQuery/index.d.ts +0 -2
  147. package/dist/node/hooks/useQuery/index.js +0 -7
@@ -7,7 +7,7 @@ export interface AddToCartButtonProps {
7
7
  value: string;
8
8
  }[];
9
9
  /** The ID of the variant. */
10
- variantID: string;
10
+ variantId: string;
11
11
  /** The item quantity. */
12
12
  quantity?: number;
13
13
  /** Any ReactNode elements. */
@@ -6,7 +6,7 @@ import { useCartLinesAddCallback, useCart, useCartCreateCallback, } from '../Car
6
6
  */
7
7
  export function AddToCartButton(props) {
8
8
  const [addingItem, setAddingItem] = useState(false);
9
- const { variantID, quantity = 1, attributes, children, onAdd, accessibleAddingToCartLabel, ...passthroughProps } = props;
9
+ const { variantId, quantity = 1, attributes, children, onAdd, accessibleAddingToCartLabel, ...passthroughProps } = props;
10
10
  const { status, id } = useCart();
11
11
  const createCart = useCartCreateCallback();
12
12
  const addLines = useCartLinesAddCallback();
@@ -23,7 +23,7 @@ export function AddToCartButton(props) {
23
23
  lines: [
24
24
  {
25
25
  quantity: quantity,
26
- merchandiseId: variantID,
26
+ merchandiseId: variantId,
27
27
  attributes: attributes,
28
28
  },
29
29
  ],
@@ -33,7 +33,7 @@ export function AddToCartButton(props) {
33
33
  addLines([
34
34
  {
35
35
  quantity: quantity,
36
- merchandiseId: variantID,
36
+ merchandiseId: variantId,
37
37
  attributes: attributes,
38
38
  },
39
39
  ]);
@@ -1,11 +1,7 @@
1
1
  import React, { useEffect, useState, useCallback } from 'react';
2
- import { CartProvider, useInstantCheckout } from '../CartProvider';
2
+ import { useInstantCheckout } from '../CartProvider';
3
3
  /** The `BuyNowButton` component renders a button that adds an item to the cart and redirects the customer to checkout. */
4
4
  export function BuyNowButton(props) {
5
- return (React.createElement(CartProvider, null,
6
- React.createElement(Button, { ...props })));
7
- }
8
- function Button(props) {
9
5
  const { createInstantCheckout, checkoutUrl } = useInstantCheckout();
10
6
  const [loading, setLoading] = useState(false);
11
7
  const { quantity, variantId, attributes, children, ...passthroughProps } = props;
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { useShop } from '../../foundation';
3
3
  import { flattenConnection } from '../../utilities';
4
4
  import { CartCreate } from '../../graphql/graphql-constants';
@@ -26,9 +26,9 @@ export function useCartFetch() {
26
26
  }, [storeDomain, graphqlApiVersion, storefrontToken]);
27
27
  }
28
28
  export function useInstantCheckout() {
29
- const [cart, updateCart] = React.useState();
30
- const [checkoutUrl, updateCheckoutUrl] = React.useState();
31
- const [error, updateError] = React.useState();
29
+ const [cart, updateCart] = useState();
30
+ const [checkoutUrl, updateCheckoutUrl] = useState();
31
+ const [error, updateError] = useState();
32
32
  const fetch = useCartFetch();
33
33
  const createInstantCheckout = React.useCallback(async (cartInput) => {
34
34
  var _a, _b;
@@ -0,0 +1 @@
1
+ export { Link } from 'react-router-dom';
@@ -0,0 +1 @@
1
+ export { Link } from 'react-router-dom';
@@ -0,0 +1 @@
1
+ export { Link } from './Link.client';
@@ -0,0 +1 @@
1
+ export { Link } from './Link.client';
@@ -5,11 +5,4 @@ export interface LocalizationContextValue {
5
5
  availableCountries: Localization['availableCountries'];
6
6
  setCountry(country: Localization['country']): void;
7
7
  }
8
- /**
9
- * This is a workaround to support exposing context from within a server component
10
- * exported from Hydrogen.
11
- */
12
- declare global {
13
- var __localizationContext: any;
14
- }
15
- export declare const LocalizationContext: any;
8
+ export declare const LocalizationContext: import("react").Context<LocalizationContextValue | null>;
@@ -1,4 +1,2 @@
1
1
  import { createContext } from 'react';
2
- export const LocalizationContext = globalThis.__localizationContext ||
3
- createContext(null);
4
- globalThis.__localizationContext = LocalizationContext;
2
+ export const LocalizationContext = createContext(null);
@@ -4,6 +4,7 @@ import { getMeasurementAsString } from '../../utilities';
4
4
  import { StarRating } from './components/StarRating';
5
5
  import { RawHtml } from '../RawHtml';
6
6
  import { MetafieldFragment as Fragment } from '../../graphql/graphql-constants';
7
+ import { Image } from '../Image';
7
8
  /**
8
9
  * The `Metafield` component renders the value of a Storefront
9
10
  * API's [Metafield object](/api/storefront/reference/common-objects/metafield).
@@ -16,6 +17,7 @@ import { MetafieldFragment as Fragment } from '../../graphql/graphql-constants';
16
17
  * Metafield's `value`. For more information, refer to the [Default Output](#default-output) section.
17
18
  */
18
19
  export function Metafield(props) {
20
+ var _a;
19
21
  const { metafield, children, as, ...passthroughProps } = props;
20
22
  const { locale } = useShop();
21
23
  if (metafield.value == null) {
@@ -54,6 +56,12 @@ export function Metafield(props) {
54
56
  case 'json':
55
57
  const Wrapper = as !== null && as !== void 0 ? as : 'span';
56
58
  return (React.createElement(Wrapper, { ...passthroughProps }, JSON.stringify(metafield.value)));
59
+ case 'file_reference': {
60
+ if (((_a = metafield.reference) === null || _a === void 0 ? void 0 : _a.__typename) === 'MediaImage') {
61
+ const ref = metafield.reference;
62
+ return ref.image ? (React.createElement(Image, { image: ref.image, ...passthroughProps })) : null;
63
+ }
64
+ }
57
65
  default: {
58
66
  const Wrapper = as !== null && as !== void 0 ? as : 'span';
59
67
  return (React.createElement(Wrapper, { ...passthroughProps }, metafield.value.toString()));
@@ -1,4 +1,19 @@
1
1
  import * as Types from '../../graphql/types/types';
2
+ import { ImageFragmentFragment } from '../Image/ImageFragment';
2
3
  export declare type MetafieldFragmentFragment = {
3
4
  __typename?: 'Metafield';
4
- } & Pick<Types.Metafield, 'id' | 'type' | 'namespace' | 'key' | 'value' | 'createdAt' | 'updatedAt' | 'description'>;
5
+ } & Pick<Types.Metafield, 'id' | 'type' | 'namespace' | 'key' | 'value' | 'createdAt' | 'updatedAt' | 'description'> & {
6
+ reference?: Types.Maybe<({
7
+ __typename: 'MediaImage';
8
+ } & Pick<Types.MediaImage, 'id' | 'mediaContentType'> & {
9
+ image?: Types.Maybe<{
10
+ __typename?: 'Image';
11
+ } & ImageFragmentFragment>;
12
+ }) | {
13
+ __typename: 'Page';
14
+ } | {
15
+ __typename: 'Product';
16
+ } | {
17
+ __typename: 'ProductVariant';
18
+ }>;
19
+ };
@@ -15,6 +15,8 @@ export interface Model3DProps {
15
15
  };
16
16
  /** A string of either `auto`, `lazy`, or `eager` to indicate the conditions for preloading. Refer to [`loading` in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-loading-attributes-loading). */
17
17
  loading?: 'auto' | 'lazy' | 'eager';
18
+ /** A url to display an image instead of the model, useful for showing the user something before a model is loaded and ready to render. If none is provided, [Model3d.previewImage](https://shopify.dev/api/storefront/reference/products/model3d#previewimage-2021-10) is used. Refer to [`poster` in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-loading-attributes-poster). */
19
+ poster?: string;
18
20
  /** A string of either `auto`, `interaction`, or `manual` to indicate when the model should be revealed. Refer to [`reveal` in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-loading-attributes-reveal). */
19
21
  reveal?: 'auto' | 'interaction' | 'manual';
20
22
  /** A boolean to enable an AR experience. Refer to [`ar` in the <model-viewer> documentation](https://modelviewer.dev/docs/index.html#entrydocs-augmentedreality-attributes-ar). */
@@ -109,7 +109,7 @@ export function Model3D(props) {
109
109
  // TODO: What do we want to display while the model-viewer library loads?
110
110
  return null;
111
111
  }
112
- return (React.createElement("model-viewer", { ref: callbackRef, ...passthroughProps, class: className, id: id, src: model.sources[0].url, alt: model.alt, "camera-controls": (_a = passthroughProps.cameraControls) !== null && _a !== void 0 ? _a : true, poster: (_b = model.previewImage) === null || _b === void 0 ? void 0 : _b.url, autoplay: (_c = passthroughProps.autoplay) !== null && _c !== void 0 ? _c : true, loading: passthroughProps.loading, reveal: passthroughProps.reveal, ar: passthroughProps.ar, "ar-modes": passthroughProps.arModes, "ar-scale": passthroughProps.arScale, "ar-placement": passthroughProps.arPlacement, "ios-src": passthroughProps.iosSrc, "touch-action": passthroughProps.touchAction, "disable-zoom": passthroughProps.disableZoom, "orbit-sensitivity": passthroughProps.orbitSensitivity, "auto-rotate": passthroughProps.autoRotate, "auto-rotate-delay": passthroughProps.autoRotateDelay, "rotation-per-second": passthroughProps.rotationPerSecond, "interaction-policy": passthroughProps.interactionPolicy, "interaction-prompt": passthroughProps.interactionPrompt, "interaction-prompt-style": passthroughProps.interactionPromptStyle, "interaction-prompt-threshold": passthroughProps.interactionPromptThreshold, "camera-orbit": passthroughProps.cameraOrbit, "camera-target": passthroughProps.cameraTarget, "field-of-view": passthroughProps.fieldOfView, "max-camera-orbit": passthroughProps.maxCameraOrbit, "min-camera-orbit": passthroughProps.minCameraOrbit, "max-field-of-view": passthroughProps.maxFieldOfView, "min-field-of-view": passthroughProps.minFieldOfView, bounds: passthroughProps.bounds, "interpolation-decay": (_d = passthroughProps.interpolationDecay) !== null && _d !== void 0 ? _d : 100, "skybox-image": passthroughProps.skyboxImage, "environment-image": passthroughProps.environmentImage, exposure: passthroughProps.exposure, "shadow-intensity": (_e = passthroughProps.shadowIntensity) !== null && _e !== void 0 ? _e : 0, "shadow-softness": (_f = passthroughProps.shadowSoftness) !== null && _f !== void 0 ? _f : 0, "animation-name": passthroughProps.animationName, "animation-crossfade-duration": passthroughProps.animationCrossfadeDuration, "variant-name": passthroughProps.variantName, orientation: passthroughProps.orientation, scale: passthroughProps.scale }, children));
112
+ return (React.createElement("model-viewer", { ref: callbackRef, ...passthroughProps, class: className, id: id, src: model.sources[0].url, alt: model.alt, "camera-controls": (_a = passthroughProps.cameraControls) !== null && _a !== void 0 ? _a : true, poster: passthroughProps.poster || ((_b = model.previewImage) === null || _b === void 0 ? void 0 : _b.url), autoplay: (_c = passthroughProps.autoplay) !== null && _c !== void 0 ? _c : true, loading: passthroughProps.loading, reveal: passthroughProps.reveal, ar: passthroughProps.ar, "ar-modes": passthroughProps.arModes, "ar-scale": passthroughProps.arScale, "ar-placement": passthroughProps.arPlacement, "ios-src": passthroughProps.iosSrc, "touch-action": passthroughProps.touchAction, "disable-zoom": passthroughProps.disableZoom, "orbit-sensitivity": passthroughProps.orbitSensitivity, "auto-rotate": passthroughProps.autoRotate, "auto-rotate-delay": passthroughProps.autoRotateDelay, "rotation-per-second": passthroughProps.rotationPerSecond, "interaction-policy": passthroughProps.interactionPolicy, "interaction-prompt": passthroughProps.interactionPrompt, "interaction-prompt-style": passthroughProps.interactionPromptStyle, "interaction-prompt-threshold": passthroughProps.interactionPromptThreshold, "camera-orbit": passthroughProps.cameraOrbit, "camera-target": passthroughProps.cameraTarget, "field-of-view": passthroughProps.fieldOfView, "max-camera-orbit": passthroughProps.maxCameraOrbit, "min-camera-orbit": passthroughProps.minCameraOrbit, "max-field-of-view": passthroughProps.maxFieldOfView, "min-field-of-view": passthroughProps.minFieldOfView, bounds: passthroughProps.bounds, "interpolation-decay": (_d = passthroughProps.interpolationDecay) !== null && _d !== void 0 ? _d : 100, "skybox-image": passthroughProps.skyboxImage, "environment-image": passthroughProps.environmentImage, exposure: passthroughProps.exposure, "shadow-intensity": (_e = passthroughProps.shadowIntensity) !== null && _e !== void 0 ? _e : 0, "shadow-softness": (_f = passthroughProps.shadowSoftness) !== null && _f !== void 0 ? _f : 0, "animation-name": passthroughProps.animationName, "animation-crossfade-duration": passthroughProps.animationCrossfadeDuration, "variant-name": passthroughProps.variantName, orientation: passthroughProps.orientation, scale: passthroughProps.scale }, children));
113
113
  }
114
114
  Model3D.Fragment = Fragment;
115
115
  export const Model3DFragment = Fragment;
@@ -1,5 +1,5 @@
1
1
  import { ProductOptionsHookValue } from '../../hooks';
2
- import { GraphQLConnection, ParsedMetafield } from '../../types';
2
+ import { GraphQLConnection, ParsedMetafield, RawMetafield } from '../../types';
3
3
  import { ProductProviderFragmentFragment } from './ProductProviderFragment';
4
4
  import { Product } from './types';
5
5
  import { Collection, Image } from '../../graphql/types/types';
@@ -8,7 +8,7 @@ export declare type ProductContextType = Omit<Product, 'media' | 'metafields' |
8
8
  media?: ProductProviderFragmentFragment['media']['edges'][0]['node'][];
9
9
  mediaConnection?: ProductProviderFragmentFragment['media'];
10
10
  metafields?: ParsedMetafield[];
11
- metafieldsConnection?: ProductProviderFragmentFragment['metafields'];
11
+ metafieldsConnection?: GraphQLConnection<RawMetafield>;
12
12
  images?: Partial<Image>[];
13
13
  imagesConnection?: GraphQLConnection<Partial<Image>>;
14
14
  collections?: Partial<Collection>[];
@@ -1,5 +1,5 @@
1
1
  import { SellingPlanGroup, Variant } from '../../hooks/useProductOptions';
2
- import { GraphQLConnection } from '../../types';
2
+ import { GraphQLConnection, RawMetafield } from '../../types';
3
3
  import { ProductProviderFragmentFragment } from './ProductProviderFragment';
4
4
  import { ImageFragmentFragment } from '../Image/ImageFragment';
5
5
  import { Collection } from '../../graphql/types/types';
@@ -9,7 +9,7 @@ export interface Product {
9
9
  handle?: ProductProviderFragmentFragment['descriptionHtml'];
10
10
  id?: ProductProviderFragmentFragment['id'];
11
11
  media?: ProductProviderFragmentFragment['media'];
12
- metafields?: ProductProviderFragmentFragment['metafields'];
12
+ metafields?: GraphQLConnection<RawMetafield>;
13
13
  priceRange?: Partial<ProductProviderFragmentFragment['priceRange']>;
14
14
  title?: ProductProviderFragmentFragment['title'];
15
15
  variants?: GraphQLConnection<Variant>;
@@ -7,5 +7,5 @@ declare type PropsWeControl = 'disabled';
7
7
  * selected variant. Clicking this button automatically adds the selected variant to the cart.
8
8
  * It must be a descendent of a `ProductProvider` and `CartProvider` component.
9
9
  */
10
- export declare function SelectedVariantAddToCartButton<TTag extends React.ElementType = 'button'>(props: Props<TTag, PropsWeControl> & Omit<AddToCartButtonProps, 'variantID'>): JSX.Element;
10
+ export declare function SelectedVariantAddToCartButton<TTag extends React.ElementType = 'button'>(props: Props<TTag, PropsWeControl> & Omit<AddToCartButtonProps, 'variantId'>): JSX.Element;
11
11
  export {};
@@ -13,5 +13,5 @@ export function SelectedVariantAddToCartButton(props) {
13
13
  throw new Error('Expected a Product context, but none was found');
14
14
  }
15
15
  const { children, quantity, attributes, ...passthroughProps } = props;
16
- return (React.createElement(CartButton, { ...passthroughProps, variantID: (_b = (_a = product.selectedVariant) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '', quantity: quantity !== null && quantity !== void 0 ? quantity : 1, disabled: !product.selectedVariant || passthroughProps.disabled, attributes: attributes }, children));
16
+ return (React.createElement(CartButton, { ...passthroughProps, variantId: (_b = (_a = product.selectedVariant) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '', quantity: quantity !== null && quantity !== void 0 ? quantity : 1, disabled: !product.selectedVariant || passthroughProps.disabled, attributes: attributes }, children));
17
17
  }
@@ -1,4 +1,4 @@
1
- export { Link } from 'react-router-dom';
1
+ export { Link } from './Link';
2
2
  export type { MediaFileProps } from './MediaFile';
3
3
  export { MediaFile, MediaFileFragment } from './MediaFile';
4
4
  export type { VideoProps } from './Video';
@@ -1,4 +1,4 @@
1
- export { Link } from 'react-router-dom';
1
+ export { Link } from './Link';
2
2
  export { MediaFile, MediaFileFragment } from './MediaFile';
3
3
  export { Video } from './Video';
4
4
  export { Image } from './Image';
@@ -73,7 +73,7 @@ const renderHydrogen = (App, hook) => {
73
73
  response.socket.on('error', (error) => {
74
74
  console.error('Fatal', error);
75
75
  });
76
- let didError = false;
76
+ let didError;
77
77
  const head = template.match(/<head>(.+?)<\/head>/s)[1];
78
78
  const { startWriting, abort } = pipeToNodeWritable(React.createElement(Html, { head: head },
79
79
  React.createElement(ReactApp, { ...state })), response, {
@@ -84,22 +84,23 @@ const renderHydrogen = (App, hook) => {
84
84
  * additional checks downstream?
85
85
  */
86
86
  response.setHeader(getCacheControlHeader({ dev }), componentResponse.cacheControlHeader);
87
+ writeHeadToServerResponse(response, componentResponse, didError);
88
+ if (isRedirect(response)) {
89
+ // Return redirects early without further rendering/streaming
90
+ return response.end();
91
+ }
87
92
  if (!componentResponse.canStream())
88
93
  return;
89
- response.statusCode = didError ? 500 : 200;
90
- response.setHeader('Content-type', 'text/html');
91
- response.write('<!DOCTYPE html>');
92
- startWriting();
94
+ startWritingHtmlToServerResponse(response, startWriting, dev ? didError : undefined);
93
95
  },
94
96
  onCompleteAll() {
95
- var _a;
96
- if (componentResponse.canStream())
97
+ if (componentResponse.canStream() || response.writableEnded)
97
98
  return;
98
- response.statusCode =
99
- (_a = componentResponse.status) !== null && _a !== void 0 ? _a : (didError ? 500 : 200);
100
- componentResponse.headers.forEach((value, header) => {
101
- response.setHeader(header, value);
102
- });
99
+ writeHeadToServerResponse(response, componentResponse, didError);
100
+ if (isRedirect(response)) {
101
+ // Redirects found after any async code
102
+ return response.end();
103
+ }
103
104
  if (componentResponse.customBody) {
104
105
  if (componentResponse.customBody instanceof Promise) {
105
106
  componentResponse.customBody.then((body) => response.end(body));
@@ -109,14 +110,14 @@ const renderHydrogen = (App, hook) => {
109
110
  }
110
111
  }
111
112
  else {
112
- response.setHeader('Content-type', 'text/html');
113
- response.write('<!DOCTYPE html>');
114
- startWriting();
113
+ startWritingHtmlToServerResponse(response, startWriting, dev ? didError : undefined);
115
114
  }
116
115
  },
117
116
  onError(error) {
118
- didError = true;
119
- if (dev) {
117
+ didError = error;
118
+ if (dev && response.headersSent) {
119
+ // Calling write would flush headers automatically.
120
+ // Delay this error until headers are properly sent.
120
121
  response.write(getErrorMarkup(error));
121
122
  }
122
123
  console.error(error);
@@ -139,7 +140,7 @@ const renderHydrogen = (App, hook) => {
139
140
  response.socket.on('error', (error) => {
140
141
  console.error('Fatal', error);
141
142
  });
142
- let didError = false;
143
+ let didError;
143
144
  const writer = new HydrationWriter();
144
145
  const { startWriting, abort } = pipeToNodeWritable(React.createElement(HydrationContext.Provider, { value: true },
145
146
  React.createElement(ReactApp, { ...state })), writer, {
@@ -157,7 +158,7 @@ const renderHydrogen = (App, hook) => {
157
158
  response.end(generateWireSyntaxFromRenderedHtml(writer.toString()));
158
159
  },
159
160
  onError(error) {
160
- didError = true;
161
+ didError = error;
161
162
  console.error(error);
162
163
  },
163
164
  });
@@ -174,7 +175,7 @@ function buildReactApp({ App, state, context, request, dev, }) {
174
175
  const componentResponse = new ServerComponentResponse();
175
176
  const ReactApp = (props) => (React.createElement(StaticRouter, { location: { pathname: state.pathname, search: state.search }, context: context },
176
177
  React.createElement(HelmetProvider, { context: helmetContext },
177
- React.createElement(App, { request: request, response: componentResponse, ...props }))));
178
+ React.createElement(App, { ...props, request: request, response: componentResponse }))));
178
179
  return { helmetContext, ReactApp, componentResponse };
179
180
  }
180
181
  function extractHeadElements(helmetContext) {
@@ -304,3 +305,32 @@ async function renderAppFromStringWithPrepass(ReactApp, state, isReactHydrationR
304
305
  : body;
305
306
  }
306
307
  export default renderHydrogen;
308
+ function startWritingHtmlToServerResponse(response, startWriting, error) {
309
+ if (!response.headersSent) {
310
+ response.setHeader('Content-type', 'text/html');
311
+ response.write('<!DOCTYPE html>');
312
+ }
313
+ startWriting();
314
+ if (error) {
315
+ // This error was delayed until the headers were properly sent.
316
+ response.write(getErrorMarkup(error));
317
+ }
318
+ }
319
+ function writeHeadToServerResponse(response, { headers, status, customStatus }, error) {
320
+ var _a, _b;
321
+ if (response.headersSent)
322
+ return;
323
+ headers.forEach((value, key) => response.setHeader(key, value));
324
+ if (error) {
325
+ response.statusCode = 500;
326
+ }
327
+ else {
328
+ response.statusCode = (_b = (_a = customStatus === null || customStatus === void 0 ? void 0 : customStatus.code) !== null && _a !== void 0 ? _a : status) !== null && _b !== void 0 ? _b : 200;
329
+ if (customStatus === null || customStatus === void 0 ? void 0 : customStatus.text) {
330
+ response.statusMessage = customStatus.text;
331
+ }
332
+ }
333
+ }
334
+ function isRedirect(response) {
335
+ return response.statusCode >= 300 && response.statusCode < 400;
336
+ }
@@ -7,12 +7,14 @@ import { useServerState } from '../useServerState';
7
7
  * server state, which in turn fetches the correct server component.
8
8
  */
9
9
  export function ServerStateRouter() {
10
- const { setServerState, pending } = useServerState();
10
+ const { setServerState, pending, serverState } = useServerState();
11
11
  const [isNavigating, setIsNavigating] = useState(false);
12
12
  const location = useLocation();
13
13
  useEffect(() => {
14
- setIsNavigating(true);
15
- setServerState('pathname', location.pathname);
14
+ if (serverState.pathname !== location.pathname) {
15
+ setIsNavigating(true);
16
+ setServerState('pathname', location.pathname);
17
+ }
16
18
  }, [location.pathname, setServerState]);
17
19
  useEffect(() => {
18
20
  /**
@@ -1,24 +1,25 @@
1
1
  import React, { ReactNode } from 'react';
2
- export interface ServerStateContextValue {
3
- serverState: Record<string, any>;
4
- setServerState(input: (() => void) | Record<string, any> | string, value?: string | Record<string, any>): void;
5
- pending: any;
6
- }
7
- /**
8
- * This is a workaround to support exposing context from within a server component
9
- * exported from Hydrogen.
10
- */
11
2
  declare global {
12
- var __serverStateContext: any;
3
+ var __DEV__: boolean;
4
+ }
5
+ export interface ServerState {
6
+ pathname: string;
7
+ search: string;
8
+ [key: string]: any;
9
+ }
10
+ export interface ServerStateSetter {
11
+ (input: ((prev: ServerState) => Partial<ServerState>) | Partial<ServerState> | string, propValue?: any): void;
12
+ }
13
+ export interface ServerStateContextValue {
14
+ pending: boolean;
15
+ serverState: ServerState;
16
+ setServerState: ServerStateSetter;
13
17
  }
14
- export declare const ServerStateContext: any;
15
- interface Props {
16
- serverState: Record<string, any>;
17
- setServerState: React.Dispatch<React.SetStateAction<{
18
- pathname: string;
19
- search: string;
20
- }>>;
18
+ export declare const ServerStateContext: React.Context<ServerStateContextValue>;
19
+ interface ServerStateProviderProps {
20
+ serverState: ServerState;
21
+ setServerState: React.Dispatch<React.SetStateAction<ServerState>>;
21
22
  children: ReactNode;
22
23
  }
23
- export declare function ServerStateProvider({ serverState, setServerState, children, }: Props): JSX.Element;
24
+ export declare function ServerStateProvider({ serverState, setServerState, children, }: ServerStateProviderProps): JSX.Element;
24
25
  export {};
@@ -1,11 +1,10 @@
1
1
  import React, { createContext, useMemo, useCallback,
2
2
  // @ts-ignore
3
3
  useTransition, } from 'react';
4
- export const ServerStateContext = globalThis.__serverStateContext || createContext(null);
5
- globalThis.__serverStateContext = ServerStateContext;
4
+ export const ServerStateContext = createContext(null);
6
5
  export function ServerStateProvider({ serverState, setServerState, children, }) {
7
6
  const [pending, startTransition] = useTransition();
8
- const setServerStateCallback = useCallback((input, value) => {
7
+ const setServerStateCallback = useCallback((input, propValue) => {
9
8
  /**
10
9
  * By wrapping this state change in a transition, React renders the new state
11
10
  * concurrently in a new "tree" instead of Suspending and showing the (blank)
@@ -14,26 +13,27 @@ export function ServerStateProvider({ serverState, setServerState, children, })
14
13
  * the `pending` flag also provided by the hook to display in the UI.
15
14
  */
16
15
  startTransition(() => {
17
- // Support callback-style setState
18
- if (typeof input === 'function') {
19
- // @ts-ignore
20
- return setServerState(input);
21
- }
22
- // Support a simple object, and spread it into the existing object.
23
- if (typeof input === 'object') {
24
- return setServerState((prev) => ({
16
+ return setServerState((prev) => {
17
+ let newValue;
18
+ if (typeof input === 'function') {
19
+ newValue = input(prev);
20
+ }
21
+ else if (typeof input === 'string') {
22
+ newValue = { [input]: propValue };
23
+ }
24
+ else {
25
+ newValue = input;
26
+ }
27
+ if (__DEV__) {
28
+ if ('request' in newValue || 'response' in newValue) {
29
+ console.warn(`Custom "request" and "response" properties in server state are ignored. Use a different name.`);
30
+ }
31
+ }
32
+ return {
25
33
  ...prev,
26
- // @ts-ignore
27
- ...input,
28
- }));
29
- }
30
- // Support a key, value as well.
31
- if (typeof input === 'string') {
32
- return setServerState((prev) => ({
33
- ...prev,
34
- [input]: value,
35
- }));
36
- }
34
+ ...newValue,
35
+ };
36
+ });
37
37
  });
38
38
  }, [setServerState, startTransition]);
39
39
  const value = useMemo(() => ({
@@ -1 +1 @@
1
- export { ServerStateProvider, ServerStateContext, ServerStateContextValue, } from './ServerStateProvider.client';
1
+ export { ServerStateProvider, ServerStateContext, ServerStateContextValue, ServerState, } from './ServerStateProvider.client';
@@ -1,5 +1,5 @@
1
1
  import { ShopifyProviderProps } from './types';
2
- export declare const DEFAULT_API_VERSION = "unstable";
2
+ export { DEFAULT_API_VERSION } from './consts';
3
3
  /**
4
4
  * The `ShopifyProvider` component wraps your entire app and provides support for hooks.
5
5
  * You should place it in your app's entry point component. For example, `<App>`.
@@ -1,6 +1,7 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import { ShopifyContext } from './ShopifyContext';
3
- export const DEFAULT_API_VERSION = 'unstable';
3
+ import { DEFAULT_API_VERSION } from './consts';
4
+ export { DEFAULT_API_VERSION } from './consts';
4
5
  /**
5
6
  * The `ShopifyProvider` component wraps your entire app and provides support for hooks.
6
7
  * You should place it in your app's entry point component. For example, `<App>`.
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ShopifyProvider } from './ShopifyProvider';
3
- import { QueryProvider } from '../../hooks/useQuery';
3
+ import { QueryProvider } from '../../foundation/useQuery';
4
4
  export function ShopifyServerProvider({ children, shopifyConfig, hydrationContext, }) {
5
5
  return (React.createElement(ShopifyProvider, { shopifyConfig: shopifyConfig },
6
6
  React.createElement(QueryProvider, { hydrationContext: hydrationContext }, children)));
@@ -0,0 +1 @@
1
+ export declare const DEFAULT_API_VERSION = "unstable";
@@ -0,0 +1 @@
1
+ export const DEFAULT_API_VERSION = 'unstable';
@@ -11,6 +11,16 @@ key,
11
11
  queryFn,
12
12
  /** Options including `cache` to manage the cache behavior of the sub-request. */
13
13
  queryOptions) {
14
+ const resolvedQueryOptions = {
15
+ /**
16
+ * Prevent react-query from from retrying request failures. This sometimes bites developers
17
+ * because they will get back a 200 GraphQL response with errors, but not properly check
18
+ * for errors. This leads to a failed `queryFn` and react-query keeps running it, leading
19
+ * to a much slower response time and a poor developer experience.
20
+ */
21
+ retry: false,
22
+ ...(queryOptions !== null && queryOptions !== void 0 ? queryOptions : {}),
23
+ };
14
24
  /**
15
25
  * Attempt to read the query from cache. If it doesn't exist or if it's stale, regenerate it.
16
26
  */
@@ -32,21 +42,16 @@ queryOptions) {
32
42
  const lockExists = await getItemFromCache(lockKey);
33
43
  if (lockExists)
34
44
  return;
35
- console.log(`[stale regen] no cache lock`);
36
45
  await setItemInCache(lockKey, true);
37
- console.log(`[stale regen] set cache lock`);
38
46
  try {
39
47
  const output = await generateNewOutput();
40
- console.log(`[stale regen] got new output`);
41
- await setItemInCache(key, output, queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.cache);
42
- console.log(`[stale regen] set new output`);
48
+ await setItemInCache(key, output, resolvedQueryOptions === null || resolvedQueryOptions === void 0 ? void 0 : resolvedQueryOptions.cache);
43
49
  }
44
50
  catch (e) {
45
51
  console.error(`Error generating async response: ${e.message}`);
46
52
  }
47
53
  finally {
48
54
  await deleteItemFromCache(lockKey);
49
- console.log(`[stale regen] deleted lock`);
50
55
  }
51
56
  });
52
57
  }
@@ -56,8 +61,8 @@ queryOptions) {
56
61
  /**
57
62
  * Important: Do this async
58
63
  */
59
- runDelayedFunction(async () => await setItemInCache(key, newOutput, queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.cache));
64
+ runDelayedFunction(async () => await setItemInCache(key, newOutput, resolvedQueryOptions === null || resolvedQueryOptions === void 0 ? void 0 : resolvedQueryOptions.cache));
60
65
  return newOutput;
61
66
  }
62
- return useReactQuery(key, cachedQueryFn, queryOptions);
67
+ return useReactQuery(key, cachedQueryFn, resolvedQueryOptions);
63
68
  }
@@ -12,4 +12,4 @@
12
12
  * | `pending` | Whether a [transition is pending](https://github.com/reactwg/react-18/discussions/41). |
13
13
  *
14
14
  */
15
- export declare function useServerState(): unknown;
15
+ export declare function useServerState(): import("../ServerStateProvider").ServerStateContextValue;
@@ -1,10 +1,9 @@
1
1
  import { FunctionComponent } from 'react';
2
- interface Props {
2
+ interface ClientMarkerMeta {
3
3
  name: string;
4
4
  id: string;
5
- props: any;
6
5
  component: FunctionComponent;
7
6
  named: boolean;
8
7
  }
9
- export declare function ClientMarker({ name, id, props: allProps, component: Component, named, }: Props): JSX.Element;
8
+ export declare function wrapInClientMarker(meta: ClientMarkerMeta): (props: any) => JSX.Element;
10
9
  export {};