@shopify/hydrogen 0.15.0 → 0.17.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 (140) hide show
  1. package/CHANGELOG.md +113 -1
  2. package/dist/esnext/FileSessionStorage.d.ts +1 -0
  3. package/dist/esnext/FileSessionStorage.js +1 -0
  4. package/dist/esnext/client.d.ts +3 -2
  5. package/dist/esnext/client.js +3 -2
  6. package/dist/esnext/components/CartProvider/CartProvider.client.d.ts +1 -1
  7. package/dist/esnext/components/CartProvider/CartProvider.client.js +3 -3
  8. package/dist/esnext/components/ExternalVideo/ExternalVideo.d.ts +2 -2
  9. package/dist/esnext/components/ExternalVideo/ExternalVideo.js +1 -1
  10. package/dist/esnext/components/Image/Image.d.ts +2 -2
  11. package/dist/esnext/components/Image/Image.js +1 -1
  12. package/dist/esnext/components/Link/Link.client.d.ts +1 -1
  13. package/dist/esnext/components/Link/Link.client.js +3 -3
  14. package/dist/esnext/components/LocalizationProvider/LocalizationClientProvider.client.js +4 -4
  15. package/dist/esnext/components/LocalizationProvider/LocalizationProvider.server.d.ts +2 -2
  16. package/dist/esnext/components/LocalizationProvider/LocalizationProvider.server.js +2 -2
  17. package/dist/esnext/components/MediaFile/MediaFile.d.ts +2 -2
  18. package/dist/esnext/components/MediaFile/MediaFile.js +1 -1
  19. package/dist/esnext/components/Metafield/Metafield.client.d.ts +8 -4
  20. package/dist/esnext/components/Metafield/Metafield.client.js +6 -2
  21. package/dist/esnext/components/ModelViewer/ModelViewer.client.d.ts +3 -3
  22. package/dist/esnext/components/ModelViewer/ModelViewer.client.js +1 -1
  23. package/dist/esnext/components/Money/Money.client.d.ts +2 -2
  24. package/dist/esnext/components/Money/Money.client.js +1 -1
  25. package/dist/esnext/components/ProductDescription/ProductDescription.client.d.ts +1 -1
  26. package/dist/esnext/components/ProductDescription/ProductDescription.client.js +1 -1
  27. package/dist/esnext/components/ProductMetafield/ProductMetafield.client.d.ts +3 -3
  28. package/dist/esnext/components/ProductMetafield/ProductMetafield.client.js +1 -1
  29. package/dist/esnext/components/ProductPrice/ProductPrice.client.d.ts +1 -1
  30. package/dist/esnext/components/ProductPrice/ProductPrice.client.js +1 -1
  31. package/dist/esnext/components/ProductProvider/ProductProvider.client.d.ts +1 -1
  32. package/dist/esnext/components/ProductTitle/ProductTitle.client.d.ts +1 -1
  33. package/dist/esnext/components/ProductTitle/ProductTitle.client.js +1 -1
  34. package/dist/esnext/components/UnitPrice/UnitPrice.client.d.ts +4 -4
  35. package/dist/esnext/components/UnitPrice/UnitPrice.client.js +2 -2
  36. package/dist/esnext/components/Video/Video.d.ts +2 -2
  37. package/dist/esnext/components/Video/Video.js +1 -1
  38. package/dist/esnext/constants.d.ts +4 -0
  39. package/dist/esnext/constants.js +4 -0
  40. package/dist/esnext/entry-client.js +4 -4
  41. package/dist/esnext/entry-server.d.ts +1 -1
  42. package/dist/esnext/entry-server.js +12 -5
  43. package/dist/esnext/foundation/Analytics/Analytics.server.js +4 -2
  44. package/dist/esnext/foundation/AnalyticsErrorBoundary.client.d.ts +4 -0
  45. package/dist/esnext/foundation/AnalyticsErrorBoundary.client.js +8 -0
  46. package/dist/esnext/foundation/Cookie/Cookie.d.ts +48 -0
  47. package/dist/esnext/foundation/Cookie/Cookie.js +66 -0
  48. package/dist/esnext/foundation/CookieSessionStorage/CookieSessionStorage.d.ts +5 -0
  49. package/dist/esnext/foundation/CookieSessionStorage/CookieSessionStorage.js +31 -0
  50. package/dist/esnext/foundation/FileSessionStorage/FileSessionStorage.d.ts +6 -0
  51. package/dist/esnext/foundation/FileSessionStorage/FileSessionStorage.js +148 -0
  52. package/dist/esnext/foundation/MemorySessionStorage/MemorySessionStorage.d.ts +5 -0
  53. package/dist/esnext/foundation/MemorySessionStorage/MemorySessionStorage.js +53 -0
  54. package/dist/esnext/foundation/Router/BrowserRouter.client.js +8 -8
  55. package/dist/esnext/foundation/ServerPropsProvider/ServerPropsProvider.d.ts +40 -0
  56. package/dist/esnext/foundation/ServerPropsProvider/ServerPropsProvider.js +76 -0
  57. package/dist/esnext/foundation/ServerPropsProvider/index.d.ts +2 -0
  58. package/dist/esnext/foundation/ServerPropsProvider/index.js +1 -0
  59. package/dist/esnext/foundation/index.d.ts +1 -1
  60. package/dist/esnext/foundation/index.js +1 -1
  61. package/dist/esnext/foundation/session/session.d.ts +27 -0
  62. package/dist/esnext/foundation/session/session.js +37 -0
  63. package/dist/esnext/foundation/useQuery/hooks.d.ts +3 -3
  64. package/dist/esnext/foundation/useQuery/hooks.js +1 -1
  65. package/dist/esnext/foundation/useServerProps/index.d.ts +1 -0
  66. package/dist/esnext/foundation/useServerProps/index.js +1 -0
  67. package/dist/esnext/foundation/useServerProps/use-server-props.d.ts +21 -0
  68. package/dist/esnext/foundation/useServerProps/use-server-props.js +35 -0
  69. package/dist/esnext/foundation/useSession/useSession.d.ts +2 -0
  70. package/dist/esnext/foundation/useSession/useSession.js +8 -0
  71. package/dist/esnext/foundation/useUrl/useUrl.js +8 -1
  72. package/dist/esnext/framework/Hydration/ServerComponentRequest.server.d.ts +2 -0
  73. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-config.js +27 -12
  74. package/dist/esnext/hooks/useMoney/hooks.d.ts +1 -1
  75. package/dist/esnext/hooks/useMoney/hooks.js +1 -1
  76. package/dist/esnext/hooks/useParsedMetafields/useParsedMetafields.d.ts +2 -2
  77. package/dist/esnext/hooks/useParsedMetafields/useParsedMetafields.js +2 -2
  78. package/dist/esnext/hooks/useShopQuery/hooks.d.ts +2 -2
  79. package/dist/esnext/hooks/useShopQuery/hooks.js +5 -17
  80. package/dist/esnext/index.d.ts +4 -0
  81. package/dist/esnext/index.js +4 -0
  82. package/dist/esnext/storefront-api-types.d.ts +31 -13
  83. package/dist/esnext/storefront-api-types.js +14 -2
  84. package/dist/esnext/types.d.ts +2 -0
  85. package/dist/esnext/utilities/apiRoutes.d.ts +4 -3
  86. package/dist/esnext/utilities/apiRoutes.js +35 -8
  87. package/dist/esnext/utilities/flattenConnection/flattenConnection.d.ts +1 -1
  88. package/dist/esnext/utilities/flattenConnection/flattenConnection.js +1 -1
  89. package/dist/esnext/utilities/parseMetafieldValue/parseMetafieldValue.d.ts +1 -1
  90. package/dist/esnext/utilities/parseMetafieldValue/parseMetafieldValue.js +1 -1
  91. package/dist/esnext/utilities/storefrontApi.d.ts +4 -0
  92. package/dist/esnext/utilities/storefrontApi.js +21 -0
  93. package/dist/esnext/version.d.ts +1 -1
  94. package/dist/esnext/version.js +1 -1
  95. package/dist/node/constants.d.ts +4 -0
  96. package/dist/node/constants.js +5 -1
  97. package/dist/node/entry-server.d.ts +1 -1
  98. package/dist/node/entry-server.js +12 -5
  99. package/dist/node/foundation/Analytics/Analytics.server.js +27 -2
  100. package/dist/node/foundation/AnalyticsErrorBoundary.client.d.ts +4 -0
  101. package/dist/node/foundation/AnalyticsErrorBoundary.client.js +14 -0
  102. package/dist/node/foundation/Router/BrowserRouter.client.js +8 -8
  103. package/dist/node/foundation/ServerPropsProvider/ServerPropsProvider.d.ts +40 -0
  104. package/dist/node/foundation/ServerPropsProvider/ServerPropsProvider.js +101 -0
  105. package/dist/node/foundation/ServerPropsProvider/index.d.ts +2 -0
  106. package/dist/node/foundation/ServerPropsProvider/index.js +6 -0
  107. package/dist/node/foundation/session/session.d.ts +27 -0
  108. package/dist/node/foundation/session/session.js +43 -0
  109. package/dist/node/foundation/useServerProps/use-server-props.d.ts +21 -0
  110. package/dist/node/foundation/useServerProps/use-server-props.js +40 -0
  111. package/dist/node/framework/Hydration/ServerComponentRequest.server.d.ts +2 -0
  112. package/dist/node/framework/plugins/vite-plugin-hydrogen-config.js +27 -12
  113. package/dist/node/storefront-api-types.d.ts +31 -13
  114. package/dist/node/storefront-api-types.js +14 -2
  115. package/dist/node/types.d.ts +2 -0
  116. package/dist/node/utilities/apiRoutes.d.ts +4 -3
  117. package/dist/node/utilities/apiRoutes.js +35 -8
  118. package/dist/node/utilities/flattenConnection/flattenConnection.d.ts +1 -1
  119. package/dist/node/utilities/flattenConnection/flattenConnection.js +1 -1
  120. package/dist/node/utilities/parseMetafieldValue/parseMetafieldValue.d.ts +1 -1
  121. package/dist/node/utilities/parseMetafieldValue/parseMetafieldValue.js +1 -1
  122. package/dist/node/utilities/storefrontApi.d.ts +4 -0
  123. package/dist/node/utilities/storefrontApi.js +25 -0
  124. package/dist/node/version.d.ts +1 -1
  125. package/dist/node/version.js +1 -1
  126. package/package.json +4 -1
  127. package/dist/esnext/foundation/ServerStateProvider/index.d.ts +0 -2
  128. package/dist/esnext/foundation/ServerStateProvider/index.js +0 -1
  129. package/dist/esnext/foundation/useServerState/index.d.ts +0 -1
  130. package/dist/esnext/foundation/useServerState/index.js +0 -1
  131. package/dist/esnext/foundation/useServerState/use-server-state.d.ts +0 -16
  132. package/dist/esnext/foundation/useServerState/use-server-state.js +0 -20
  133. package/dist/node/foundation/ServerStateProvider/ServerStateProvider.d.ts +0 -30
  134. package/dist/node/foundation/ServerStateProvider/ServerStateProvider.js +0 -77
  135. package/dist/node/foundation/ServerStateProvider/index.d.ts +0 -2
  136. package/dist/node/foundation/ServerStateProvider/index.js +0 -6
  137. package/dist/node/foundation/useServerState/index.d.ts +0 -1
  138. package/dist/node/foundation/useServerState/index.js +0 -5
  139. package/dist/node/foundation/useServerState/use-server-state.d.ts +0 -16
  140. package/dist/node/foundation/useServerState/use-server-state.js +0 -24
