@shopify/hydrogen 0.11.0-experimental.1 → 0.11.1-experimental.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 (264) hide show
  1. package/CHANGELOG.md +504 -0
  2. package/dist/esnext/client.d.ts +1 -1
  3. package/dist/esnext/client.js +1 -1
  4. package/dist/esnext/components/AddToCartButton/AddToCartButton.client.d.ts +1 -1
  5. package/dist/esnext/components/AddToCartButton/AddToCartButton.client.js +11 -2
  6. package/dist/esnext/components/CartEstimatedCost/CartEstimatedCost.client.js +1 -1
  7. package/dist/esnext/components/CartLineImage/CartLineImage.client.js +2 -2
  8. package/dist/esnext/components/CartLinePrice/CartLinePrice.client.d.ts +1 -1
  9. package/dist/esnext/components/CartLinePrice/CartLinePrice.client.js +1 -1
  10. package/dist/esnext/components/CartProvider/CartProvider.client.d.ts +2 -2
  11. package/dist/esnext/components/CartProvider/CartProvider.client.js +1 -1
  12. package/dist/esnext/components/ExternalVideo/ExternalVideo.d.ts +1 -1
  13. package/dist/esnext/components/ExternalVideo/ExternalVideo.js +2 -2
  14. package/dist/esnext/components/Image/Image.d.ts +1 -1
  15. package/dist/esnext/components/Image/Image.js +9 -9
  16. package/dist/esnext/components/LocalizationProvider/LocalizationProvider.server.d.ts +13 -4
  17. package/dist/esnext/components/LocalizationProvider/LocalizationProvider.server.js +5 -3
  18. package/dist/esnext/components/MediaFile/MediaFile.d.ts +8 -6
  19. package/dist/esnext/components/MediaFile/MediaFile.js +6 -6
  20. package/dist/esnext/components/Metafield/Metafield.client.d.ts +1 -1
  21. package/dist/esnext/components/Metafield/Metafield.client.js +16 -16
  22. package/dist/esnext/components/ModelViewer/ModelViewer.client.d.ts +1 -1
  23. package/dist/esnext/components/ModelViewer/ModelViewer.client.js +1 -1
  24. package/dist/esnext/components/Money/Money.client.d.ts +1 -1
  25. package/dist/esnext/components/Money/Money.client.js +2 -2
  26. package/dist/esnext/components/ProductMetafield/ProductMetafield.client.d.ts +3 -1
  27. package/dist/esnext/components/ProductMetafield/ProductMetafield.client.js +21 -4
  28. package/dist/esnext/components/ProductPrice/ProductPrice.client.d.ts +5 -3
  29. package/dist/esnext/components/ProductPrice/ProductPrice.client.js +30 -9
  30. package/dist/esnext/components/ProductProvider/ProductOptionsProvider.client.d.ts +8 -0
  31. package/dist/esnext/components/ProductProvider/ProductOptionsProvider.client.js +12 -0
  32. package/dist/esnext/components/ProductProvider/ProductProvider.client.d.ts +8 -7
  33. package/dist/esnext/components/ProductProvider/ProductProvider.client.js +9 -35
  34. package/dist/esnext/components/ProductProvider/context.d.ts +10 -6
  35. package/dist/esnext/components/ProductProvider/context.js +1 -0
  36. package/dist/esnext/components/Seo/DefaultPageSeo.client.js +2 -2
  37. package/dist/esnext/components/Seo/DescriptionSeo.client.js +2 -2
  38. package/dist/esnext/components/Seo/HomePageSeo.client.js +2 -2
  39. package/dist/esnext/components/Seo/ImageSeo.client.js +2 -2
  40. package/dist/esnext/components/Seo/ProductSeo.client.js +5 -3
  41. package/dist/esnext/components/Seo/TitleSeo.client.js +2 -2
  42. package/dist/esnext/components/Seo/TwitterSeo.client.js +2 -2
  43. package/dist/esnext/components/Seo/types.d.ts +1 -1
  44. package/dist/esnext/components/UnitPrice/UnitPrice.client.d.ts +2 -2
  45. package/dist/esnext/components/UnitPrice/UnitPrice.client.js +3 -3
  46. package/dist/esnext/components/Video/Video.d.ts +1 -1
  47. package/dist/esnext/components/Video/Video.js +3 -3
  48. package/dist/esnext/components/index.d.ts +0 -8
  49. package/dist/esnext/components/index.js +0 -8
  50. package/dist/esnext/entry-client.js +1 -1
  51. package/dist/esnext/entry-server.d.ts +18 -2
  52. package/dist/esnext/entry-server.js +347 -329
  53. package/dist/esnext/foundation/Head/Head.client.d.ts +5 -0
  54. package/dist/esnext/foundation/Head/Head.client.js +10 -0
  55. package/dist/esnext/foundation/Head/index.d.ts +1 -0
  56. package/dist/esnext/foundation/Head/index.js +1 -0
  57. package/dist/esnext/foundation/ServerRequestProvider/ServerRequestProvider.d.ts +2 -1
  58. package/dist/esnext/foundation/ServerRequestProvider/ServerRequestProvider.js +43 -4
  59. package/dist/esnext/foundation/useQuery/hooks.d.ts +11 -3
  60. package/dist/esnext/foundation/useQuery/hooks.js +21 -5
  61. package/dist/esnext/framework/CachingStrategy/index.d.ts +10 -0
  62. package/dist/esnext/framework/CachingStrategy/index.js +96 -0
  63. package/dist/esnext/framework/Hydration/Html.js +5 -12
  64. package/dist/esnext/framework/Hydration/ServerComponentRequest.server.d.ts +20 -2
  65. package/dist/esnext/framework/Hydration/ServerComponentRequest.server.js +51 -2
  66. package/dist/esnext/framework/Hydration/ServerComponentResponse.server.d.ts +2 -2
  67. package/dist/esnext/framework/Hydration/ServerComponentResponse.server.js +2 -11
  68. package/dist/esnext/framework/cache/in-memory.js +6 -5
  69. package/dist/esnext/framework/cache.d.ts +3 -3
  70. package/dist/esnext/framework/cache.js +4 -18
  71. package/dist/esnext/framework/middleware.d.ts +7 -7
  72. package/dist/esnext/framework/middleware.js +15 -43
  73. package/dist/esnext/framework/plugin.js +14 -3
  74. package/dist/esnext/framework/plugins/vite-plugin-hydration-auto-import.d.ts +3 -0
  75. package/dist/esnext/framework/plugins/vite-plugin-hydration-auto-import.js +38 -0
  76. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-client-middleware.d.ts +9 -0
  77. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-client-middleware.js +37 -0
  78. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-middleware.d.ts +2 -1
  79. package/dist/esnext/framework/plugins/vite-plugin-hydrogen-middleware.js +3 -1
  80. package/dist/esnext/framework/plugins/vite-plugin-platform-entry.d.ts +3 -0
  81. package/dist/esnext/framework/plugins/vite-plugin-platform-entry.js +70 -0
  82. package/dist/esnext/hooks/useProduct/useProduct.d.ts +150 -1
  83. package/dist/esnext/hooks/useProduct/useProduct.js +36 -3
  84. package/dist/esnext/hooks/useProductOptions/types.d.ts +1 -1
  85. package/dist/esnext/hooks/useProductOptions/useProductOptions.d.ts +1 -1
  86. package/dist/esnext/hooks/useProductOptions/useProductOptions.js +6 -6
  87. package/dist/esnext/hooks/useShopQuery/hooks.d.ts +11 -4
  88. package/dist/esnext/hooks/useShopQuery/hooks.js +6 -3
  89. package/dist/esnext/index.d.ts +2 -1
  90. package/dist/esnext/index.js +2 -1
  91. package/dist/esnext/platforms/node.d.ts +10 -0
  92. package/dist/esnext/platforms/node.js +38 -0
  93. package/dist/esnext/platforms/worker-event.d.ts +9 -0
  94. package/dist/esnext/platforms/worker-event.js +24 -0
  95. package/dist/esnext/streaming.server.d.ts +17 -7
  96. package/dist/esnext/streaming.server.js +44 -0
  97. package/dist/esnext/types.d.ts +25 -25
  98. package/dist/esnext/utilities/apiRoutes.d.ts +1 -2
  99. package/dist/esnext/utilities/apiRoutes.js +4 -3
  100. package/dist/esnext/utilities/bot-ua.d.ts +4 -0
  101. package/dist/esnext/utilities/bot-ua.js +54 -0
  102. package/dist/esnext/utilities/fetch.js +1 -26
  103. package/dist/esnext/utilities/log/index.d.ts +4 -1
  104. package/dist/esnext/utilities/log/index.js +4 -1
  105. package/dist/esnext/utilities/log/log-cache-api-status.d.ts +1 -0
  106. package/dist/esnext/utilities/log/log-cache-api-status.js +9 -0
  107. package/dist/esnext/utilities/log/log-cache-header.d.ts +10 -0
  108. package/dist/esnext/utilities/log/log-cache-header.js +30 -0
  109. package/dist/esnext/utilities/log/log-query-timeline.d.ts +12 -0
  110. package/dist/esnext/utilities/log/log-query-timeline.js +83 -0
  111. package/dist/esnext/utilities/log/log.d.ts +11 -8
  112. package/dist/esnext/utilities/log/log.js +24 -35
  113. package/dist/esnext/utilities/log/utils.d.ts +3 -0
  114. package/dist/esnext/utilities/log/utils.js +13 -0
  115. package/dist/esnext/utilities/template.d.ts +9 -0
  116. package/dist/esnext/utilities/template.js +23 -0
  117. package/dist/{worker/foundation/ShopifyProvider/types.js → esnext/utilities/web-api-polyfill.d.ts} +0 -0
  118. package/dist/esnext/utilities/web-api-polyfill.js +19 -0
  119. package/dist/esnext/version.d.ts +1 -1
  120. package/dist/esnext/version.js +1 -1
  121. package/dist/node/entry-server.d.ts +22 -0
  122. package/dist/node/entry-server.js +581 -0
  123. package/dist/node/foundation/ServerRequestProvider/ServerRequestProvider.d.ts +23 -0
  124. package/dist/node/foundation/ServerRequestProvider/ServerRequestProvider.js +132 -0
  125. package/dist/node/foundation/ServerRequestProvider/index.d.ts +1 -0
  126. package/dist/node/foundation/ServerRequestProvider/index.js +13 -0
  127. package/dist/node/foundation/ServerStateProvider/ServerStateProvider.client.d.ts +25 -0
  128. package/dist/node/foundation/ServerStateProvider/ServerStateProvider.client.js +68 -0
  129. package/dist/node/foundation/ServerStateProvider/index.d.ts +2 -0
  130. package/dist/node/foundation/ServerStateProvider/index.js +6 -0
  131. package/dist/node/framework/CachingStrategy/index.d.ts +10 -0
  132. package/dist/node/framework/CachingStrategy/index.js +108 -0
  133. package/dist/node/framework/Hydration/Html.js +5 -12
  134. package/dist/node/framework/Hydration/ServerComponentRequest.server.d.ts +20 -2
  135. package/dist/node/framework/Hydration/ServerComponentRequest.server.js +50 -1
  136. package/dist/node/framework/Hydration/ServerComponentResponse.server.d.ts +2 -2
  137. package/dist/node/framework/Hydration/ServerComponentResponse.server.js +2 -11
  138. package/dist/node/framework/cache/in-memory.js +6 -5
  139. package/dist/node/framework/cache.d.ts +3 -3
  140. package/dist/node/framework/cache.js +6 -20
  141. package/dist/node/framework/middleware.d.ts +7 -7
  142. package/dist/node/framework/middleware.js +14 -46
  143. package/dist/node/framework/plugin.js +33 -3
  144. package/dist/node/framework/plugins/vite-plugin-hydration-auto-import.d.ts +3 -0
  145. package/dist/node/framework/plugins/vite-plugin-hydration-auto-import.js +43 -0
  146. package/dist/node/framework/plugins/vite-plugin-hydrogen-client-middleware.d.ts +9 -0
  147. package/dist/node/framework/plugins/vite-plugin-hydrogen-client-middleware.js +39 -0
  148. package/dist/node/framework/plugins/vite-plugin-hydrogen-middleware.d.ts +2 -1
  149. package/dist/node/framework/plugins/vite-plugin-hydrogen-middleware.js +4 -1
  150. package/dist/node/framework/plugins/vite-plugin-platform-entry.d.ts +3 -0
  151. package/dist/node/framework/plugins/vite-plugin-platform-entry.js +75 -0
  152. package/dist/node/streaming.server.d.ts +21 -0
  153. package/dist/node/streaming.server.js +59 -0
  154. package/dist/node/types.d.ts +25 -25
  155. package/dist/node/utilities/apiRoutes.d.ts +1 -2
  156. package/dist/node/utilities/apiRoutes.js +4 -3
  157. package/dist/node/utilities/bot-ua.d.ts +4 -0
  158. package/dist/node/utilities/bot-ua.js +58 -0
  159. package/dist/node/utilities/defer.d.ts +6 -0
  160. package/dist/node/utilities/defer.js +18 -0
  161. package/dist/node/utilities/error.d.ts +1 -0
  162. package/dist/node/utilities/error.js +10 -0
  163. package/dist/node/utilities/log/index.d.ts +4 -0
  164. package/dist/node/utilities/log/index.js +18 -0
  165. package/dist/node/utilities/log/log-cache-api-status.d.ts +1 -0
  166. package/dist/node/utilities/log/log-cache-api-status.js +13 -0
  167. package/dist/node/utilities/log/log-cache-header.d.ts +10 -0
  168. package/dist/node/utilities/log/log-cache-header.js +35 -0
  169. package/dist/node/utilities/log/log-query-timeline.d.ts +12 -0
  170. package/dist/node/utilities/log/log-query-timeline.js +88 -0
  171. package/dist/node/utilities/log/log.d.ts +11 -8
  172. package/dist/node/utilities/log/log.js +27 -37
  173. package/dist/node/utilities/log/utils.d.ts +3 -0
  174. package/dist/node/utilities/log/utils.js +18 -0
  175. package/dist/node/utilities/template.d.ts +9 -0
  176. package/dist/node/utilities/template.js +27 -0
  177. package/dist/{worker/types.js → node/utilities/web-api-polyfill.d.ts} +0 -0
  178. package/dist/node/utilities/web-api-polyfill.js +43 -0
  179. package/package.json +20 -10
  180. package/web-polyfills.js +1 -0
  181. package/LICENSE.md +0 -7
  182. package/dist/esnext/components/CartLineAttributes/CartLineAttributes.client.d.ts +0 -22
  183. package/dist/esnext/components/CartLineAttributes/CartLineAttributes.client.js +0 -13
  184. package/dist/esnext/components/CartLineAttributes/index.d.ts +0 -1
  185. package/dist/esnext/components/CartLineAttributes/index.js +0 -1
  186. package/dist/esnext/components/CartLineSelectedOptions/CartLineSelectedOptions.client.d.ts +0 -21
  187. package/dist/esnext/components/CartLineSelectedOptions/CartLineSelectedOptions.client.js +0 -13
  188. package/dist/esnext/components/CartLineSelectedOptions/index.d.ts +0 -1
  189. package/dist/esnext/components/CartLineSelectedOptions/index.js +0 -1
  190. package/dist/esnext/components/SelectedVariantAddToCartButton/SelectedVariantAddToCartButton.client.d.ts +0 -11
  191. package/dist/esnext/components/SelectedVariantAddToCartButton/SelectedVariantAddToCartButton.client.js +0 -17
  192. package/dist/esnext/components/SelectedVariantAddToCartButton/index.d.ts +0 -1
  193. package/dist/esnext/components/SelectedVariantAddToCartButton/index.js +0 -1
  194. package/dist/esnext/components/SelectedVariantBuyNowButton/SelectedVariantBuyNowButton.d.ts +0 -9
  195. package/dist/esnext/components/SelectedVariantBuyNowButton/SelectedVariantBuyNowButton.js +0 -17
  196. package/dist/esnext/components/SelectedVariantBuyNowButton/index.d.ts +0 -1
  197. package/dist/esnext/components/SelectedVariantBuyNowButton/index.js +0 -1
  198. package/dist/esnext/components/SelectedVariantImage/SelectedVariantImage.client.d.ts +0 -10
  199. package/dist/esnext/components/SelectedVariantImage/SelectedVariantImage.client.js +0 -16
  200. package/dist/esnext/components/SelectedVariantImage/index.d.ts +0 -1
  201. package/dist/esnext/components/SelectedVariantImage/index.js +0 -1
  202. package/dist/esnext/components/SelectedVariantMetafield/SelectedVariantMetafield.client.d.ts +0 -15
  203. package/dist/esnext/components/SelectedVariantMetafield/SelectedVariantMetafield.client.js +0 -24
  204. package/dist/esnext/components/SelectedVariantMetafield/index.d.ts +0 -1
  205. package/dist/esnext/components/SelectedVariantMetafield/index.js +0 -1
  206. package/dist/esnext/components/SelectedVariantPrice/SelectedVariantPrice.client.d.ts +0 -12
  207. package/dist/esnext/components/SelectedVariantPrice/SelectedVariantPrice.client.js +0 -22
  208. package/dist/esnext/components/SelectedVariantPrice/index.d.ts +0 -1
  209. package/dist/esnext/components/SelectedVariantPrice/index.js +0 -1
  210. package/dist/esnext/components/SelectedVariantShopPayButton/SelectedVariantShopPayButton.client.d.ts +0 -8
  211. package/dist/esnext/components/SelectedVariantShopPayButton/SelectedVariantShopPayButton.client.js +0 -18
  212. package/dist/esnext/components/SelectedVariantShopPayButton/index.d.ts +0 -1
  213. package/dist/esnext/components/SelectedVariantShopPayButton/index.js +0 -1
  214. package/dist/esnext/components/SelectedVariantUnitPrice/SelectedVariantUnitPrice.client.d.ts +0 -7
  215. package/dist/esnext/components/SelectedVariantUnitPrice/SelectedVariantUnitPrice.client.js +0 -16
  216. package/dist/esnext/components/SelectedVariantUnitPrice/index.d.ts +0 -1
  217. package/dist/esnext/components/SelectedVariantUnitPrice/index.js +0 -1
  218. package/dist/esnext/foundation/Helmet/Helmet.client.d.ts +0 -10
  219. package/dist/esnext/foundation/Helmet/Helmet.client.js +0 -10
  220. package/dist/esnext/foundation/Helmet/index.d.ts +0 -1
  221. package/dist/esnext/foundation/Helmet/index.js +0 -1
  222. package/dist/esnext/handle-event.d.ts +0 -23
  223. package/dist/esnext/handle-event.js +0 -132
  224. package/dist/esnext/handle-worker-event.d.ts +0 -11
  225. package/dist/esnext/handle-worker-event.js +0 -14
  226. package/dist/node/foundation/Helmet/Helmet.client.d.ts +0 -10
  227. package/dist/node/foundation/Helmet/Helmet.client.js +0 -17
  228. package/dist/node/foundation/Helmet/index.d.ts +0 -1
  229. package/dist/node/foundation/Helmet/index.js +0 -5
  230. package/dist/node/foundation/ssr-interop.d.ts +0 -29
  231. package/dist/node/foundation/ssr-interop.js +0 -39
  232. package/dist/node/handle-event.d.ts +0 -23
  233. package/dist/node/handle-event.js +0 -135
  234. package/dist/worker/constants.d.ts +0 -1
  235. package/dist/worker/constants.js +0 -1
  236. package/dist/worker/foundation/Helmet/Helmet.client.d.ts +0 -10
  237. package/dist/worker/foundation/Helmet/Helmet.client.js +0 -10
  238. package/dist/worker/foundation/Helmet/index.d.ts +0 -1
  239. package/dist/worker/foundation/Helmet/index.js +0 -1
  240. package/dist/worker/foundation/ShopifyProvider/types.d.ts +0 -15
  241. package/dist/worker/foundation/ssr-interop.d.ts +0 -29
  242. package/dist/worker/foundation/ssr-interop.js +0 -35
  243. package/dist/worker/framework/Hydration/ServerComponentRequest.server.d.ts +0 -23
  244. package/dist/worker/framework/Hydration/ServerComponentRequest.server.js +0 -57
  245. package/dist/worker/framework/config.d.ts +0 -6
  246. package/dist/worker/framework/config.js +0 -6
  247. package/dist/worker/framework/runtime.d.ts +0 -13
  248. package/dist/worker/framework/runtime.js +0 -27
  249. package/dist/worker/graphql/types/types.d.ts +0 -5761
  250. package/dist/worker/graphql/types/types.js +0 -1436
  251. package/dist/worker/handle-event.d.ts +0 -23
  252. package/dist/worker/handle-event.js +0 -132
  253. package/dist/worker/handle-worker-event.d.ts +0 -11
  254. package/dist/worker/handle-worker-event.js +0 -14
  255. package/dist/worker/types.d.ts +0 -96
  256. package/dist/worker/utilities/apiRoutes.d.ts +0 -21
  257. package/dist/worker/utilities/apiRoutes.js +0 -87
  258. package/dist/worker/utilities/log/log.d.ts +0 -18
  259. package/dist/worker/utilities/log/log.js +0 -77
  260. package/dist/worker/utilities/matchPath.d.ts +0 -10
  261. package/dist/worker/utilities/matchPath.js +0 -54
  262. package/dist/worker/utilities/timing.d.ts +0 -7
  263. package/dist/worker/utilities/timing.js +0 -14
  264. package/worker.js +0 -1