@@ -170,7 +170,13 @@ export var CollectionSortKeys;
170
170
  /** Sort by the `updated_at` value. */
171
171
  CollectionSortKeys["UpdatedAt"] = "UPDATED_AT";
172
172
  })(CollectionSortKeys || (CollectionSortKeys = {}));
173
- /** ISO 3166-1 alpha-2 country codes with some differences. */
173
+ /**
174
+ * The code designating a country, which generally follows ISO 3166-1 alpha-2 guidelines.
175
+ * If a territory doesn't have a country code value in the `CountryCode` enum, it might be considered a subdivision
176
+ * of another country. For example, the territories associated with Spain are represented by the country code `ES`,
177
+ * and the territories associated with the United States of America are represented by the country code `US`.
178
+ *
179
+ */
174
180
  export var CountryCode;
175
181
  (function (CountryCode) {
176
182
  /** Ascension Island. */
@@ -1101,7 +1107,13 @@ export var DiscountApplicationTargetType;
1101
1107
  /** The discount applies onto shipping lines. */
1102
1108
  DiscountApplicationTargetType["ShippingLine"] = "SHIPPING_LINE";
1103
1109
  })(DiscountApplicationTargetType || (DiscountApplicationTargetType = {}));
1104
- /** Denotes the type of data this filter group represents. */
1110
+ /**
1111
+ * The type of data that the filter group represents.
1112
+ *
1113
+ * For more information, refer to [Filter products in a collection with the Storefront API]
1114
+ * (https://shopify.dev/api/examples/filter-products).
1115
+ *
1116
+ */
1105
1117
  export var FilterType;