@@ -1,330 +1,365 @@
1
1
  import React from 'react';
2
- import { logServerResponse, getLoggerFromContext, log as noContextLogger, } from './utilities/log/log';
2
+ import { logServerResponse, logCacheControlHeaders, logQueryTimings, getLoggerWithContext, } from './utilities/log';
3
3
  import { getErrorMarkup } from './utilities/error';
4
4
  import { defer } from './utilities/defer';
5
5
  import { Html } from './framework/Hydration/Html';
6
6
  import { ServerComponentResponse } from './framework/Hydration/ServerComponentResponse.server';
7
+ import { ServerComponentRequest } from './framework/Hydration/ServerComponentRequest.server';
7
8
  import { getCacheControlHeader } from './framework/cache';
8
- import { ServerRequestProvider } from './foundation/ServerRequestProvider';
9
- import { getApiRouteFromURL, getApiRoutesFromPages } from './utilities/apiRoutes';
9
+ import { preloadRequestCacheData, ServerRequestProvider, } from './foundation/ServerRequestProvider';
10
+ import { getApiRouteFromURL, renderApiRoute, getApiRoutesFromPages, } from './utilities/apiRoutes';
10
11
  import { ServerStateProvider } from './foundation/ServerStateProvider';
11
- import { ssrRenderToPipeableStream, ssrRenderToReadableStream, rscRenderToReadableStream, createFromReadableStream, } from './streaming.server';
12
+ import { isBotUA } from './utilities/bot-ua';
13
+ import { setContext, setCache } from './framework/runtime';
14
+ import { setConfig } from './framework/config';
15
+ import { ssrRenderToPipeableStream, ssrRenderToReadableStream, rscRenderToReadableStream, createFromReadableStream, supportsReadableStream, isStreamingSupported, bufferReadableStream, } from './streaming.server';
12
16
  import { RSC_PATHNAME } from './constants';