1106
1118
  (function (FilterType) {
1107
1119
  /** A boolean value. */
@@ -4,6 +4,7 @@ import type { Logger } from './utilities/log/log';
4
4
  import type { ServerComponentRequest } from './framework/Hydration/ServerComponentRequest.server';
5
5
  import type { ServerComponentResponse } from './framework/Hydration/ServerComponentResponse.server';
6
6
  import type { Metafield, ProductVariant, Product, MediaImage } from './storefront-api-types';
7
+ import type { SessionStorageAdapter } from './foundation/session/session';
7
8
  declare type CommonOptions = {
8
9
  App: any;
9
10
  routes?: ImportGlobEagerOutput;
@@ -42,6 +43,7 @@ export declare type ServerHandlerConfig = {
42
43
  routes?: ImportGlobEagerOutput;
43
44
  shopifyConfig: ShopifyConfig;
44
45
  serverAnalyticsConnectors?: Array<ServerAnalyticsConnector>;
46
+ session?: (log: Logger) => SessionStorageAdapter;
45
47
  };
46
48
  export declare type ClientHandlerConfig = {
47
49
  shopifyConfig: ShopifyConfig;
@@ -1,9 +1,12 @@
1
1
  import { ImportGlobEagerOutput, ShopifyConfig } from '../types';
2
+ import type { ServerComponentRequest } from '../framework/Hydration/ServerComponentRequest.server';
2
3
  import type { ASTNode } from 'graphql';
4
+ import { SessionApi, SessionStorageAdapter } from '../foundation/session/session';
3
5
  declare type RouteParams = Record<string, string>;
4
6
  declare type RequestOptions = {
5
7
  params: RouteParams;
6
8
  queryShop: (args: QueryShopArgs) => Promise<any>;
9
+ session: SessionApi | null;
7
10
  };
8
11
  declare type ResourceGetter = (request: Request, requestOptions: RequestOptions) => Promise<Response | Object | String>;
9
12
  interface HydrogenApiRoute {
@@ -29,8 +32,6 @@ interface QueryShopArgs {
29
32
  query: ASTNode | string;
30
33
  /** An object of the variables for the GraphQL query. */
31
34
  variables?: Record<string, any>;
32
- /** A string corresponding to a valid locale identifier like `en-us` used to make the request. */
33
- locale?: string;
34
35
  }
35
- export declare function renderApiRoute(request: Request, route: ApiRouteMatch, shopifyConfig: ShopifyConfig): Promise<Response | Request>;
36
+ export declare function renderApiRoute(request: ServerComponentRequest, route: ApiRouteMatch, shopifyConfig: ShopifyConfig, session?: SessionStorageAdapter): Promise<Response | Request>;
36
37
  export {};
@@ -1,6 +1,8 @@
1
1
  import { matchPath } from './matchPath';
2
2
  import { getLoggerWithContext, logServerResponse } from '../utilities/log/';
3
3
  import { fetchBuilder, graphqlRequestBody } from './fetch';
4
+ import { getStorefrontApiRequestHeaders } from './storefrontApi';
5
+ import { emptySessionImplementation, } from '../foundation/session/session';
4
6
  let memoizedRoutes = [];
5
7
  let memoizedPages = {};
6
8
  export function getApiRoutes(pages, topLevelPath = '*') {
@@ -64,30 +66,49 @@ export function getApiRouteFromURL(url, routes) {
64
66
  hasServerComponent: foundRoute.hasServerComponent,
65
67
  };
66
68
  }
67
- function queryShopBuilder(shopifyConfig) {
68
- return async function queryShop({ query, variables, locale, }) {
69
- var _a;
70
- const { storeDomain, storefrontApiVersion, storefrontToken, defaultLocale } = shopifyConfig;
69
+ function queryShopBuilder(shopifyConfig, request) {
70
+ return async function queryShop({ query, variables, }) {
71
+ const { storeDomain, storefrontApiVersion, storefrontToken } = shopifyConfig;
72
+ const buyerIp = request.getBuyerIp();
73
+ const extraHeaders = getStorefrontApiRequestHeaders({
74
+ buyerIp,
75
+ storefrontToken,
76
+ });
71
77
  const fetcher = fetchBuilder(`https://${storeDomain}/api/${storefrontApiVersion}/graphql.json`, {
72
78
  method: 'POST',
73
79
  body: graphqlRequestBody(query, variables),
74
80
  headers: {
75
- 'X-Shopify-Storefront-Access-Token': storefrontToken,
76
- 'Accept-Language': (_a = locale) !== null && _a !== void 0 ? _a : defaultLocale,
77
81
  'Content-Type': 'application/json',
82
+ ...extraHeaders,
78
83
  },
79
84
  });
80
85
  return await fetcher();
81
86
  };
82
87
  }
83
- export async function renderApiRoute(request, route, shopifyConfig) {
88
+ export async function renderApiRoute(request, route, shopifyConfig, session) {
84
89
  var _a;
85
90
  let response;
86
91
  const log = getLoggerWithContext(request);
92
+ let cookieToSet = '';
87
93
  try {
88
94
  response = await route.resource(request, {
89
95
  params: route.params,
90
- queryShop: queryShopBuilder(shopifyConfig),
96
+ queryShop: queryShopBuilder(shopifyConfig, request),
97
+ session: session
98
+ ? {
99
+ async get() {
100
+ return session.get(request);
101
+ },
102
+ async set(key, value) {
103
+ const data = await session.get(request);
104
+ data[key] = value;
105
+ cookieToSet = await session.set(request, data);
106
+ },
107
+ async destroy() {
108
+ cookieToSet = await session.destroy(request);
109
+ },
110
+ }
111
+ : emptySessionImplementation(log),
91
112
  });
92
113
  if (!(response instanceof Response || response instanceof Request)) {
93
114
  if (typeof response === 'string' || response instanceof String) {
@@ -101,6 +122,12 @@ export async function renderApiRoute(request, route, shopifyConfig) {
101
122
  });
102
123
  }
103
124
  }
125
+ if (!response) {
126
+ response = new Response(null);
127
+ }
128
+ if (cookieToSet) {
129
+ response.headers.set('Set-Cookie', cookieToSet);
130
+ }
104
131
  }
105
132
  catch (e) {
106
133
  log.error(e);
@@ -1,6 +1,6 @@
1
1
  import type { GraphQLConnection } from '../../types';
2
2
  import type { PartialDeep } from 'type-fest';
3
3
  /**
4
- * The `flattenConnection` utility transforms a connection object from the Storefront API (for example, [Product-related connections](/api/storefront/reference/products/product)) into a flat array of nodes.
4
+ * The `flattenConnection` utility transforms a connection object from the Storefront API (for example, [Product-related connections](https://shopify.dev/api/storefront/reference/products/product)) into a flat array of nodes.
5
5
  */
6
6
  export declare function flattenConnection<T>(connection: PartialDeep<GraphQLConnection<T>>): PartialDeep<T>[];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The `flattenConnection` utility transforms a connection object from the Storefront API (for example, [Product-related connections](/api/storefront/reference/products/product)) into a flat array of nodes.
2
+ * The `flattenConnection` utility transforms a connection object from the Storefront API (for example, [Product-related connections](https://shopify.dev/api/storefront/reference/products/product)) into a flat array of nodes.
3
3
  */
4
4
  export function flattenConnection(connection) {
5
5
  return (connection.edges || []).map((edge) => {
@@ -1,6 +1,6 @@
1
1
  import type { Metafield } from '../../storefront-api-types';
2
2
  import type { PartialDeep } from 'type-fest';
3
3
  /**
4
- * The `parseMetafieldValue` function parses a [Metafield](/api/storefront/reference/common-objects/metafield)'s `value` from a string into a sensible type corresponding to the [Metafield](/api/storefront/reference/common-objects/metafield)'s `type`.
4
+ * The `parseMetafieldValue` function parses a [Metafield](https://shopify.dev/api/storefront/reference/common-objects/metafield)'s `value` from a string into a sensible type corresponding to the [Metafield](https://shopify.dev/api/storefront/reference/common-objects/metafield)'s `type`.
5
5
  */
6
6
  export declare function parseMetafieldValue(metafield: PartialDeep<Metafield>): any;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The `parseMetafieldValue` function parses a [Metafield](/api/storefront/reference/common-objects/metafield)'s `value` from a string into a sensible type corresponding to the [Metafield](/api/storefront/reference/common-objects/metafield)'s `type`.
2
+ * The `parseMetafieldValue` function parses a [Metafield](https://shopify.dev/api/storefront/reference/common-objects/metafield)'s `value` from a string into a sensible type corresponding to the [Metafield](https://shopify.dev/api/storefront/reference/common-objects/metafield)'s `type`.
3
3
  */
4
4
  export function parseMetafieldValue(metafield) {
5
5
  if (metafield.value == null) {
@@ -0,0 +1,4 @@
1
+ export declare function getStorefrontApiRequestHeaders({ buyerIp, storefrontToken, }: {
2
+ buyerIp?: string | null;
3
+ storefrontToken: string;
4
+ }): Record<string, any>;
@@ -0,0 +1,21 @@
1
+ import { OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE, STOREFRONT_API_SECRET_TOKEN_HEADER, STOREFRONT_API_PUBLIC_TOKEN_HEADER, STOREFRONT_API_BUYER_IP_HEADER, } from '../constants';
2
+ export function getStorefrontApiRequestHeaders({ buyerIp, storefrontToken, }) {
3
+ var _a;
4
+ const headers = {};
5
+ const secretToken = typeof Oxygen !== 'undefined'
6
+ ? (_a = Oxygen === null || Oxygen === void 0 ? void 0 : Oxygen.env) === null || _a === void 0 ? void 0 : _a[OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE]
7
+ : null;
8
+ /**
9
+ * Only pass one type of storefront token at a time.
10
+ */
11
+ if (secretToken) {
12
+ headers[STOREFRONT_API_SECRET_TOKEN_HEADER] = secretToken;
13
+ }
14
+ else {
15
+ headers[STOREFRONT_API_PUBLIC_TOKEN_HEADER] = storefrontToken;
16
+ }
17
+ if (buyerIp) {
18
+ headers[STOREFRONT_API_BUYER_IP_HEADER] = buyerIp;
19
+ }
20
+ return headers;
21
+ }
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "0.15.0";
1
+ export declare const LIB_VERSION = "0.17.0";
@@ -1 +1 @@
1
- export const LIB_VERSION = '0.15.0';
1
+ export const LIB_VERSION = '0.17.0';
@@ -1,3 +1,7 @@
1
1
  export declare const RSC_PATHNAME = "/__rsc";
2
2
  export declare const EVENT_PATHNAME = "/__event";
3
3
  export declare const EVENT_PATHNAME_REGEX: RegExp;
4
+ export declare const OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE = "SHOPIFY_STOREFRONT_API_SECRET_TOKEN";
5
+ export declare const STOREFRONT_API_SECRET_TOKEN_HEADER = "Shopify-Storefront-Private-Token";
6
+ export declare const STOREFRONT_API_PUBLIC_TOKEN_HEADER = "X-Shopify-Storefront-Access-Token";
7
+ export declare const STOREFRONT_API_BUYER_IP_HEADER = "Shopify-Storefront-Buyer-IP";
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EVENT_PATHNAME_REGEX = exports.EVENT_PATHNAME = exports.RSC_PATHNAME = void 0;
3
+ exports.STOREFRONT_API_BUYER_IP_HEADER = exports.STOREFRONT_API_PUBLIC_TOKEN_HEADER = exports.STOREFRONT_API_SECRET_TOKEN_HEADER = exports.OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE = exports.EVENT_PATHNAME_REGEX = exports.EVENT_PATHNAME = exports.RSC_PATHNAME = void 0;
4
4
  exports.RSC_PATHNAME = '/__rsc';
5
5
  exports.EVENT_PATHNAME = '/__event';
6
6
  exports.EVENT_PATHNAME_REGEX = new RegExp(`^${exports.EVENT_PATHNAME}\/`);
7
+ exports.OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE = 'SHOPIFY_STOREFRONT_API_SECRET_TOKEN';
8
+ exports.STOREFRONT_API_SECRET_TOKEN_HEADER = 'Shopify-Storefront-Private-Token';
9
+ exports.STOREFRONT_API_PUBLIC_TOKEN_HEADER = 'X-Shopify-Storefront-Access-Token';
10
+ exports.STOREFRONT_API_BUYER_IP_HEADER = 'Shopify-Storefront-Buyer-IP';
@@ -19,5 +19,5 @@ interface RequestHandlerOptions {
19
19
  export interface RequestHandler {
20
20
  (request: Request | IncomingMessage, options: RequestHandlerOptions): Promise<Response | undefined>;
21
21
  }
22
- export declare const renderHydrogen: (App: any, { shopifyConfig, routes, serverAnalyticsConnectors }: ServerHandlerConfig) => RequestHandler;
22
+ export declare const renderHydrogen: (App: any, { shopifyConfig, routes, serverAnalyticsConnectors, session, }: ServerHandlerConfig) => RequestHandler;
23
23
  export default renderHydrogen;
@@ -33,7 +33,7 @@ const ServerComponentResponse_server_1 = require("./framework/Hydration/ServerCo
33
33
  const ServerComponentRequest_server_1 = require("./framework/Hydration/ServerComponentRequest.server");
34
34
  const ServerRequestProvider_1 = require("./foundation/ServerRequestProvider");
35
35
  const apiRoutes_1 = require("./utilities/apiRoutes");
36
- const ServerStateProvider_1 = require("./foundation/ServerStateProvider");
36
+ const ServerPropsProvider_1 = require("./foundation/ServerPropsProvider");
37
37
  const bot_ua_1 = require("./utilities/bot-ua");
38
38
  const runtime_1 = require("./framework/runtime");
39
39
  const config_1 = require("./framework/config");
@@ -42,17 +42,20 @@ const constants_1 = require("./constants");
42
42
  const template_1 = require("./utilities/template");
43
43
  const Analytics_server_1 = require("./foundation/Analytics/Analytics.server");
44
44
  const ServerAnalyticsRoute_server_1 = require("./foundation/Analytics/ServerAnalyticsRoute.server");
45
+ const session_1 = require("./foundation/session/session");
45
46
  const DOCTYPE = '<!DOCTYPE html>';
46
47
  const CONTENT_TYPE = 'Content-Type';
47
48
  const HTML_CONTENT_TYPE = 'text/html; charset=UTF-8';
48
- const renderHydrogen = (App, { shopifyConfig, routes, serverAnalyticsConnectors }) => {
49
+ const renderHydrogen = (App, { shopifyConfig, routes, serverAnalyticsConnectors, session, }) => {
49
50
  const handleRequest = async function (rawRequest, options) {
50
51
  const { indexTemplate, streamableResponse, dev, cache, context, nonce, buyerIpHeader, } = options;
51
52
  const request = new ServerComponentRequest_server_1.ServerComponentRequest(rawRequest);
52
53
  request.ctx.buyerIpHeader = buyerIpHeader;
53
54
  const url = new URL(request.url);
54
55
  const log = (0, log_1.getLoggerWithContext)(request);
56
+ const sessionApi = session ? session(log) : undefined;
55
57
  const componentResponse = new ServerComponentResponse_server_1.ServerComponentResponse();
58
+ request.ctx.session = (0, session_1.getSyncSessionApi)(request, componentResponse, log, sessionApi);
56
59
  /**
57
60
  * Inject the cache & context into the module loader so we can pull it out for subrequests.
58
61
  */
@@ -70,7 +73,7 @@ const renderHydrogen = (App, { shopifyConfig, routes, serverAnalyticsConnectors
70
73
  // If it does, only render the API route if the request method is GET
71
74
  if (apiRoute &&
72
75
  (!apiRoute.hasServerComponent || request.method !== 'GET')) {
73
- const apiResponse = await (0, apiRoutes_1.renderApiRoute)(request, apiRoute, shopifyConfig);
76
+ const apiResponse = await (0, apiRoutes_1.renderApiRoute)(request, apiRoute, shopifyConfig, sessionApi);
74
77
  return apiResponse instanceof Request
75
78
  ? handleRequest(apiResponse, options)
76
79
  : apiResponse;
@@ -158,7 +161,11 @@ async function render(url, { App, routes, request, componentResponse, log, templ
158
161
  headers[CONTENT_TYPE] = HTML_CONTENT_TYPE;
159
162
  html = (0, Html_1.applyHtmlHead)(html, request.ctx.head, template);
160
163
  if (flight) {
161
- html = html.replace('</body>', `${flightContainer({ init: true, nonce, chunk: flight })}</body>`);
164
+ html = html.replace('</body>', () => `${flightContainer({
165
+ init: true,
166
+ nonce,
167
+ chunk: flight,
168
+ })}</body>`);
162
169
  }
163
170
  postRequestTasks('ssr', status, request, componentResponse);
164
171
  return new Response(html, {
@@ -437,7 +444,7 @@ function buildAppSSR({ App, state, request, response, log, routes }, htmlOptions
437
444
  const RscConsumer = () => rscResponse.readRoot();
438
445
  const AppSSR = (react_1.default.createElement(Html_1.Html, { ...htmlOptions },
439
446
  react_1.default.createElement(ServerRequestProvider_1.ServerRequestProvider, { request: request, isRSC: false },
440
- react_1.default.createElement(ServerStateProvider_1.ServerStateProvider, { serverState: state, setServerState: () => { } },
447
+ react_1.default.createElement(ServerPropsProvider_1.ServerPropsProvider, { initialServerProps: state, setServerPropsForRsc: () => { } },
441
448
  react_1.default.createElement(PreloadQueries, { request: request },
442
449
  react_1.default.createElement(react_1.Suspense, { fallback: null },
443
450
  react_1.default.createElement(RscConsumer, null)),
@@ -1,13 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
29
  exports.Analytics = void 0;
7
- const react_1 = __importDefault(require("react"));
30
+ const React = __importStar(require("react"));
8
31
  const hook_1 = require("./hook");
9
32
  const Analytics_client_1 = require("./Analytics.client");
10
33
  const ServerRequestProvider_1 = require("../ServerRequestProvider");
34
+ const AnalyticsErrorBoundary_client_1 = __importDefault(require("../AnalyticsErrorBoundary.client"));
11
35
  const DELAY_KEY = 'analytics-delay';
12
36
  function Analytics() {
13
37
  const cache = (0, ServerRequestProvider_1.useServerRequest)().ctx.cache;
@@ -40,6 +64,7 @@ function Analytics() {
40
64
  }
41
65
  });
42
66
  const analyticsData = (0, hook_1.useServerAnalytics)();
43
- return react_1.default.createElement(Analytics_client_1.Analytics, { analyticsDataFromServer: analyticsData });
67
+ return (React.createElement(AnalyticsErrorBoundary_client_1.default, null,
68
+ React.createElement(Analytics_client_1.Analytics, { analyticsDataFromServer: analyticsData })));
44
69
  }
45
70
  exports.Analytics = Analytics;
@@ -0,0 +1,4 @@
1
+ import type { ReactNode } from 'react';
2
+ export default function AnalyticsErrorBoundary({ children, }: {
3
+ children: ReactNode;
4
+ }): JSX.Element;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const react_error_boundary_1 = require("react-error-boundary");
8
+ function AnalyticsErrorBoundary({ children, }) {
9
+ // Analytics fail to load, most likely due to an ad blocker
10
+ return (react_1.default.createElement(react_error_boundary_1.ErrorBoundary, { fallbackRender: () => {
11
+ return null;
12
+ } }, children));
13
+ }
14
+ exports.default = AnalyticsErrorBoundary;
@@ -27,7 +27,7 @@ exports.useLocation = exports.useRouter = exports.BrowserRouter = exports.Router
27
27
  const history_1 = require("history");
28
28
  const react_1 = __importStar(require("react"));
29
29
  const ssr_interop_1 = require("../ssr-interop");
30
- const useServerState_1 = require("../useServerState");
30
+ const use_server_props_1 = require("../useServerProps/use-server-props");
31
31
  exports.RouterContext = (0, react_1.createContext)({});
32
32
  let isFirstLoad = true;
33
33
  const positions = {};
@@ -36,12 +36,12 @@ const BrowserRouter = ({ history: pHistory, children, }) => {
36
36
  return react_1.default.createElement(react_1.default.Fragment, null, children);
37
37
  const history = (0, react_1.useMemo)(() => pHistory || (0, history_1.createBrowserHistory)(), [pHistory]);
38
38
  const [location, setLocation] = (0, react_1.useState)(history.location);
39
- const { pending, serverState, setServerState } = (0, useServerState_1.useServerState)();
40
- useScrollRestoration(location, pending, serverState);
39
+ const { pending, locationServerProps, setLocationServerProps } = (0, use_server_props_1.useInternalServerProps)();
40
+ useScrollRestoration(location, pending, locationServerProps);
41
41
  (0, react_1.useLayoutEffect)(() => {
42
42
  const unlisten = history.listen(({ location: newLocation }) => {
43
43
  positions[location.key] = window.scrollY;
44
- setServerState({
44
+ setLocationServerProps({
45
45
  pathname: newLocation.pathname,
46
46
  search: location.search,
47
47
  });
@@ -78,7 +78,7 @@ function useBeforeUnload(callback) {
78
78
  };
79
79
  }, [callback]);
80
80
  }
81
- function useScrollRestoration(location, pending, serverState) {
81
+ function useScrollRestoration(location, pending, serverProps) {
82
82
  /**
83
83
  * Browsers have an API for scroll restoration. We wait for the page to load first,
84
84
  * in case the browser is able to restore scroll position automatically, and then
@@ -106,8 +106,8 @@ function useScrollRestoration(location, pending, serverState) {
106
106
  * location pointer and serverState match, and pending is false, to do any scrolling.
107
107
  */
108
108
  const finishedNavigating = !pending &&
109
- location.pathname === serverState.pathname &&
110
- location.search === serverState.search;
109
+ location.pathname === serverProps.pathname &&
110
+ location.search === serverProps.search;
111
111
  if (!finishedNavigating) {
112
112
  return;
113
113
  }
@@ -126,5 +126,5 @@ function useScrollRestoration(location, pending, serverState) {
126
126
  }
127
127
  // Scroll to the top of new pages
128
128
  window.scrollTo(0, 0);
129
- }, [location, pending, serverState]);
129
+ }, [location, pending, serverProps]);
130
130
  }
@@ -0,0 +1,40 @@
1
+ import React, { ReactNode } from 'react';
2
+ declare global {
3
+ var __DEV__: boolean;
4
+ }
5
+ export interface LocationServerProps {
6
+ pathname: string;
7
+ search: string;
8
+ }
9
+ export interface ServerProps {
10
+ [key: string]: any;
11
+ }
12
+ declare type ServerPropsSetterInput = ((prev: ServerProps) => Partial<ServerProps>) | Partial<ServerProps> | string;
13
+ export interface ServerPropsSetter {
14
+ (input: ServerPropsSetterInput, propValue?: any): void;
15
+ }
16
+ interface ProposedServerPropsSetter {
17
+ (input: ServerPropsSetterInput, propValue?: any): LocationServerProps;
18
+ }
19
+ interface BaseServerPropsContextValue {
20
+ pending: boolean;
21
+ }
22
+ export interface InternalServerPropsContextValue extends BaseServerPropsContextValue {
23
+ setLocationServerProps: ServerPropsSetter;
24
+ setServerProps: ServerPropsSetter;
25
+ serverProps: ServerProps;
26
+ locationServerProps: LocationServerProps;
27
+ getProposedLocationServerProps: ProposedServerPropsSetter;
28
+ }
29
+ export interface ServerPropsContextValue extends BaseServerPropsContextValue {
30
+ serverProps: ServerProps;
31
+ setServerProps: ServerPropsSetter;
32
+ }
33
+ export declare const ServerPropsContext: React.Context<InternalServerPropsContextValue>;
34
+ interface ServerPropsProviderProps {
35
+ initialServerProps: LocationServerProps;
36
+ setServerPropsForRsc: React.Dispatch<React.SetStateAction<LocationServerProps>>;
37
+ children: ReactNode;
38
+ }
39
+ export declare function ServerPropsProvider({ initialServerProps, setServerPropsForRsc, children, }: ServerPropsProviderProps): JSX.Element;
40
+ export {};
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.ServerPropsProvider = exports.ServerPropsContext = void 0;
27
+ const react_1 = __importStar(require("react"));
28
+ const PRIVATE_PROPS = ['request', 'response'];
29
+ exports.ServerPropsContext = (0, react_1.createContext)(null);
30
+ function ServerPropsProvider({ initialServerProps, setServerPropsForRsc, children, }) {
31
+ const [locationServerProps, setLocationServerProps] = (0, react_1.useState)(initialServerProps);
32
+ const [serverProps, setServerProps] = (0, react_1.useState)({});
33
+ const [pending, startTransition] = (0, react_1.useTransition)();
34
+ const setServerPropsCallback = (0, react_1.useCallback)((input, propValue) => {
35
+ setServerProps((prev) => getNewValue(prev, input, propValue));
36
+ }, []);
37
+ const setLocationServerPropsCallback = (0, react_1.useCallback)((input, propValue) => {
38
+ // Flush the existing user server state when location changes, leaving only the persisted state
39
+ setServerProps({});
40
+ setLocationServerProps((prev) => getNewValue(prev, input, propValue));
41
+ }, []);
42
+ const getProposedLocationServerPropsCallback = (0, react_1.useCallback)((input, propValue) => {
43
+ return getNewValue(locationServerProps, input, propValue);
44
+ }, [locationServerProps]);
45
+ function getNewValue(prev, input, propValue) {
46
+ let newValue;
47
+ if (typeof input === 'function') {
48
+ newValue = input(prev);
49
+ }
50
+ else if (typeof input === 'string') {
51
+ newValue = { [input]: propValue };
52
+ }
53
+ else {
54
+ newValue = input;
55
+ }
56
+ if (__DEV__) {
57
+ const privateProp = PRIVATE_PROPS.find((prop) => prop in newValue);
58
+ if (privateProp) {
59
+ console.warn(`Custom "${privateProp}" property in server state is ignored. Use a different name.`);
60
+ }
61
+ }
62
+ return {
63
+ ...prev,
64
+ ...newValue,
65
+ };
66
+ }
67
+ const resolvedServerPropsForRsc = (0, react_1.useMemo)(() => {
68
+ return {
69
+ ...serverProps,
70
+ ...locationServerProps,
71
+ };
72
+ }, [serverProps, locationServerProps]);
73
+ const resolvedServerProps = (0, react_1.useMemo)(() => {
74
+ return {
75
+ ...serverProps,
76
+ };
77
+ }, [serverProps]);
78
+ (0, react_1.useEffect)(() => {
79
+ startTransition(() => {
80
+ setServerPropsForRsc(resolvedServerPropsForRsc);
81
+ });
82
+ return () => { };
83
+ }, [resolvedServerPropsForRsc]);
84
+ const value = (0, react_1.useMemo)(() => ({
85
+ pending,
86
+ locationServerProps: locationServerProps,
87
+ serverProps: resolvedServerProps,
88
+ setServerProps: setServerPropsCallback,
89
+ setLocationServerProps: setLocationServerPropsCallback,
90
+ getProposedLocationServerProps: getProposedLocationServerPropsCallback,
91
+ }), [
92
+ pending,
93
+ locationServerProps,
94
+ resolvedServerProps,
95
+ setServerPropsCallback,
96
+ setLocationServerPropsCallback,
97
+ getProposedLocationServerPropsCallback,
98
+ ]);
99
+ return (react_1.default.createElement(exports.ServerPropsContext.Provider, { value: value }, children));
100
+ }
101
+ exports.ServerPropsProvider = ServerPropsProvider;
@@ -0,0 +1,2 @@
1
+ export { ServerPropsProvider, ServerPropsContext } from './ServerPropsProvider';
2
+ export type { ServerProps, ServerPropsContextValue } from './ServerPropsProvider';
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServerPropsContext = exports.ServerPropsProvider = void 0;
4
+ var ServerPropsProvider_1 = require("./ServerPropsProvider");
5
+ Object.defineProperty(exports, "ServerPropsProvider", { enumerable: true, get: function () { return ServerPropsProvider_1.ServerPropsProvider; } });
6
+ Object.defineProperty(exports, "ServerPropsContext", { enumerable: true, get: function () { return ServerPropsProvider_1.ServerPropsContext; } });
@@ -0,0 +1,27 @@
1
+ import { Logger } from '../../utilities/log';
2
+ import type { ServerComponentResponse } from '../../framework/Hydration/ServerComponentResponse.server';
3
+ import type { ServerComponentRequest } from '../../framework/Hydration/ServerComponentRequest.server';
4
+ export declare type SessionSyncApi = {
5
+ get: () => Record<string, string>;
6
+ };
7
+ export declare type SessionApi = {
8
+ get: () => Promise<Record<string, string>>;
9
+ set: (key: string, value: string) => Promise<void>;
10
+ destroy: () => Promise<void>;
11
+ };
12
+ export declare type SessionStorageAdapter = {
13
+ get: (request: Request) => Promise<Record<string, string>>;
14
+ set: (request: Request, value: Record<string, string>) => Promise<string>;
15
+ destroy: (request: Request) => Promise<string>;
16
+ };
17
+ export declare function getSyncSessionApi(request: ServerComponentRequest, componentResponse: ServerComponentResponse, log: Logger, session?: SessionStorageAdapter): {
18
+ get(): any;
19
+ };
20
+ export declare const emptySessionImplementation: (log: Logger) => {
21
+ get(): Promise<{}>;
22
+ set(key: string, value: string): Promise<void>;
23
+ destroy(): Promise<void>;
24
+ };
25
+ export declare const emptySyncSessionImplementation: (log: Logger) => {
26
+ get(): {};
27
+ };