13
- /**
14
- * If a query is taking too long, or something else went wrong,
15
- * send back a response containing the Suspense fallback and rely
16
- * on the client to hydrate and build the React tree.
17
- */
18
- const STREAM_ABORT_TIMEOUT_MS = 3000;
17
+ import { stripScriptsFromTemplate } from './utilities/template';
19
18
  const HTML_CONTENT_TYPE = 'text/html; charset=UTF-8';
20
- const renderHydrogen = (App, { pages }) => {
21
- /**
22
- * The render function is responsible for turning the provided `App` into an HTML string,
23
- * and returning any initial state that needs to be hydrated into the client version of the app.
24
- * NOTE: This is currently only used for SEO bots or Worker runtime (where Stream is not yet supported).
25
- */
26
- const render = async function (url, { request, template, nonce, dev }) {
27
- const { log, state, componentResponse } = setupCurrentRequest(url, request);
28
- const { AppSSR, rscReadable } = buildAppSSR({
19
+ export const renderHydrogen = (App, { pages }) => {
20
+ const handleRequest = async function (rawRequest, { indexTemplate, streamableResponse, dev, cache, context, nonce }) {
21
+ const request = new ServerComponentRequest(rawRequest);
22
+ const url = new URL(request.url);
23
+ const log = getLoggerWithContext(request);
24
+ const componentResponse = new ServerComponentResponse();
25
+ /**
26
+ * Inject the cache & context into the module loader so we can pull it out for subrequests.
27
+ */
28
+ setCache(cache);
29
+ setContext(context);
30
+ setConfig({ dev });
31
+ const isReactHydrationRequest = url.pathname === RSC_PATHNAME;
32
+ let template = typeof indexTemplate === 'function'
33
+ ? await indexTemplate(url.toString())
34
+ : indexTemplate;
35
+ if (template && typeof template !== 'string') {
36
+ template = template.default;
37
+ }
38
+ if (!isReactHydrationRequest && pages) {
39
+ const apiRoute = getApiRoute(url, { pages });
40
+ // The API Route might have a default export, making it also a server component
41
+ // If it does, only render the API route if the request method is GET
42
+ if (apiRoute &&
43
+ (!apiRoute.hasServerComponent || request.method !== 'GET')) {
44
+ return renderApiRoute(request, apiRoute);
45
+ }
46
+ }
47
+ const isStreamable = !isBotUA(url, request.headers.get('user-agent')) &&
48
+ (!!streamableResponse || supportsReadableStream());
49
+ const params = {
29
50
  App,
30
- state,
31
- request,
32
- response: componentResponse,
33
- pages,
34
51
  log,
35
- }, { template });
36
- let [html, flight] = await Promise.all([
37
- renderToBufferedString(AppSSR, { log, nonce }),
38
- bufferReadableStream(rscReadable.getReader()),
39
- ]);
40
- const { headers, status, statusText } = getResponseOptions(componentResponse);
52
+ dev,
53
+ pages,
54
+ nonce,
55
+ request,
56
+ template,
57
+ isStreamable,
58
+ componentResponse,
59
+ response: streamableResponse,
60
+ };
61
+ if (isReactHydrationRequest) {
62
+ return hydrate(url, params);
63
+ }
41
64
  /**
42
- * TODO: Also add `Vary` headers for `accept-language` and any other keys
43
- * we want to shard our full-page cache for all Hydrogen storefronts.
65
+ * Stream back real-user responses, but for bots/etc,
66
+ * use `render` instead. This is because we need to inject <head>
67
+ * things for SEO reasons.
44
68
  */
45
- headers[getCacheControlHeader({ dev })] =
46
- componentResponse.cacheControlHeader;
47
- if (componentResponse.customBody) {
48
- // This can be used to return sitemap.xml or any other custom response.
49
- logServerResponse('ssr', log, request, status);
50
- return new Response(await componentResponse.customBody, {
51
- status,
52
- statusText,
53
- headers,
54
- });
69
+ if (isStreamable) {
70
+ return stream(url, params);
55
71
  }
56
- headers['Content-type'] = HTML_CONTENT_TYPE;
57
- const { bodyAttributes, htmlAttributes, ...head } = extractHeadElements(request.ctx.helmet);
58
- html = html
59
- .replace(/<head>(.*?)<\/head>/s, generateHeadTag(head))
60
- .replace('<html', htmlAttributes ? `<html ${htmlAttributes}` : '$&')
61
- .replace('<body', bodyAttributes ? `<body ${bodyAttributes}` : '$&')
62
- .replace('</body>', flight
63
- ? `${flightContainer({ init: true, nonce, chunk: flight })}</body>`
64
- : '$&');
65
- logServerResponse('ssr', log, request, status);
66
- return new Response(html, {
67
- status,
68
- statusText,
69
- headers,
70
- });
72
+ return render(url, params);
71
73
  };
74
+ return handleRequest;
75
+ };
76
+ function getApiRoute(url, { pages }) {
77
+ const routes = getApiRoutesFromPages(pages);
78
+ return getApiRouteFromURL(url, routes);
79
+ }
80
+ /**
81
+ * The render function is responsible for turning the provided `App` into an HTML string,
82
+ * and returning any initial state that needs to be hydrated into the client version of the app.
83
+ * NOTE: This is currently only used for SEO bots or Worker runtime (where Stream is not yet supported).
84
+ */
85
+ async function render(url, { App, pages, request, componentResponse, log, template, nonce, dev, }) {
86
+ const state = { pathname: url.pathname, search: url.search };
87
+ const { AppSSR, rscReadable } = buildAppSSR({
88
+ App,
89
+ state,
90
+ request,
91
+ response: componentResponse,
92
+ pages,
93
+ log,
94
+ }, { template });
95
+ let [html, flight] = await Promise.all([
96
+ renderToBufferedString(AppSSR, { log, nonce }),
97
+ bufferReadableStream(rscReadable.getReader()),
98
+ ]);
99
+ const { headers, status, statusText } = getResponseOptions(componentResponse);
72
100
  /**
73
- * Stream a response to the client. NOTE: This omits custom `<head>`
74
- * information, so this method should not be used by crawlers.
101
+ * TODO: Also add `Vary` headers for `accept-language` and any other keys
102
+ * we want to shard our full-page cache for all Hydrogen storefronts.
75
103
  */
76
- const stream = async function (url, { request, response, template, nonce, dev }) {
77
- const { log, state, componentResponse } = setupCurrentRequest(url, request);
78
- log.trace('start stream');
79
- const { AppSSR, rscReadable } = buildAppSSR({
80
- App,
81
- state,
82
- request,
83
- response: componentResponse,
84
- log,
85
- pages,
86
- }, {
87
- template,
88
- htmlAttrs: { lang: 'en' },
89
- });
90
- const rscToScriptTagReadable = new ReadableStream({
91
- start(controller) {
92
- log.trace('rsc start chunks');
93
- let init = true;
94
- const encoder = new TextEncoder();
95
- bufferReadableStream(rscReadable.getReader(), (chunk) => {
96
- const scriptTag = flightContainer({ init, chunk, nonce });
97
- controller.enqueue(encoder.encode(scriptTag));
98
- init = false;
99
- }).then(() => {
100
- log.trace('rsc finish chunks');
101
- return controller.close();
102
- });
103
- },
104
+ headers[getCacheControlHeader({ dev })] = componentResponse.cacheControlHeader;
105
+ if (componentResponse.customBody) {
106
+ // This can be used to return sitemap.xml or any other custom response.
107
+ postRequestTasks('ssr', status, request, componentResponse);
108
+ return new Response(await componentResponse.customBody, {
109
+ status,
110
+ statusText,
111
+ headers,
104
112
  });
105
- let didError;
106
- if (__WORKER__) {
107
- const deferredShouldReturnApp = defer();
113
+ }
114
+ headers['Content-type'] = HTML_CONTENT_TYPE;
115
+ const { bodyAttributes, htmlAttributes, ...head } = extractHeadElements(request.ctx.head);
116
+ html = html
117
+ .replace(/<head>(.*?)<\/head>/s, generateHeadTag(head))
118
+ .replace('<html', htmlAttributes ? `<html ${htmlAttributes}` : '$&')
119
+ .replace('<body', bodyAttributes ? `<body ${bodyAttributes}` : '$&')
120
+ .replace('</body>', flight
121
+ ? `${flightContainer({ init: true, nonce, chunk: flight })}</body>`
122
+ : '$&');
123
+ postRequestTasks('ssr', status, request, componentResponse);
124
+ return new Response(html, {
125
+ status,
126
+ statusText,
127
+ headers,
128
+ });
129
+ }
130
+ /**
131
+ * Stream a response to the client. NOTE: This omits custom `<head>`
132
+ * information, so this method should not be used by crawlers.
133
+ */
134
+ async function stream(url, { App, pages, request, response, componentResponse, log, template, nonce, dev, }) {
135
+ const state = { pathname: url.pathname, search: url.search };
136
+ log.trace('start stream');
137
+ const { noScriptTemplate, bootstrapScripts, bootstrapModules } = stripScriptsFromTemplate(template);
138
+ const { AppSSR, rscReadable } = buildAppSSR({
139
+ App,
140
+ state,
141
+ request,
142
+ response: componentResponse,
143
+ log,
144
+ pages,
145
+ }, {
146
+ template: noScriptTemplate,
147
+ htmlAttrs: { lang: 'en' },
148
+ });
149
+ const rscToScriptTagReadable = new ReadableStream({
150
+ start(controller) {
151
+ log.trace('rsc start chunks');
152
+ let init = true;
108
153
  const encoder = new TextEncoder();
109
- const transform = new TransformStream();
110
- const writable = transform.writable.getWriter();
111
- const responseOptions = {};
112
- const ssrReadable = ssrRenderToReadableStream(AppSSR, {
113
- nonce,
114
- onCompleteShell() {
115
- log.trace('worker ready to stream');
116
- Object.assign(responseOptions, getResponseOptions(componentResponse, didError));
117
- /**
118
- * TODO: This assumes `response.cache()` has been called _before_ any
119
- * queries which might be caught behind Suspense. Clarify this or add
120
- * additional checks downstream?
121
- */
122
- responseOptions.headers[getCacheControlHeader({ dev })] =
123
- componentResponse.cacheControlHeader;
124
- if (isRedirect(responseOptions)) {
125
- // Return redirects early without further rendering/streaming
126
- return deferredShouldReturnApp.resolve(false);
127
- }
128
- if (!componentResponse.canStream())
129
- return;
130
- startWritingHtmlToStream(responseOptions, writable, encoder, dev ? didError : undefined);
131
- deferredShouldReturnApp.resolve(true);
132
- },
133
- async onCompleteAll() {
134
- log.trace('worker complete stream');
135
- if (componentResponse.canStream())
136
- return;
137
- Object.assign(responseOptions, getResponseOptions(componentResponse, didError));
138
- if (isRedirect(responseOptions)) {
139
- // Redirects found after any async code
140
- return deferredShouldReturnApp.resolve(false);
141
- }
142
- if (componentResponse.customBody) {
143
- writable.write(encoder.encode(await componentResponse.customBody));
144
- return deferredShouldReturnApp.resolve(false);
145
- }
146
- startWritingHtmlToStream(responseOptions, writable, encoder, dev ? didError : undefined);
147
- deferredShouldReturnApp.resolve(true);
148
- },
149
- onError(error) {
150
- didError = error;
151
- if (dev && deferredShouldReturnApp.status === 'pending') {
152
- writable.write(getErrorMarkup(error));
153
- }
154
- console.error(error);
155
- },
154
+ bufferReadableStream(rscReadable.getReader(), (chunk) => {
155
+ const scriptTag = flightContainer({ init, chunk, nonce });
156
+ controller.enqueue(encoder.encode(scriptTag));
157
+ init = false;
158
+ }).then(() => {
159
+ log.trace('rsc finish chunks');
160
+ return controller.close();
156
161
  });
157
- if (await deferredShouldReturnApp.promise) {
158
- let bufferedSsr = '';
159
- let isPendingSsrWrite = false;
160
- const writingSSR = bufferReadableStream(ssrReadable.getReader(), (chunk) => {
161
- bufferedSsr += chunk;
162
- if (!isPendingSsrWrite) {
163
- isPendingSsrWrite = true;
164
- setTimeout(() => {
165
- isPendingSsrWrite = false;
166
- // React can write fractional chunks synchronously.
167
- // This timeout ensures we only write full HTML tags
168
- // in order to allow RSC writing concurrently.
169
- if (bufferedSsr) {
170
- writable.write(encoder.encode(bufferedSsr));
171
- bufferedSsr = '';
172
- }
173
- }, 0);
174
- }
175
- });
176
- const writingRSC = bufferReadableStream(rscToScriptTagReadable.getReader(), (scriptTag) => writable.write(encoder.encode(scriptTag)));
177
- Promise.all([writingSSR, writingRSC]).then(() => {
178
- // Last SSR write might be pending, delay closing the writable one tick
179
- setTimeout(() => writable.close(), 0);
180
- logServerResponse('str', log, request, responseOptions.status);
181
- });
182
- }
183
- else {
184
- writable.close();
185
- logServerResponse('str', log, request, responseOptions.status);
186
- }
187
- if (await isStreamingSupported()) {
188
- return new Response(transform.readable, responseOptions);
189
- }
190
- const bufferedBody = await bufferReadableStream(transform.readable.getReader());
191
- return new Response(bufferedBody, responseOptions);
192
- }
193
- else if (response) {
194
- response.socket.on('error', log.fatal);
195
- const { pipe } = ssrRenderToPipeableStream(AppSSR, {
196
- nonce,
197
- onCompleteShell() {
198
- log.trace('node ready to stream');
199
- /**
200
- * TODO: This assumes `response.cache()` has been called _before_ any
201
- * queries which might be caught behind Suspense. Clarify this or add
202
- * additional checks downstream?
203
- */
204
- response.setHeader(getCacheControlHeader({ dev }), componentResponse.cacheControlHeader);
205
- writeHeadToServerResponse(response, componentResponse, log, didError);
206
- logServerResponse('str', log, request, response.statusCode);
207
- if (isRedirect(response)) {
208
- // Return redirects early without further rendering/streaming
209
- return response.end();
210
- }
211
- if (!componentResponse.canStream())
212
- return;
213
- startWritingHtmlToServerResponse(response, dev ? didError : undefined);
162
+ },
163
+ });
164
+ let didError;
165
+ if (__WORKER__) {
166
+ const deferredShouldReturnApp = defer();
167
+ const encoder = new TextEncoder();
168
+ const transform = new TransformStream();
169
+ const writable = transform.writable.getWriter();
170
+ const responseOptions = {};
171
+ const ssrReadable = ssrRenderToReadableStream(AppSSR, {
172
+ nonce,
173
+ bootstrapScripts,
174
+ bootstrapModules,
175
+ onCompleteShell() {
176
+ log.trace('worker ready to stream');
177
+ Object.assign(responseOptions, getResponseOptions(componentResponse, didError));
178
+ /**
179
+ * TODO: This assumes `response.cache()` has been called _before_ any
180
+ * queries which might be caught behind Suspense. Clarify this or add
181
+ * additional checks downstream?
182
+ */
183
+ responseOptions.headers[getCacheControlHeader({ dev })] =
184
+ componentResponse.cacheControlHeader;
185
+ if (isRedirect(responseOptions)) {
186
+ // Return redirects early without further rendering/streaming
187
+ return deferredShouldReturnApp.resolve(false);
188
+ }
189
+ if (!componentResponse.canStream())
190
+ return;
191
+ startWritingHtmlToStream(responseOptions, writable, encoder, dev ? didError : undefined);
192
+ deferredShouldReturnApp.resolve(true);
193
+ },
194
+ async onCompleteAll() {
195
+ log.trace('worker complete stream');
196
+ if (componentResponse.canStream())
197
+ return;
198
+ Object.assign(responseOptions, getResponseOptions(componentResponse, didError));
199
+ if (isRedirect(responseOptions)) {
200
+ // Redirects found after any async code
201
+ return deferredShouldReturnApp.resolve(false);
202
+ }
203
+ if (componentResponse.customBody) {
204
+ writable.write(encoder.encode(await componentResponse.customBody));
205
+ return deferredShouldReturnApp.resolve(false);
206
+ }
207
+ startWritingHtmlToStream(responseOptions, writable, encoder, dev ? didError : undefined);
208
+ deferredShouldReturnApp.resolve(true);
209
+ },
210
+ onError(error) {
211
+ didError = error;
212
+ if (dev && deferredShouldReturnApp.status === 'pending') {
213
+ writable.write(getErrorMarkup(error));
214
+ }
215
+ log.error(error);
216
+ },
217
+ });
218
+ if (await deferredShouldReturnApp.promise) {
219
+ let bufferedSsr = '';
220
+ let isPendingSsrWrite = false;
221
+ const writingSSR = bufferReadableStream(ssrReadable.getReader(), (chunk) => {
222
+ bufferedSsr += chunk;
223
+ if (!isPendingSsrWrite) {
224
+ isPendingSsrWrite = true;
214
225
  setTimeout(() => {
215
- log.trace('node pipe response');
216
- pipe(response);
226
+ isPendingSsrWrite = false;
227
+ // React can write fractional chunks synchronously.
228
+ // This timeout ensures we only write full HTML tags
229
+ // in order to allow RSC writing concurrently.
230
+ if (bufferedSsr) {
231
+ writable.write(encoder.encode(bufferedSsr));
232
+ bufferedSsr = '';
233
+ }
217
234
  }, 0);
218
- bufferReadableStream(rscToScriptTagReadable.getReader(), (chunk) => {
219
- log.trace('rsc chunk');
220
- return response.write(chunk);
221
- });
222
- },
223
- async onCompleteAll() {
224
- log.trace('node complete stream');
225
- clearTimeout(streamTimeout);
226
- if (componentResponse.canStream() || response.writableEnded)
227
- return;
228
- writeHeadToServerResponse(response, componentResponse, log, didError);
229
- logServerResponse('str', log, request, response.statusCode);
230
- if (isRedirect(response)) {
231
- // Redirects found after any async code
232
- return response.end();
233
- }
234
- if (componentResponse.customBody) {
235
- return response.end(await componentResponse.customBody);
236
- }
237
- startWritingHtmlToServerResponse(response, dev ? didError : undefined);
238
- bufferReadableStream(rscToScriptTagReadable.getReader()).then((scriptTags) => {
239
- // Piping ends the response so script tags
240
- // must be written before that.
241
- response.write(scriptTags);
242
- pipe(response);
243
- });
244
- },
245
- onError(error) {
246
- didError = error;
247
- if (dev && response.headersSent) {
248
- // Calling write would flush headers automatically.
249
- // Delay this error until headers are properly sent.
250
- response.write(getErrorMarkup(error));
251
- }
252
- log.error(error);
253
- },
235
+ }
236
+ });
237
+ const writingRSC = bufferReadableStream(rscToScriptTagReadable.getReader(), (scriptTag) => writable.write(encoder.encode(scriptTag)));
238
+ Promise.all([writingSSR, writingRSC]).then(() => {
239
+ // Last SSR write might be pending, delay closing the writable one tick
240
+ setTimeout(() => writable.close(), 0);
241
+ postRequestTasks('str', responseOptions.status, request, componentResponse);
254
242
  });
255
- const streamTimeout = setTimeout(() => {
256
- log.warn(`The app failed to stream after ${STREAM_ABORT_TIMEOUT_MS} ms`);
257
- }, STREAM_ABORT_TIMEOUT_MS);
258
243
  }
259
- };
260
- /**
261
- * Stream a hydration response to the client.
262
- */
263
- const hydrate = async function (url, { request, response, isStreamable }) {
264
- const { log, state, componentResponse } = setupCurrentRequest(url, request);
265
- const { AppRSC } = buildAppRSC({
266
- App,
267
- state,
268
- request,
269
- response: componentResponse,
270
- log,
271
- pages,
272
- });
273
- if (__WORKER__) {
274
- const rscReadable = rscRenderToReadableStream(AppRSC);
275
- if (isStreamable && (await isStreamingSupported())) {
276
- logServerResponse('rsc', log, request, 200);
277
- return new Response(rscReadable);
278
- }
279
- // Note: CFW does not support reader.piteTo nor iterable syntax
280
- const bufferedBody = await bufferReadableStream(rscReadable.getReader());
281
- logServerResponse('rsc', log, request, 200);
282
- return new Response(bufferedBody);
244
+ else {
245
+ writable.close();
246
+ postRequestTasks('str', responseOptions.status, request, componentResponse);
283
247
  }
284
- else if (response) {
285
- response.socket.on('error', log.fatal);
286
- const rscWriter = await import(
287
- // @ts-ignore
288
- '@shopify/hydrogen/vendor/react-server-dom-vite/writer.node.server');
289
- const stream = rscWriter
290
- .renderToPipeableStream(AppRSC)
291
- .pipe(response);
292
- stream.on('finish', function () {
293
- logServerResponse('rsc', log, request, response.statusCode);
294
- });
248
+ if (await isStreamingSupported()) {
249
+ return new Response(transform.readable, responseOptions);
295
250
  }
296
- };
297
- function getApiRoute(url) {
298
- const routes = getApiRoutesFromPages(pages);
299
- return getApiRouteFromURL(url, routes);
251
+ const bufferedBody = await bufferReadableStream(transform.readable.getReader());
252
+ return new Response(bufferedBody, responseOptions);
300
253
  }
301
- return {
302
- render,
303
- stream,
304
- hydrate,
305
- getApiRoute,
306
- log: noContextLogger,
307
- };
308
- };
309
- async function bufferReadableStream(reader, cb) {
310
- const decoder = new TextDecoder();
311
- let result = '';
312
- while (true) {
313
- const { done, value } = await reader.read();
314
- if (done)
315
- break;
316
- const stringValue = typeof value === 'string' ? value : decoder.decode(value);
317
- result += stringValue;
318
- if (cb) {
319
- cb(stringValue);
254
+ else if (response) {
255
+ response.socket.on('error', log.fatal);
256
+ const { pipe } = ssrRenderToPipeableStream(AppSSR, {
257
+ nonce,
258
+ bootstrapScripts,
259
+ bootstrapModules,
260
+ onCompleteShell() {
261
+ log.trace('node ready to stream');
262
+ /**
263
+ * TODO: This assumes `response.cache()` has been called _before_ any
264
+ * queries which might be caught behind Suspense. Clarify this or add
265
+ * additional checks downstream?
266
+ */
267
+ response.setHeader(getCacheControlHeader({ dev }), componentResponse.cacheControlHeader);
268
+ writeHeadToServerResponse(response, componentResponse, log, didError);
269
+ logServerResponse('str', request, response.statusCode);
270
+ if (isRedirect(response)) {
271
+ // Return redirects early without further rendering/streaming
272
+ return response.end();
273
+ }
274
+ if (!componentResponse.canStream())
275
+ return;
276
+ startWritingHtmlToServerResponse(response, dev ? didError : undefined);
277
+ setTimeout(() => {
278
+ log.trace('node pipe response');
279
+ pipe(response);
280
+ }, 0);
281
+ bufferReadableStream(rscToScriptTagReadable.getReader(), (chunk) => {
282
+ log.trace('rsc chunk');
283
+ return response.write(chunk);
284
+ });
285
+ },
286
+ async onCompleteAll() {
287
+ log.trace('node complete stream');
288
+ if (componentResponse.canStream() || response.writableEnded) {
289
+ postRequestTasks('str', response.statusCode, request, componentResponse);
290
+ return;
291
+ }
292
+ writeHeadToServerResponse(response, componentResponse, log, didError);
293
+ postRequestTasks('str', response.statusCode, request, componentResponse);
294
+ if (isRedirect(response)) {
295
+ // Redirects found after any async code
296
+ return response.end();
297
+ }
298
+ if (componentResponse.customBody) {
299
+ return response.end(await componentResponse.customBody);
300
+ }
301
+ startWritingHtmlToServerResponse(response, dev ? didError : undefined);
302
+ bufferReadableStream(rscToScriptTagReadable.getReader()).then((scriptTags) => {
303
+ // Piping ends the response so script tags
304
+ // must be written before that.
305
+ response.write(scriptTags);
306
+ pipe(response);
307
+ });
308
+ },
309
+ onError(error) {
310
+ didError = error;
311
+ if (dev && response.headersSent) {
312
+ // Calling write would flush headers automatically.
313
+ // Delay this error until headers are properly sent.
314
+ response.write(getErrorMarkup(error));
315
+ }
316
+ log.error(error);
317
+ },
318
+ });
319
+ }
320
+ }
321
+ /**
322
+ * Stream a hydration response to the client.
323
+ */
324
+ async function hydrate(url, { App, pages, request, response, componentResponse, isStreamable, log, }) {
325
+ const state = JSON.parse(url.searchParams.get('state') || '{}');
326
+ const { AppRSC } = buildAppRSC({
327
+ App,
328
+ state,
329
+ request,
330
+ response: componentResponse,
331
+ log,
332
+ pages,
333
+ });
334
+ if (__WORKER__) {
335
+ const rscReadable = rscRenderToReadableStream(AppRSC);
336
+ if (isStreamable && (await isStreamingSupported())) {
337
+ postRequestTasks('rsc', 200, request, componentResponse);
338
+ return new Response(rscReadable);
320
339
  }
340
+ // Note: CFW does not support reader.piteTo nor iterable syntax
341
+ const bufferedBody = await bufferReadableStream(rscReadable.getReader());
342
+ postRequestTasks('rsc', 200, request, componentResponse);
343
+ return new Response(bufferedBody);
344
+ }
345
+ else if (response) {
346
+ response.socket.on('error', log.fatal);
347
+ const rscWriter = await import(
348
+ // @ts-ignore
349
+ '@shopify/hydrogen/vendor/react-server-dom-vite/writer.node.server');
350
+ const stream = rscWriter
351
+ .renderToPipeableStream(AppRSC)
352
+ .pipe(response);
353
+ stream.on('finish', function () {
354
+ postRequestTasks('rsc', response.statusCode, request, componentResponse);
355
+ });
321
356
  }
322
- return result;
323
357
  }
324
358
  function buildAppRSC({ App, state, request, response, log, pages, }) {
325
359
  const hydrogenServerProps = { request, response, log };
326
360
  const AppRSC = (React.createElement(ServerRequestProvider, { request: request, isRSC: true },
327
- React.createElement(App, { ...state, ...hydrogenServerProps, pages: pages })));
361
+ React.createElement(PreloadQueries, { request: request },
362
+ React.createElement(App, { ...state, ...hydrogenServerProps, pages: pages }))));
328
363
  return { AppRSC };
329
364
  }
330
365
  function buildAppSSR({ App, state, request, response, log, pages }, htmlOptions) {
@@ -342,10 +377,16 @@ function buildAppSSR({ App, state, request, response, log, pages }, htmlOptions)
342
377
  const AppSSR = (React.createElement(Html, { ...htmlOptions },
343
378
  React.createElement(ServerRequestProvider, { request: request, isRSC: false },
344
379
  React.createElement(ServerStateProvider, { serverState: state, setServerState: () => { } },
345
- React.createElement(React.Suspense, { fallback: null },
346
- React.createElement(RscConsumer, null))))));
380
+ React.createElement(PreloadQueries, { request: request },
381
+ React.createElement(React.Suspense, { fallback: null },
382
+ React.createElement(RscConsumer, null)))))));
347
383
  return { AppSSR, rscReadable: rscReadableForFlight };
348
384
  }
385
+ function PreloadQueries({ request, children, }) {
386
+ const preloadQueries = request.getPreloadQueries();
387
+ preloadRequestCacheData(request, preloadQueries);
388
+ return children;
389
+ }
349
390
  function extractHeadElements({ context: { helmet } }) {
350
391
  return helmet
351
392
  ? {
@@ -363,15 +404,11 @@ function extractHeadElements({ context: { helmet } }) {
363
404
  }
364
405
  async function renderToBufferedString(ReactApp, { log, nonce }) {
365
406
  return new Promise(async (resolve, reject) => {
366
- const errorTimeout = setTimeout(() => {
367
- log.warn(`The app failed to SSR after ${STREAM_ABORT_TIMEOUT_MS} ms`);
368
- }, STREAM_ABORT_TIMEOUT_MS);
369
407
  if (__WORKER__) {
370
408
  const deferred = defer();
371
409
  const readable = ssrRenderToReadableStream(ReactApp, {
372
410
  nonce,
373
411
  onCompleteAll() {
374
- clearTimeout(errorTimeout);
375
412
  /**
376
413
  * We want to wait until `onCompleteAll` has been called before fetching the
377
414
  * stream body. Otherwise, React 18's streaming JS script/template tags
@@ -397,7 +434,6 @@ async function renderToBufferedString(ReactApp, { log, nonce }) {
397
434
  * `template` and `script` tags inserted and rendered as part of the hydration response.
398
435
  */
399
436
  onCompleteAll() {
400
- clearTimeout(errorTimeout);
401
437
  let data = '';
402
438
  writer.on('data', (chunk) => (data += chunk.toString()));
403
439
  writer.once('error', reject);
@@ -502,36 +538,18 @@ function flightContainer({ init, chunk, nonce, }) {
502
538
  script += 'var __flight=[];';
503
539
  }
504
540
  if (chunk) {
505
- const normalizedChunk = chunk === null || chunk === void 0 ? void 0 : chunk.replace(/\\/g, String.raw `\\`);
541
+ const normalizedChunk = chunk
542
+ // 1. Duplicate the escape char (\) for already escaped characters (e.g. \n or \").
543
+ .replace(/\\/g, String.raw `\\`)
544
+ // 2. Escape existing backticks to allow wrapping the whole thing in `...`.
545
+ .replace(/`/g, String.raw `\``);
506
546
  script += `__flight.push(\`${normalizedChunk}\`)`;
507
547
  }
508
548
  return script + '</script>';
509
549
  }
510
- let cachedStreamingSupport;
511
- async function isStreamingSupported() {
512
- if (cachedStreamingSupport === undefined) {
513
- try {
514
- const rs = new ReadableStream({
515
- start(controller) {
516
- controller.close();
517
- },
518
- });
519
- // This will throw in CFW until streaming
520
- // is supported. It works in Miniflare.
521
- await new Response(rs).text();
522
- cachedStreamingSupport = true;
523
- }
524
- catch (_) {
525
- cachedStreamingSupport = false;
526
- }
527
- }
528
- return cachedStreamingSupport;
529
- }
530
- function setupCurrentRequest(url, request) {
531
- const log = getLoggerFromContext(request);
532
- const state = url.pathname === RSC_PATHNAME
533
- ? JSON.parse(url.searchParams.get('state') || '{}')
534
- : { pathname: url.pathname, search: url.search };
535
- const componentResponse = new ServerComponentResponse();
536
- return { log, state, componentResponse };
550
+ function postRequestTasks(type, status, request, componentResponse) {
551
+ logServerResponse(type, request, status);
552
+ logCacheControlHeaders(type, request, componentResponse);
553
+ logQueryTimings(type, request);
554
+ request.savePreloadQueries();
537
555
  }