@shopify/hydrogen 2026.10.0-preview.3 → 2026.10.0-preview.5

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 (531) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/index.mjs +150 -36
  3. package/dist/client/client.d.mts +18 -15
  4. package/dist/client/client.mjs +10 -5
  5. package/dist/client/client.mjs.map +1 -1
  6. package/dist/client/errors.d.mts +41 -19
  7. package/dist/client/errors.mjs +29 -16
  8. package/dist/client/errors.mjs.map +1 -1
  9. package/dist/client/types.d.mts +164 -33
  10. package/dist/core/analytics/cart-tracker.d.mts +23 -0
  11. package/dist/core/analytics/cart-tracker.mjs +23 -1
  12. package/dist/core/analytics/cart-tracker.mjs.map +1 -1
  13. package/dist/core/analytics/custom-consent.mjs +12 -0
  14. package/dist/core/analytics/custom-consent.mjs.map +1 -0
  15. package/dist/core/analytics/events.d.mts +20 -7
  16. package/dist/core/analytics/events.mjs +16 -0
  17. package/dist/core/analytics/events.mjs.map +1 -1
  18. package/dist/core/analytics/index.d.mts +1 -1
  19. package/dist/core/analytics/types.d.mts +265 -40
  20. package/dist/core/analytics/utils/flatten-connection.d.mts +11 -9
  21. package/dist/core/analytics/utils/flatten-connection.mjs +7 -6
  22. package/dist/core/analytics/utils/flatten-connection.mjs.map +1 -1
  23. package/dist/core/cache/fetch-with-cache.d.mts +24 -4
  24. package/dist/core/cache/fetch-with-cache.mjs +17 -2
  25. package/dist/core/cache/fetch-with-cache.mjs.map +1 -1
  26. package/dist/core/cache/key.d.mts +2 -0
  27. package/dist/core/cache/key.mjs.map +1 -1
  28. package/dist/core/cache/run-with-cache.d.mts +56 -8
  29. package/dist/core/cache/run-with-cache.mjs +17 -6
  30. package/dist/core/cache/run-with-cache.mjs.map +1 -1
  31. package/dist/core/cache/store.d.mts +8 -6
  32. package/dist/core/cache/store.mjs.map +1 -1
  33. package/dist/core/cache/strategies.d.mts +27 -2
  34. package/dist/core/cache/strategies.mjs +6 -0
  35. package/dist/core/cache/strategies.mjs.map +1 -1
  36. package/dist/core/cart/actions.d.mts +59 -8
  37. package/dist/core/cart/actions.mjs +39 -0
  38. package/dist/core/cart/actions.mjs.map +1 -1
  39. package/dist/core/cart/attach-quantity-input.d.mts +27 -0
  40. package/dist/core/cart/attach-quantity-input.mjs +27 -0
  41. package/dist/core/cart/attach-quantity-input.mjs.map +1 -1
  42. package/dist/core/cart/buyer-identity-sync.d.mts +7 -6
  43. package/dist/core/cart/buyer-identity-sync.mjs.map +1 -1
  44. package/dist/core/cart/cart.d.mts +121 -8
  45. package/dist/core/cart/cart.mjs +76 -18
  46. package/dist/core/cart/cart.mjs.map +1 -1
  47. package/dist/core/cart/cookie.d.mts +19 -0
  48. package/dist/core/cart/cookie.mjs +19 -0
  49. package/dist/core/cart/cookie.mjs.map +1 -1
  50. package/dist/core/cart/form.d.mts +75 -0
  51. package/dist/core/cart/form.mjs +20 -0
  52. package/dist/core/cart/form.mjs.map +1 -1
  53. package/dist/core/cart/get-cart.d.mts +55 -2
  54. package/dist/core/cart/get-cart.mjs +38 -0
  55. package/dist/core/cart/get-cart.mjs.map +1 -1
  56. package/dist/core/cart/index.d.mts +1 -1
  57. package/dist/core/cart/quantity.d.mts +28 -0
  58. package/dist/core/cart/quantity.mjs +28 -0
  59. package/dist/core/cart/quantity.mjs.map +1 -1
  60. package/dist/core/cart/queries.d.mts +353 -314
  61. package/dist/core/cart/queries.mjs +16 -1
  62. package/dist/core/cart/queries.mjs.map +1 -1
  63. package/dist/core/cart/server-handlers.d.mts +120 -14
  64. package/dist/core/cart/server-handlers.mjs +6 -10
  65. package/dist/core/cart/server-handlers.mjs.map +1 -1
  66. package/dist/core/cart/state.d.mts +180 -1
  67. package/dist/core/cart/state.mjs +26 -0
  68. package/dist/core/cart/state.mjs.map +1 -1
  69. package/dist/core/collection/collection.d.mts +66 -51
  70. package/dist/core/collection/collection.mjs +16 -3
  71. package/dist/core/collection/collection.mjs.map +1 -1
  72. package/dist/core/collection/index.d.mts +1 -1
  73. package/dist/core/collection/reconciler.d.mts +27 -11
  74. package/dist/core/collection/reconciler.mjs +8 -4
  75. package/dist/core/collection/reconciler.mjs.map +1 -1
  76. package/dist/core/collection/state.d.mts +33 -23
  77. package/dist/core/collection/state.mjs +4 -3
  78. package/dist/core/collection/state.mjs.map +1 -1
  79. package/dist/core/collection/url.d.mts +71 -27
  80. package/dist/core/collection/url.mjs +63 -25
  81. package/dist/core/collection/url.mjs.map +1 -1
  82. package/dist/core/constants.d.mts +2 -1
  83. package/dist/core/constants.mjs +3 -2
  84. package/dist/core/constants.mjs.map +1 -1
  85. package/dist/core/headers.mjs +2 -2
  86. package/dist/core/headers.mjs.map +1 -1
  87. package/dist/core/index.d.mts +5 -5
  88. package/dist/core/logging/logging.d.mts +14 -8
  89. package/dist/core/logging/logging.mjs +15 -10
  90. package/dist/core/logging/logging.mjs.map +1 -1
  91. package/dist/core/logging/types.d.mts +28 -11
  92. package/dist/core/logging/types.mjs.map +1 -1
  93. package/dist/core/money/format.d.mts +6 -4
  94. package/dist/core/money/format.mjs +24 -11
  95. package/dist/core/money/format.mjs.map +1 -1
  96. package/dist/core/money/types.d.mts +41 -25
  97. package/dist/core/observable.mjs +0 -3
  98. package/dist/core/observable.mjs.map +1 -1
  99. package/dist/core/predictive-search/form.d.mts +37 -6
  100. package/dist/core/predictive-search/form.mjs +21 -2
  101. package/dist/core/predictive-search/form.mjs.map +1 -1
  102. package/dist/core/predictive-search/index.d.mts +1 -1
  103. package/dist/core/predictive-search/queries.d.mts +30 -6
  104. package/dist/core/predictive-search/queries.mjs +11 -1
  105. package/dist/core/predictive-search/queries.mjs.map +1 -1
  106. package/dist/core/predictive-search/search.d.mts +41 -10
  107. package/dist/core/predictive-search/search.mjs +35 -0
  108. package/dist/core/predictive-search/search.mjs.map +1 -1
  109. package/dist/core/predictive-search/server-handlers.d.mts +28 -3
  110. package/dist/core/predictive-search/server-handlers.mjs +9 -3
  111. package/dist/core/predictive-search/server-handlers.mjs.map +1 -1
  112. package/dist/core/predictive-search/store.d.mts +67 -24
  113. package/dist/core/predictive-search/store.mjs +12 -1
  114. package/dist/core/predictive-search/store.mjs.map +1 -1
  115. package/dist/core/predictive-search/url.d.mts +49 -8
  116. package/dist/core/predictive-search/url.mjs +17 -0
  117. package/dist/core/predictive-search/url.mjs.map +1 -1
  118. package/dist/core/product/form.d.mts +63 -16
  119. package/dist/core/product/form.mjs +8 -2
  120. package/dist/core/product/form.mjs.map +1 -1
  121. package/dist/core/product/options.d.mts +8 -10
  122. package/dist/core/product/options.mjs +8 -10
  123. package/dist/core/product/options.mjs.map +1 -1
  124. package/dist/core/product/product-form.d.mts +92 -12
  125. package/dist/core/product/product-form.mjs +56 -4
  126. package/dist/core/product/product-form.mjs.map +1 -1
  127. package/dist/core/product/state.d.mts +64 -0
  128. package/dist/core/product/url.d.mts +22 -10
  129. package/dist/core/product/url.mjs +10 -10
  130. package/dist/core/product/url.mjs.map +1 -1
  131. package/dist/core/request-context.d.mts +77 -22
  132. package/dist/core/request-context.mjs +17 -39
  133. package/dist/core/request-context.mjs.map +1 -1
  134. package/dist/core/request-routing/handle-shopify-redirects.d.mts +12 -4
  135. package/dist/core/request-routing/handle-shopify-redirects.mjs +9 -2
  136. package/dist/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
  137. package/dist/core/request-routing/handle-shopify-routes.d.mts +5 -5
  138. package/dist/core/request-routing/handle-shopify-routes.mjs +9 -6
  139. package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  140. package/dist/core/request-routing/interceptors/ajax-api.mjs +1 -1
  141. package/dist/core/request-routing/interceptors/api-proxy.mjs +1 -1
  142. package/dist/core/request-routing/interceptors/checkout.mjs +19 -6
  143. package/dist/core/request-routing/interceptors/checkout.mjs.map +1 -1
  144. package/dist/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
  145. package/dist/core/request-routing/interceptors/proxy.mjs +83 -32
  146. package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
  147. package/dist/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
  148. package/dist/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
  149. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
  150. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
  151. package/dist/core/request-routing/interceptors/ucp-mcp-proxy.mjs +1 -1
  152. package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
  153. package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
  154. package/dist/core/request-routing/interceptors/url-redirects.mjs +6 -1
  155. package/dist/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
  156. package/dist/core/request-routing/interceptors/well-known.mjs +2 -2
  157. package/dist/core/request-routing/interceptors/well-known.mjs.map +1 -1
  158. package/dist/core/request-routing/registered-routes.d.mts +12 -0
  159. package/dist/core/request-routing/registered-routes.mjs +12 -0
  160. package/dist/core/request-routing/registered-routes.mjs.map +1 -1
  161. package/dist/core/request-routing/route-types.d.mts +43 -18
  162. package/dist/core/shop-pay/shop-pay.d.mts +71 -35
  163. package/dist/core/shop-pay/shop-pay.mjs +36 -10
  164. package/dist/core/shop-pay/shop-pay.mjs.map +1 -1
  165. package/dist/core/shopify-scripts/analytics.mjs +2 -2
  166. package/dist/core/shopify-scripts/analytics.mjs.map +1 -1
  167. package/dist/core/shopify-scripts/constants.mjs +1 -1
  168. package/dist/core/shopify-scripts/constants.mjs.map +1 -1
  169. package/dist/core/shopify-scripts/global.mjs +5 -1
  170. package/dist/core/shopify-scripts/global.mjs.map +1 -1
  171. package/dist/core/shopify-scripts/index.d.mts +15 -5
  172. package/dist/core/shopify-scripts/index.mjs +15 -5
  173. package/dist/core/shopify-scripts/index.mjs.map +1 -1
  174. package/dist/core/shopify-scripts/initialize.d.mts +13 -4
  175. package/dist/core/shopify-scripts/initialize.mjs +22 -7
  176. package/dist/core/shopify-scripts/initialize.mjs.map +1 -1
  177. package/dist/core/shopify-scripts/types.d.mts +58 -24
  178. package/dist/core/standard-routes/build.d.mts +11 -12
  179. package/dist/core/standard-routes/build.mjs +11 -12
  180. package/dist/core/standard-routes/build.mjs.map +1 -1
  181. package/dist/core/standard-routes/types.d.mts +30 -24
  182. package/dist/core/url.mjs +21 -2
  183. package/dist/core/url.mjs.map +1 -1
  184. package/dist/customer-account/client.d.mts +95 -4
  185. package/dist/customer-account/client.mjs +26 -1
  186. package/dist/customer-account/client.mjs.map +1 -1
  187. package/dist/customer-account/errors.d.mts +55 -0
  188. package/dist/customer-account/errors.mjs +31 -0
  189. package/dist/customer-account/errors.mjs.map +1 -1
  190. package/dist/customer-account/graphql.d.mts +56 -0
  191. package/dist/customer-account/graphql.mjs +41 -0
  192. package/dist/customer-account/graphql.mjs.map +1 -1
  193. package/dist/customer-account/session.d.mts +166 -45
  194. package/dist/customer-account/session.mjs +49 -12
  195. package/dist/customer-account/session.mjs.map +1 -1
  196. package/dist/customer-account/type-resolver.d.mts +22 -0
  197. package/dist/customer-account-api-types.d.ts +570 -374
  198. package/dist/customer-account.schema.json +2981 -1527
  199. package/dist/development/client/client.d.mts +18 -15
  200. package/dist/development/client/client.mjs +10 -5
  201. package/dist/development/client/client.mjs.map +1 -1
  202. package/dist/development/client/errors.d.mts +41 -19
  203. package/dist/development/client/errors.mjs +30 -20
  204. package/dist/development/client/errors.mjs.map +1 -1
  205. package/dist/development/client/types.d.mts +164 -33
  206. package/dist/development/core/analytics/cart-tracker.d.mts +23 -0
  207. package/dist/development/core/analytics/cart-tracker.mjs +23 -1
  208. package/dist/development/core/analytics/cart-tracker.mjs.map +1 -1
  209. package/dist/development/core/analytics/custom-consent.mjs +12 -0
  210. package/dist/development/core/analytics/custom-consent.mjs.map +1 -0
  211. package/dist/development/core/analytics/events.d.mts +20 -7
  212. package/dist/development/core/analytics/events.mjs +16 -0
  213. package/dist/development/core/analytics/events.mjs.map +1 -1
  214. package/dist/development/core/analytics/index.d.mts +1 -1
  215. package/dist/development/core/analytics/types.d.mts +265 -40
  216. package/dist/development/core/analytics/utils/flatten-connection.d.mts +11 -9
  217. package/dist/development/core/analytics/utils/flatten-connection.mjs +7 -6
  218. package/dist/development/core/analytics/utils/flatten-connection.mjs.map +1 -1
  219. package/dist/development/core/cache/fetch-with-cache.d.mts +24 -4
  220. package/dist/development/core/cache/fetch-with-cache.mjs +17 -2
  221. package/dist/development/core/cache/fetch-with-cache.mjs.map +1 -1
  222. package/dist/development/core/cache/key.d.mts +2 -0
  223. package/dist/development/core/cache/key.mjs.map +1 -1
  224. package/dist/development/core/cache/run-with-cache.d.mts +56 -8
  225. package/dist/development/core/cache/run-with-cache.mjs +17 -6
  226. package/dist/development/core/cache/run-with-cache.mjs.map +1 -1
  227. package/dist/development/core/cache/store.d.mts +8 -6
  228. package/dist/development/core/cache/store.mjs.map +1 -1
  229. package/dist/development/core/cache/strategies.d.mts +27 -2
  230. package/dist/development/core/cache/strategies.mjs +6 -0
  231. package/dist/development/core/cache/strategies.mjs.map +1 -1
  232. package/dist/development/core/cart/actions.d.mts +59 -8
  233. package/dist/development/core/cart/actions.mjs +39 -0
  234. package/dist/development/core/cart/actions.mjs.map +1 -1
  235. package/dist/development/core/cart/attach-quantity-input.d.mts +27 -0
  236. package/dist/development/core/cart/attach-quantity-input.mjs +27 -0
  237. package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -1
  238. package/dist/development/core/cart/buyer-identity-sync.d.mts +6 -5
  239. package/dist/development/core/cart/buyer-identity-sync.mjs.map +1 -1
  240. package/dist/development/core/cart/cart.d.mts +121 -8
  241. package/dist/development/core/cart/cart.mjs +76 -18
  242. package/dist/development/core/cart/cart.mjs.map +1 -1
  243. package/dist/development/core/cart/cookie.d.mts +19 -0
  244. package/dist/development/core/cart/cookie.mjs +19 -0
  245. package/dist/development/core/cart/cookie.mjs.map +1 -1
  246. package/dist/development/core/cart/form.d.mts +75 -0
  247. package/dist/development/core/cart/form.mjs +20 -0
  248. package/dist/development/core/cart/form.mjs.map +1 -1
  249. package/dist/development/core/cart/get-cart.d.mts +55 -2
  250. package/dist/development/core/cart/get-cart.mjs +38 -0
  251. package/dist/development/core/cart/get-cart.mjs.map +1 -1
  252. package/dist/development/core/cart/index.d.mts +1 -1
  253. package/dist/development/core/cart/quantity.d.mts +28 -0
  254. package/dist/development/core/cart/quantity.mjs +28 -0
  255. package/dist/development/core/cart/quantity.mjs.map +1 -1
  256. package/dist/development/core/cart/queries.d.mts +410 -371
  257. package/dist/development/core/cart/queries.mjs +16 -1
  258. package/dist/development/core/cart/queries.mjs.map +1 -1
  259. package/dist/development/core/cart/server-handlers.d.mts +120 -14
  260. package/dist/development/core/cart/server-handlers.mjs +6 -10
  261. package/dist/development/core/cart/server-handlers.mjs.map +1 -1
  262. package/dist/development/core/cart/state.d.mts +180 -1
  263. package/dist/development/core/cart/state.mjs +26 -0
  264. package/dist/development/core/cart/state.mjs.map +1 -1
  265. package/dist/development/core/collection/collection.d.mts +66 -51
  266. package/dist/development/core/collection/collection.mjs +16 -3
  267. package/dist/development/core/collection/collection.mjs.map +1 -1
  268. package/dist/development/core/collection/index.d.mts +1 -1
  269. package/dist/development/core/collection/reconciler.d.mts +27 -11
  270. package/dist/development/core/collection/reconciler.mjs +8 -4
  271. package/dist/development/core/collection/reconciler.mjs.map +1 -1
  272. package/dist/development/core/collection/state.d.mts +33 -23
  273. package/dist/development/core/collection/state.mjs +4 -3
  274. package/dist/development/core/collection/state.mjs.map +1 -1
  275. package/dist/development/core/collection/url.d.mts +71 -27
  276. package/dist/development/core/collection/url.mjs +63 -25
  277. package/dist/development/core/collection/url.mjs.map +1 -1
  278. package/dist/development/core/constants.mjs +1 -1
  279. package/dist/development/core/constants.mjs.map +1 -1
  280. package/dist/development/core/development.d.mts +5 -5
  281. package/dist/development/core/headers.mjs +2 -2
  282. package/dist/development/core/headers.mjs.map +1 -1
  283. package/dist/development/core/index.d.mts +4 -4
  284. package/dist/development/core/logging/logging.d.mts +14 -8
  285. package/dist/development/core/logging/logging.mjs +15 -10
  286. package/dist/development/core/logging/logging.mjs.map +1 -1
  287. package/dist/development/core/logging/types.d.mts +28 -11
  288. package/dist/development/core/logging/types.mjs.map +1 -1
  289. package/dist/development/core/money/format.d.mts +6 -4
  290. package/dist/development/core/money/format.mjs +24 -11
  291. package/dist/development/core/money/format.mjs.map +1 -1
  292. package/dist/development/core/money/types.d.mts +41 -25
  293. package/dist/development/core/observable.mjs +0 -3
  294. package/dist/development/core/observable.mjs.map +1 -1
  295. package/dist/development/core/predictive-search/form.d.mts +37 -6
  296. package/dist/development/core/predictive-search/form.mjs +21 -2
  297. package/dist/development/core/predictive-search/form.mjs.map +1 -1
  298. package/dist/development/core/predictive-search/index.d.mts +1 -1
  299. package/dist/development/core/predictive-search/queries.d.mts +32 -8
  300. package/dist/development/core/predictive-search/queries.mjs +11 -1
  301. package/dist/development/core/predictive-search/queries.mjs.map +1 -1
  302. package/dist/development/core/predictive-search/search.d.mts +41 -10
  303. package/dist/development/core/predictive-search/search.mjs +35 -0
  304. package/dist/development/core/predictive-search/search.mjs.map +1 -1
  305. package/dist/development/core/predictive-search/server-handlers.d.mts +28 -3
  306. package/dist/development/core/predictive-search/server-handlers.mjs +9 -3
  307. package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -1
  308. package/dist/development/core/predictive-search/store.d.mts +67 -24
  309. package/dist/development/core/predictive-search/store.mjs +12 -1
  310. package/dist/development/core/predictive-search/store.mjs.map +1 -1
  311. package/dist/development/core/predictive-search/url.d.mts +49 -8
  312. package/dist/development/core/predictive-search/url.mjs +17 -0
  313. package/dist/development/core/predictive-search/url.mjs.map +1 -1
  314. package/dist/development/core/product/form.d.mts +63 -16
  315. package/dist/development/core/product/form.mjs +8 -2
  316. package/dist/development/core/product/form.mjs.map +1 -1
  317. package/dist/development/core/product/options.d.mts +8 -10
  318. package/dist/development/core/product/options.mjs +8 -10
  319. package/dist/development/core/product/options.mjs.map +1 -1
  320. package/dist/development/core/product/product-form.d.mts +92 -12
  321. package/dist/development/core/product/product-form.mjs +56 -4
  322. package/dist/development/core/product/product-form.mjs.map +1 -1
  323. package/dist/development/core/product/state.d.mts +64 -0
  324. package/dist/development/core/product/url.d.mts +22 -10
  325. package/dist/development/core/product/url.mjs +10 -10
  326. package/dist/development/core/product/url.mjs.map +1 -1
  327. package/dist/development/core/request-context.d.mts +77 -22
  328. package/dist/development/core/request-context.mjs +17 -39
  329. package/dist/development/core/request-context.mjs.map +1 -1
  330. package/dist/development/core/request-routing/handle-shopify-redirects.d.mts +12 -4
  331. package/dist/development/core/request-routing/handle-shopify-redirects.mjs +9 -2
  332. package/dist/development/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
  333. package/dist/development/core/request-routing/handle-shopify-routes.development.d.mts +9 -1
  334. package/dist/development/core/request-routing/handle-shopify-routes.development.mjs +7 -0
  335. package/dist/development/core/request-routing/handle-shopify-routes.development.mjs.map +1 -1
  336. package/dist/development/core/request-routing/handle-shopify-routes.mjs +9 -6
  337. package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  338. package/dist/development/core/request-routing/interceptors/ajax-api.mjs +1 -1
  339. package/dist/development/core/request-routing/interceptors/api-proxy.mjs +1 -1
  340. package/dist/development/core/request-routing/interceptors/checkout.mjs +19 -6
  341. package/dist/development/core/request-routing/interceptors/checkout.mjs.map +1 -1
  342. package/dist/development/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
  343. package/dist/development/core/request-routing/interceptors/proxy.mjs +83 -32
  344. package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
  345. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
  346. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
  347. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
  348. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
  349. package/dist/development/core/request-routing/interceptors/ucp-mcp-proxy.mjs +1 -1
  350. package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
  351. package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
  352. package/dist/development/core/request-routing/interceptors/url-redirects.mjs +6 -1
  353. package/dist/development/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
  354. package/dist/development/core/request-routing/interceptors/well-known.mjs +2 -2
  355. package/dist/development/core/request-routing/interceptors/well-known.mjs.map +1 -1
  356. package/dist/development/core/request-routing/registered-routes.d.mts +12 -0
  357. package/dist/development/core/request-routing/registered-routes.mjs +12 -0
  358. package/dist/development/core/request-routing/registered-routes.mjs.map +1 -1
  359. package/dist/development/core/request-routing/route-types.d.mts +43 -18
  360. package/dist/development/core/shop-pay/shop-pay.d.mts +71 -35
  361. package/dist/development/core/shop-pay/shop-pay.mjs +36 -10
  362. package/dist/development/core/shop-pay/shop-pay.mjs.map +1 -1
  363. package/dist/development/core/shopify-scripts/analytics.mjs +2 -2
  364. package/dist/development/core/shopify-scripts/analytics.mjs.map +1 -1
  365. package/dist/development/core/shopify-scripts/constants.mjs +1 -1
  366. package/dist/development/core/shopify-scripts/constants.mjs.map +1 -1
  367. package/dist/development/core/shopify-scripts/global.mjs +5 -1
  368. package/dist/development/core/shopify-scripts/global.mjs.map +1 -1
  369. package/dist/development/core/shopify-scripts/index.d.mts +15 -5
  370. package/dist/development/core/shopify-scripts/index.mjs +15 -5
  371. package/dist/development/core/shopify-scripts/index.mjs.map +1 -1
  372. package/dist/development/core/shopify-scripts/initialize.d.mts +13 -4
  373. package/dist/development/core/shopify-scripts/initialize.mjs +22 -7
  374. package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -1
  375. package/dist/development/core/shopify-scripts/types.d.mts +58 -24
  376. package/dist/development/core/standard-routes/build.d.mts +11 -12
  377. package/dist/development/core/standard-routes/build.mjs +11 -12
  378. package/dist/development/core/standard-routes/build.mjs.map +1 -1
  379. package/dist/development/core/standard-routes/types.d.mts +30 -24
  380. package/dist/development/core/url.mjs +21 -2
  381. package/dist/development/core/url.mjs.map +1 -1
  382. package/dist/development/customer-account/session.d.mts +65 -18
  383. package/dist/development/customer-account/session.mjs +20 -0
  384. package/dist/development/customer-account/session.mjs.map +1 -1
  385. package/dist/development/globals.d.mts +22 -15
  386. package/dist/development/graphql/generated/graphql-env.d.mts +459 -16
  387. package/dist/development/graphql/generated/storefront-api-types.d.mts +128 -2
  388. package/dist/development/graphql/graphql.d.mts +39 -5
  389. package/dist/development/graphql/graphql.mjs +29 -0
  390. package/dist/development/graphql/graphql.mjs.map +1 -1
  391. package/dist/development/graphql/type-resolver.d.mts +21 -0
  392. package/dist/development/react/cart.d.mts +144 -27
  393. package/dist/development/react/cart.mjs +152 -5
  394. package/dist/development/react/cart.mjs.map +1 -1
  395. package/dist/development/react/collection.d.mts +42 -26
  396. package/dist/development/react/collection.mjs +27 -7
  397. package/dist/development/react/collection.mjs.map +1 -1
  398. package/dist/development/react/index.d.mts +7 -5
  399. package/dist/development/react/index.mjs +2 -2
  400. package/dist/development/react/predictive-search.d.mts +85 -9
  401. package/dist/development/react/predictive-search.mjs +56 -12
  402. package/dist/development/react/predictive-search.mjs.map +1 -1
  403. package/dist/development/react/product.d.mts +45 -15
  404. package/dist/development/react/product.mjs +18 -13
  405. package/dist/development/react/product.mjs.map +1 -1
  406. package/dist/development/react/shop-pay.d.mts +15 -0
  407. package/dist/development/react/shop-pay.mjs +9 -0
  408. package/dist/development/react/shop-pay.mjs.map +1 -1
  409. package/dist/development/react/shopify-scripts.d.mts +23 -2
  410. package/dist/development/react/shopify-scripts.mjs +10 -0
  411. package/dist/development/react/shopify-scripts.mjs.map +1 -1
  412. package/dist/development/vue/cart.d.mts +94 -10
  413. package/dist/development/vue/cart.mjs +115 -3
  414. package/dist/development/vue/cart.mjs.map +1 -1
  415. package/dist/development/vue/collection.d.mts +91 -5
  416. package/dist/development/vue/collection.mjs +60 -0
  417. package/dist/development/vue/collection.mjs.map +1 -1
  418. package/dist/development/vue/index.d.mts +7 -5
  419. package/dist/development/vue/index.mjs +2 -2
  420. package/dist/development/vue/predictive-search.d.mts +48 -6
  421. package/dist/development/vue/predictive-search.mjs +27 -0
  422. package/dist/development/vue/predictive-search.mjs.map +1 -1
  423. package/dist/development/vue/product.d.mts +18 -13
  424. package/dist/development/vue/product.mjs +11 -11
  425. package/dist/development/vue/product.mjs.map +1 -1
  426. package/dist/development/vue/shop-pay.d.mts +14 -17
  427. package/dist/development/vue/shop-pay.mjs +8 -0
  428. package/dist/development/vue/shop-pay.mjs.map +1 -1
  429. package/dist/development/vue/shopify-scripts.d.mts +32 -3
  430. package/dist/development/vue/shopify-scripts.mjs +7 -0
  431. package/dist/development/vue/shopify-scripts.mjs.map +1 -1
  432. package/dist/globals.d.mts +22 -15
  433. package/dist/graphql/generated/customer-account-graphql-env.d.mts +1717 -1090
  434. package/dist/graphql/generated/graphql-env.d.mts +459 -16
  435. package/dist/graphql/generated/storefront-api-types.d.mts +128 -2
  436. package/dist/graphql/graphql.d.mts +39 -5
  437. package/dist/graphql/graphql.mjs +29 -0
  438. package/dist/graphql/graphql.mjs.map +1 -1
  439. package/dist/graphql/type-resolver.d.mts +21 -0
  440. package/dist/react/cart.d.mts +144 -27
  441. package/dist/react/cart.mjs +152 -5
  442. package/dist/react/cart.mjs.map +1 -1
  443. package/dist/react/collection.d.mts +42 -26
  444. package/dist/react/collection.mjs +27 -7
  445. package/dist/react/collection.mjs.map +1 -1
  446. package/dist/react/index.d.mts +7 -5
  447. package/dist/react/index.mjs +2 -2
  448. package/dist/react/predictive-search.d.mts +85 -9
  449. package/dist/react/predictive-search.mjs +56 -12
  450. package/dist/react/predictive-search.mjs.map +1 -1
  451. package/dist/react/product.d.mts +45 -15
  452. package/dist/react/product.mjs +18 -13
  453. package/dist/react/product.mjs.map +1 -1
  454. package/dist/react/shop-pay.d.mts +15 -0
  455. package/dist/react/shop-pay.mjs +9 -0
  456. package/dist/react/shop-pay.mjs.map +1 -1
  457. package/dist/react/shopify-scripts.d.mts +23 -2
  458. package/dist/react/shopify-scripts.mjs +10 -0
  459. package/dist/react/shopify-scripts.mjs.map +1 -1
  460. package/dist/storefront-api-types.d.ts +165 -20
  461. package/dist/storefront.schema.json +782 -57
  462. package/dist/ts-plugin/index.cjs +6 -0
  463. package/dist/ts-plugin/index.cjs.map +1 -1
  464. package/dist/ts-plugin/index.d.cts +6 -0
  465. package/dist/vite/customer-account.mjs +191 -0
  466. package/dist/vite/customer-account.mjs.map +1 -0
  467. package/dist/vite/local-https.d.mts +51 -17
  468. package/dist/vite/local-https.mjs +39 -34
  469. package/dist/vite/local-https.mjs.map +1 -1
  470. package/dist/vite/mkcert.mjs +1 -1
  471. package/dist/vue/cart.d.mts +94 -10
  472. package/dist/vue/cart.mjs +115 -3
  473. package/dist/vue/cart.mjs.map +1 -1
  474. package/dist/vue/collection.d.mts +91 -5
  475. package/dist/vue/collection.mjs +60 -0
  476. package/dist/vue/collection.mjs.map +1 -1
  477. package/dist/vue/index.d.mts +7 -5
  478. package/dist/vue/index.mjs +2 -2
  479. package/dist/vue/predictive-search.d.mts +48 -6
  480. package/dist/vue/predictive-search.mjs +27 -0
  481. package/dist/vue/predictive-search.mjs.map +1 -1
  482. package/dist/vue/product.d.mts +18 -13
  483. package/dist/vue/product.mjs +11 -11
  484. package/dist/vue/product.mjs.map +1 -1
  485. package/dist/vue/shop-pay.d.mts +14 -17
  486. package/dist/vue/shop-pay.mjs +8 -0
  487. package/dist/vue/shop-pay.mjs.map +1 -1
  488. package/dist/vue/shopify-scripts.d.mts +32 -3
  489. package/dist/vue/shopify-scripts.mjs +7 -0
  490. package/dist/vue/shopify-scripts.mjs.map +1 -1
  491. package/package.json +7 -2
  492. package/skills/hydrogen-analytics/SKILL.md +7 -6
  493. package/skills/hydrogen-analytics/references/custom-consent.md +52 -0
  494. package/skills/hydrogen-analytics/references/react.md +1 -1
  495. package/skills/hydrogen-cart-ui/SKILL.md +28 -2
  496. package/skills/hydrogen-collection-browser/SKILL.md +9 -1
  497. package/skills/hydrogen-collection-browser/references/nextjs.md +1 -1
  498. package/skills/hydrogen-collection-browser/references/nuxt.md +1 -1
  499. package/skills/hydrogen-collection-browser/references/react.md +151 -32
  500. package/skills/hydrogen-customer-account/SKILL.md +5 -3
  501. package/skills/hydrogen-local-https/SKILL.md +22 -7
  502. package/skills/hydrogen-request-handlers/SKILL.md +23 -13
  503. package/skills/hydrogen-request-handlers/references/nuxt.md +0 -6
  504. package/skills/hydrogen-setup/references/analytics.md +37 -30
  505. package/skills/hydrogen-setup/steps/1-inspect-app.md +1 -1
  506. package/skills/hydrogen-setup/steps/2-scaffold.md +1 -1
  507. package/skills/hydrogen-setup/steps/6-product-detail-page.md +21 -11
  508. package/skills/hydrogen-smoke-test/SKILL.md +2 -2
  509. package/skills/hydrogen-storefront-client/SKILL.md +1 -3
  510. package/skills/hydrogen-storefront-client/references/caching.md +2 -0
  511. package/skills/hydrogen-variant-form/SKILL.md +25 -7
  512. package/skills/hydrogen-variant-form/references/nextjs.md +39 -4
  513. package/skills/hydrogen-variant-form/references/nuxt.md +83 -42
  514. package/skills/hydrogen-variant-form/references/react.md +45 -9
  515. package/ts-plugin/package.json +5 -0
  516. package/dist/core/shopify-scripts/deprecated-cookies.mjs +0 -98
  517. package/dist/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
  518. package/dist/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
  519. package/dist/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
  520. package/dist/core/shopify-scripts/utils/tracking-values.mjs +0 -65
  521. package/dist/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
  522. package/dist/core/shopify-scripts/utils/uuid.mjs +0 -17
  523. package/dist/core/shopify-scripts/utils/uuid.mjs.map +0 -1
  524. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs +0 -98
  525. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
  526. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
  527. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
  528. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs +0 -65
  529. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
  530. package/dist/development/core/shopify-scripts/utils/uuid.mjs +0 -17
  531. package/dist/development/core/shopify-scripts/utils/uuid.mjs.map +0 -1
package/README.md CHANGED
@@ -100,13 +100,13 @@ const isLoggedIn = await customerSession.isLoggedIn(
100
100
  );
101
101
  ```
102
102
 
103
- Customer Account OAuth methods require a public HTTPS origin. The writable session manager should expose the request origin; explicit `origin` options are only needed as overrides. For local development, use a tunnel or trusted local HTTPS through `localHttps` from `@shopify/hydrogen/vite`, and pass the framework's canonical request URL rather than an untrusted forwarded host.
103
+ Customer Account OAuth methods require a public HTTPS origin. The writable session manager should expose the request origin; explicit `origin` options are only needed as overrides. For local development, use a tunnel or trusted local HTTPS through `localHttps` from `@shopify/hydrogen/vite`, which provisions certificates and uses Shopify CLI to push Customer Account URLs outside CI. Pass the framework's canonical request URL rather than an untrusted forwarded host.
104
104
 
105
105
  Pass `customerSession` to `createCartServerHandlers({customerSession})` to associate newly created carts with the current customer when the session has a usable access token or successfully refreshed access token, and mark checkout URLs in authenticated cart GET responses with `logged_in=true`.
106
106
 
107
107
  Use `createCustomerAccountServerHandlers({customerSession})` with `handleShopifyRoutes` to install the default `GET /account/login`, `GET /account/authorize`, `GET /account/refresh`, and `POST /account/logout` handlers. Pass those cart server handlers as `cartServerHandlers` to also keep the browser cart's buyer identity in step with the customer session: authorization and refresh attach the customer to the cart, a definitive refresh rejection and logout detach it. Sync is best-effort — a failed cart mutation never blocks the route's redirect; a failed detach during logout or definitive refresh rejection expires the cart cookie instead. `cartServerHandlers` requires the `customerSession` returned by `createCustomerSession` shared by both handler groups. Pass the same request-scoped `requestContext` and `sessionManager` into `handleShopifyRoutes` once alongside the `storefrontClient`. Session managers can be read-only for `isLoggedIn()` / `getAccessToken()` and writable for cart POST handlers configured with `customerSession`, `getOrRefreshAccessToken()`, `prepareLoginUrl()`, `handleOAuthCallback()`, `logout()`, and registered account handlers. `isLoggedIn()` is a read-only UI/session-presence check: it returns true for a usable access token or a refresh token that can attempt to restore one later. `getAccessToken()` still returns only a currently usable access token and never refreshes.
108
108
 
109
- The login handler respects a sanitized `return_to` search parameter and otherwise redirects back to `defaultPostLoginRedirectPathname` (default `/`). The logout handler redirects through Shopify using `postLogoutRedirectUri` (default `/`) when an ID token is present; otherwise, it redirects directly to `postLogoutRedirectUri`. Apps can still implement their own framework routes when they need custom behavior.
109
+ The login handler respects a `return_to` search parameter when it is a same-origin path or URL, such as `/account/orders`, and otherwise redirects back to `defaultPostLoginRedirectPathname` (default `/`). The logout handler redirects through Shopify using `postLogoutRedirectUri` (default `/`) when an ID token is present; otherwise, it redirects directly to `postLogoutRedirectUri`. Apps can still implement their own framework routes when they need custom behavior.
110
110
 
111
111
  When a writable method can update cookie-backed session state, commit once while finalizing the response and append headers from `await sessionManager.commit?.()` there. After committing session headers, call `requestContext.applyResponseHeaders(response.headers)`. Customer Account session reads and GraphQL calls mark the request context private so the finalizer can prevent public/CDN caching of personalized responses.
112
112
 
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
3
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { chmod, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
6
- import { homedir } from "node:os";
7
- import { dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
6
+ import { homedir, tmpdir } from "node:os";
7
+ import { basename, dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
8
8
  import { createInterface } from "node:readline/promises";
9
9
  import { spawn } from "node:child_process";
10
10
  import { createHash, randomUUID } from "node:crypto";
@@ -46,9 +46,9 @@ function writeToConsole(level, message, context) {
46
46
  console[CONSOLE_METHODS[level]](...args);
47
47
  }
48
48
  /**
49
- * Built-in sink: writes `[hydrogen:<level>:<scope>] <message>` to a
50
- * level-specific `console` method, followed by `context.error` and any extra
51
- * context fields. `trace` uses `console.debug`; `fatal` uses `console.error`.
49
+ * The built-in logger. Writes each entry to the matching console method, with a
50
+ * `[hydrogen:<level>:<scope>]` prefix when the entry has a scope, followed by the error and any
51
+ * extra context fields. Trace entries use `console.debug`, and fatal entries use `console.error`.
52
52
  */
53
53
  const consoleLogger = {
54
54
  trace: (message, context) => writeToConsole("trace", message, context),
@@ -243,15 +243,27 @@ async function uninstallCertificateAuthority() {
243
243
 
244
244
  //#endregion
245
245
  //#region src/vite/local-https.ts
246
+ /**
247
+ * Default host, `local.tryhydrogen.dev`, and port, `5173`, for local HTTPS. Pass the values to frameworks that set their own dev server host and port, such as Astro.
248
+ *
249
+ * @publicDocs
250
+ */
246
251
  const LOCAL_HTTPS_DEFAULTS = {
247
252
  host: "local.tryhydrogen.dev",
248
253
  port: 5173
249
254
  };
250
255
  /**
251
- * Downloads a pinned, checksum-verified mkcert release and generates the
252
- * trusted local certificate files when they do not exist yet. The Vite plugin
253
- * runs this automatically on `vite dev`; call it directly for frameworks that
254
- * read certificate paths before Vite starts or from setup scripts.
256
+ * Creates trusted certificate files for local HTTPS. Call the function from a setup script, or
257
+ * before you start a framework that reads certificate paths when its config loads, such as Nuxt.
258
+ *
259
+ * When both files exist, the function returns their paths and changes nothing. Otherwise, the
260
+ * function downloads a pinned, checksum-verified mkcert release, adds the mkcert certificate
261
+ * authority to your system trust store, and creates the certificate and private key. mkcert can
262
+ * ask for your password.
263
+ *
264
+ * @param options Host and certificate paths. Each option has a Hydrogen default.
265
+ * @returns The host and the absolute paths to the certificate and the private key.
266
+ * @publicDocs
255
267
  */
256
268
  async function provisionLocalHttps(options = {}) {
257
269
  const settings = resolveLocalHttpsSettings({
@@ -444,6 +456,24 @@ function isObjectRecord(value) {
444
456
  return typeof value === "object" && value !== null && !Array.isArray(value);
445
457
  }
446
458
 
459
+ //#endregion
460
+ //#region src/cli/terminal.ts
461
+ function canPromptInTerminal(env = process.env) {
462
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY) && !env.CI;
463
+ }
464
+ async function confirmInTerminal(question) {
465
+ const readline = createInterface({
466
+ input: process.stdin,
467
+ output: process.stdout
468
+ });
469
+ try {
470
+ const answer = (await readline.question(`${question} [Y/n] `)).trim().toLowerCase();
471
+ return answer === "" || answer === "y" || answer === "yes";
472
+ } finally {
473
+ readline.close();
474
+ }
475
+ }
476
+
447
477
  //#endregion
448
478
  //#region src/cli/skills.ts
449
479
  const PACKAGE_NAME$1 = "@shopify/hydrogen";
@@ -681,21 +711,6 @@ async function planOrphans(plans, version, confirm) {
681
711
  });
682
712
  }
683
713
  }
684
- function canPromptInTerminal() {
685
- return Boolean(process.stdin.isTTY && process.stdout.isTTY) && !process.env.CI;
686
- }
687
- async function confirmInTerminal(question) {
688
- const readline = createInterface({
689
- input: process.stdin,
690
- output: process.stdout
691
- });
692
- try {
693
- const answer = (await readline.question(`${question} [Y/n] `)).trim().toLowerCase();
694
- return answer === "" || answer === "y" || answer === "yes";
695
- } finally {
696
- readline.close();
697
- }
698
- }
699
714
  /** Never removes user edits without an answer, so no TTY means keep. */
700
715
  function confirmByDefault(question) {
701
716
  return canPromptInTerminal() ? confirmInTerminal(question) : Promise.resolve(false);
@@ -931,13 +946,14 @@ function parsePackageManager(value) {
931
946
  if (value.startsWith(`${packageManager}/`)) return packageManager;
932
947
  }
933
948
  }
949
+ function detectPackageManagerFromEnv(env) {
950
+ return parsePackageManager(env.npm_config_user_agent);
951
+ }
934
952
  function detectPackageManager(appRoot, packageJson, env) {
935
953
  const packageJsonManager = parsePackageManager(packageJson.packageManager);
936
954
  if (packageJsonManager) return packageJsonManager;
937
955
  for (const [packageManager, lockfile] of PACKAGE_MANAGER_LOCKFILES) if (existsSync(join(appRoot, lockfile))) return packageManager;
938
- const userAgentManager = parsePackageManager(env.npm_config_user_agent);
939
- if (userAgentManager) return userAgentManager;
940
- throw new Error("Could not detect a package manager. Add a packageManager field or a lockfile before running setup.");
956
+ return detectPackageManagerFromEnv(env) ?? "npm";
941
957
  }
942
958
  function hasHydrogenDependency(packageJson) {
943
959
  return Boolean(packageJson.dependencies?.[PACKAGE_NAME] ?? packageJson.devDependencies?.[PACKAGE_NAME]);
@@ -961,17 +977,115 @@ function spawnRunCommand(command, args, options) {
961
977
  });
962
978
  });
963
979
  }
964
- async function setupHydrogen(options = {}) {
965
- const appRoot = options.cwd ?? process.cwd();
966
- const env = options.env ?? process.env;
967
- const runCommand = options.runCommand ?? spawnRunCommand;
968
- const log = options.log ?? console.log;
980
+ const TEMPLATE_TARBALL_URL = "https://codeload.github.com/Shopify/hydrogen/tar.gz/dist-preview";
981
+ const TEMPLATE_TARBALL_PREFIX = "hydrogen-dist-preview/templates/react-router/";
982
+ const TEMPLATE_TARBALL_STRIP_COMPONENTS = TEMPLATE_TARBALL_PREFIX.split("/").filter(Boolean).length;
983
+ async function promptSetupChoice(log) {
984
+ log("\nNo project found in this directory.\n");
985
+ log(" 1) Create a new Hydrogen storefront (React Router)");
986
+ log(" 2) Just install Hydrogen skills\n");
987
+ const readline = createInterface({
988
+ input: process.stdin,
989
+ output: process.stdout
990
+ });
991
+ try {
992
+ while (true) {
993
+ const answer = (await readline.question("Your choice [1] ")).trim();
994
+ if (answer === "" || answer === "1") return "scaffold";
995
+ if (answer === "2") return "skills";
996
+ log("Please enter \"1\" or \"2\".");
997
+ }
998
+ } finally {
999
+ readline.close();
1000
+ }
1001
+ }
1002
+ function hasPackageJson(appRoot) {
1003
+ return existsSync(join(appRoot, PACKAGE_JSON_FILE_NAME));
1004
+ }
1005
+ async function downloadAndExtractTemplate(appRoot, runCommand) {
1006
+ const response = await fetch(TEMPLATE_TARBALL_URL, { signal: AbortSignal.timeout(3e4) });
1007
+ if (!response.ok) throw new Error(`Failed to download template (${response.status}). Check your internet connection.`);
1008
+ const tempDir = mkdtempSync(join(tmpdir(), "hydrogen-template-"));
1009
+ const tempFile = "template.tar.gz";
1010
+ try {
1011
+ writeFileSync(join(tempDir, tempFile), Buffer.from(await response.arrayBuffer()));
1012
+ await runCommand("tar", [
1013
+ "xzf",
1014
+ tempFile,
1015
+ `--strip-components=${TEMPLATE_TARBALL_STRIP_COMPONENTS}`,
1016
+ "-C",
1017
+ appRoot,
1018
+ TEMPLATE_TARBALL_PREFIX
1019
+ ], { cwd: tempDir });
1020
+ } finally {
1021
+ rmSync(tempDir, {
1022
+ recursive: true,
1023
+ force: true
1024
+ });
1025
+ }
1026
+ }
1027
+ function updateScaffoldedPackageJson(appRoot) {
1028
+ const packageJsonPath = join(appRoot, PACKAGE_JSON_FILE_NAME);
1029
+ const raw = readFileSync(packageJsonPath, "utf8");
1030
+ const parsed = JSON.parse(raw);
1031
+ if (!isObjectRecord(parsed)) return;
1032
+ parsed.name = basename(appRoot);
1033
+ delete parsed.private;
1034
+ delete parsed.packageManager;
1035
+ writeFileSync(packageJsonPath, JSON.stringify(parsed, null, 2) + "\n");
1036
+ }
1037
+ async function scaffoldTemplate(appRoot, env, runCommand, log) {
1038
+ log("Downloading Hydrogen template...");
1039
+ await downloadAndExtractTemplate(appRoot, runCommand);
1040
+ updateScaffoldedPackageJson(appRoot);
1041
+ const packageManager = detectPackageManagerFromEnv(env) ?? "npm";
1042
+ log(`Installing dependencies with ${packageManager}...`);
1043
+ try {
1044
+ await runCommand(packageManager, ["install"], { cwd: appRoot });
1045
+ } catch (error) {
1046
+ throw new Error(`Template was created but dependency install failed. Run \`${packageManager} install\` or re-run \`npx @shopify/hydrogen setup\` in ${appRoot} to finish.`, { cause: error });
1047
+ }
1048
+ return packageManager;
1049
+ }
1050
+ async function ensureHydrogenInstalled(appRoot, env, runCommand, log) {
969
1051
  const packageJson = readPackageJson(appRoot);
970
- if (!hasHydrogenDependency(packageJson)) {
1052
+ if (hasHydrogenDependency(packageJson) && getInstalledPackageRoot(appRoot)) return;
1053
+ if (hasHydrogenDependency(packageJson)) {
971
1054
  const packageManager = detectPackageManager(appRoot, packageJson, env);
972
- log(`Installing ${PACKAGE_NAME} with ${packageManager}...`);
973
- await installHydrogen(appRoot, packageManager, runCommand);
1055
+ log(`${PACKAGE_NAME} is declared but not installed. Running ${packageManager} install...`);
1056
+ await runCommand(packageManager, ["install"], { cwd: appRoot });
1057
+ return;
974
1058
  }
1059
+ const packageManager = detectPackageManager(appRoot, packageJson, env);
1060
+ log(`Installing ${PACKAGE_NAME} with ${packageManager}...`);
1061
+ await installHydrogen(appRoot, packageManager, runCommand);
1062
+ }
1063
+ function resolveSetupOptions(options) {
1064
+ const env = options.env ?? process.env;
1065
+ return {
1066
+ appRoot: options.cwd ?? process.cwd(),
1067
+ env,
1068
+ runCommand: options.runCommand ?? spawnRunCommand,
1069
+ log: options.log ?? console.log,
1070
+ prompt: options.prompt ?? (canPromptInTerminal(env) ? promptSetupChoice : void 0)
1071
+ };
1072
+ }
1073
+ const IGNORED_DIRECTORY_ENTRIES = new Set([".git", ".DS_Store"]);
1074
+ function isEmptyDirectory(dir) {
1075
+ return readdirSync(dir).every((entry) => IGNORED_DIRECTORY_ENTRIES.has(entry));
1076
+ }
1077
+ async function setupHydrogen(options = {}) {
1078
+ const { appRoot, env, runCommand, log, prompt } = resolveSetupOptions(options);
1079
+ if (hasPackageJson(appRoot)) await ensureHydrogenInstalled(appRoot, env, runCommand, log);
1080
+ else if (isEmptyDirectory(appRoot)) {
1081
+ let choice;
1082
+ if (prompt) choice = await prompt(log);
1083
+ else {
1084
+ log("No package.json found and no terminal to prompt; installing skills only.");
1085
+ choice = "skills";
1086
+ }
1087
+ if (choice === "scaffold") log(`\nHydrogen storefront created. Run \`${await scaffoldTemplate(appRoot, env, runCommand, log)} run dev\` to start (uses mock.shop by default).\n`);
1088
+ } else throw new Error("No package.json found. Run this command from your project root.");
975
1089
  await syncSkills({
976
1090
  force: options.force,
977
1091
  cwd: appRoot,
@@ -5,26 +5,29 @@ import { ClientType, CreateStorefrontClientArgs, StorefrontClient } from "./type
5
5
 
6
6
  //#region src/client/client.d.ts
7
7
  /**
8
- * Creates a type-safe Storefront API client.
8
+ * Creates a Storefront API client that runs typed queries and mutations against your store.
9
9
  *
10
- * Pick a `type` based on where your code runs and whether buyer context is available:
10
+ * Set `type` based on where your code runs and whether customer context is available:
11
11
  *
12
- * | | public | private | private_no_buyer_context |
13
- * |-----------------|--------------------------------|-------------------------------|--------------------------|
14
- * | Runs in | Browser or mobile | Server (SSR) | Server (background) |
15
- * | Token | Public access token, or none | Private access token | Private access token |
16
- * | Buyer context | Browser request context | You forward trusted `buyerIp` | None |
17
- * | Best for | Client-side fetches | SSR with buyer context | Webhooks, background jobs |
12
+ * | | `public` | `private` | `private_no_buyer_context` |
13
+ * |------------------|------------------------------|-------------------------------|------------------------------|
14
+ * | Runs in | Browser or mobile | Server-side rendering | Background server code |
15
+ * | Token | Public access token, or none | Private access token | Private access token |
16
+ * | Customer context | Browser request context | You forward trusted `buyerIp` | None |
17
+ * | Best for | Client-side fetches | SSR with customer context | Webhooks, background jobs |
18
18
  *
19
- * Pass `requestContext` so the client's SFAPI requests carry request-scoped
20
- * headers, follow the incoming request's abort signal, and read the resolved
21
- * `i18n` used for Storefront API variable injection and localized routes.
19
+ * Pass a request context from `createShopifyRequestContext`. The client forwards the incoming
20
+ * request's cookies and `Sec-GPC` header to the Storefront API, and fills `$country`
21
+ * and `$language` variables from the context's locale. The client reads the request headers once,
22
+ * when you create the client. Create a client for each incoming request, and create a client at
23
+ * module scope only with a static request context.
22
24
  *
23
- * Token-based access is required for some Storefront API fields, including
24
- * product tags, metaobjects, metafields, menus, and customers. Use a private
25
- * client only in trusted server code where the token cannot leak to browsers.
25
+ * Keep private clients in server code, where the token can't reach browsers. The function throws an
26
+ * error when you create either private client type in a browser. The function throws a `TypeError`
27
+ * when the request context of a `private` client has no `buyerIp`.
26
28
  *
27
- * @see {@link https://shopify.dev/docs/api/storefront#authentication | Storefront API authentication}
29
+ * @see [Storefront API authentication](https://shopify.dev/docs/api/storefront#authentication)
30
+ * @publicDocs
28
31
  */
29
32
  declare function createStorefrontClient<const Type extends ClientType, const RequestContext extends ShopifyRequestContext, const CacheConfig extends CacheInstance | undefined = undefined>(args: CreateStorefrontClientArgs<RequestContext, Type, CacheConfig>): StorefrontClient<[CacheConfig] extends [CacheInstance] ? {
30
33
  cache?: CachingStrategy;
@@ -1,8 +1,8 @@
1
+ import { normalizeStoreDomain } from "../core/url.mjs";
1
2
  import { getHydrogenCacheStatus } from "../core/cache/cache-status.mjs";
2
3
  import { createFetchWithCache } from "../core/cache/fetch-with-cache.mjs";
3
4
  import { DEFAULT_TIMEOUT_IN_MS, STOREFRONT_API_VERSION } from "../core/constants.mjs";
4
5
  import { HYDROGEN_VERSION_HEADER, REQUEST_GROUP_ID_HEADER, SDK_VARIANT_HEADER, SDK_VARIANT_SOURCE_HEADER, SDK_VERSION_HEADER, SHOPIFY_STOREFRONT_S_HEADER, SHOPIFY_STOREFRONT_Y_HEADER, SHOPIFY_UNIQUE_TOKEN_HEADER, SHOPIFY_VISIT_TOKEN_HEADER, STOREFRONT_ACCESS_TOKEN_HEADER, STOREFRONT_BUYER_IP_HEADER, STOREFRONT_ID_HEADER, STOREFRONT_PRIVATE_TOKEN_HEADER } from "../core/headers.mjs";
5
- import { normalizeStoreDomain } from "../core/url.mjs";
6
6
  import { StorefrontApiError, StorefrontTimeoutError } from "./errors.mjs";
7
7
 
8
8
  //#region src/client/client.ts
@@ -38,11 +38,15 @@ function withStorefrontClientCache(client, options, strategy, shouldCacheResult)
38
38
  [SHOULD_CACHE_RESULT]: shouldCacheResult
39
39
  };
40
40
  }
41
+ /**
42
+ * @param args The client type, request context, and store configuration for the new client.
43
+ * @returns The client, with a `graphql()` method that runs queries and mutations.
44
+ */
41
45
  function createStorefrontClient(args) {
42
46
  const { config, requestContext, type: clientType } = args;
43
47
  if (!config.storeDomain) throw new Error("storeDomain is required and must be non-empty");
44
48
  const storeUrl = normalizeStoreDomain(config.storeDomain);
45
- const apiUrl = `${storeUrl}/api/${config.apiVersion ?? "2026-04"}/graphql.json`;
49
+ const apiUrl = `${storeUrl}/api/${config.apiVersion ?? "2026-10"}/graphql.json`;
46
50
  const originFetch = config.fetch ?? globalThis.fetch;
47
51
  if (typeof originFetch !== "function") throw new Error("No fetch function available. Pass a fetch option or ensure globalThis.fetch exists.");
48
52
  const resolvedFetch = config.cache ? createFetchWithCache({
@@ -104,7 +108,7 @@ function createStorefrontClient(args) {
104
108
  }),
105
109
  signal: externalSignals.length > 0 ? AbortSignal.any(externalSignals) : void 0
106
110
  };
107
- const response = await resolvedFetch(apiUrl, init, createStorefrontCacheOptions(apiUrl, init, queryText, opts.cache, opts[SHOULD_CACHE_RESULT]));
111
+ const response = await resolvedFetch(apiUrl, init, createStorefrontCacheOptions(apiUrl, init, queryText, opts.cache, opts[SHOULD_CACHE_RESULT], timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT_IN_MS));
108
112
  responseHeaders = response.headers;
109
113
  if (getHydrogenCacheStatus(responseHeaders) !== "hit") requestContext.captureSubrequestHeaders(responseHeaders);
110
114
  requestId = responseHeaders.get("x-request-id") ?? void 0;
@@ -174,7 +178,7 @@ function buildVariables(queryText, userVariables, resolvedI18n) {
174
178
  if (LANGUAGE_VAR_RE.test(queryText)) variables.language = resolvedI18n.language;
175
179
  return variables;
176
180
  }
177
- function createStorefrontCacheOptions(apiUrl, init, queryText, strategy, shouldCacheResult) {
181
+ function createStorefrontCacheOptions(apiUrl, init, queryText, strategy, shouldCacheResult, backgroundTimeoutInMs) {
178
182
  if (!strategy) return void 0;
179
183
  if (/\bmutation\b/.test(queryText)) return void 0;
180
184
  return {
@@ -185,7 +189,8 @@ function createStorefrontCacheOptions(apiUrl, init, queryText, strategy, shouldC
185
189
  const body = await json().catch(() => void 0);
186
190
  if (typeof body !== "object" || body == null || Array.isArray(body) || "errors" in body) return false;
187
191
  return shouldCacheResult?.(body) ?? true;
188
- }
192
+ },
193
+ backgroundSignal: () => AbortSignal.timeout(backgroundTimeoutInMs)
189
194
  };
190
195
  }
191
196
  function createStorefrontCacheKey(apiUrl, init) {
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../../src/client/client.ts"],"sourcesContent":["import type { TadaDocumentNode } from \"gql.tada\";\n\nimport { getHydrogenCacheStatus } from \"../core/cache/cache-status\";\nimport { createFetchWithCache } from \"../core/cache/fetch-with-cache\";\nimport type { FetchCacheOptions, FetchCacheResponseContext } from \"../core/cache/fetch-with-cache\";\nimport type { CacheInstance } from \"../core/cache/run-with-cache\";\nimport type { CachingStrategy } from \"../core/cache/strategies\";\nimport { DEFAULT_TIMEOUT_IN_MS, STOREFRONT_API_VERSION } from \"../core/constants\";\nimport {\n HYDROGEN_VERSION_HEADER,\n REQUEST_GROUP_ID_HEADER,\n SDK_VARIANT_HEADER,\n SDK_VARIANT_SOURCE_HEADER,\n SDK_VERSION_HEADER,\n SHOPIFY_STOREFRONT_S_HEADER,\n SHOPIFY_STOREFRONT_Y_HEADER,\n STOREFRONT_ACCESS_TOKEN_HEADER,\n STOREFRONT_BUYER_IP_HEADER,\n STOREFRONT_ID_HEADER,\n STOREFRONT_PRIVATE_TOKEN_HEADER,\n SHOPIFY_UNIQUE_TOKEN_HEADER,\n SHOPIFY_VISIT_TOKEN_HEADER,\n} from \"../core/headers\";\nimport type { I18nConfig, ShopifyRequestContext } from \"../core/request-context\";\nimport { normalizeStoreDomain } from \"../core/url\";\nimport type { AnyStorefrontQueryString } from \"../graphql\";\nimport { StorefrontApiError, StorefrontTimeoutError } from \"./errors\";\nimport type {\n ClientType,\n CreateStorefrontClientArgs,\n GraphQLFormattedError,\n StorefrontClient,\n StorefrontGraphqlResult,\n} from \"./types\";\n\ntype DocLike = TadaDocumentNode<unknown, unknown> | AnyStorefrontQueryString;\ntype FetchInput = Parameters<typeof globalThis.fetch>[0];\ntype FetchInit = Parameters<typeof globalThis.fetch>[1];\ntype ResolvedStorefrontFetch = (\n input: FetchInput,\n init: FetchInit,\n cacheOptions: FetchCacheOptions | undefined,\n) => Promise<Response>;\n\nconst COUNTRY_VAR_RE = /\\$country\\s*:/;\nconst LANGUAGE_VAR_RE = /\\$language\\s*:/;\nconst REQUEST_CACHE_KEY_HEADERS = new Set([\n \"content-type\",\n \"user-agent\",\n SDK_VARIANT_HEADER.toLowerCase(),\n SDK_VARIANT_SOURCE_HEADER.toLowerCase(),\n SDK_VERSION_HEADER.toLowerCase(),\n HYDROGEN_VERSION_HEADER.toLowerCase(),\n STOREFRONT_ACCESS_TOKEN_HEADER.toLowerCase(),\n STOREFRONT_PRIVATE_TOKEN_HEADER.toLowerCase(),\n]);\nconst REQUEST_IDENTITY_HEADERS = new Set([\n \"cookie\",\n REQUEST_GROUP_ID_HEADER.toLowerCase(),\n STOREFRONT_BUYER_IP_HEADER.toLowerCase(),\n SHOPIFY_UNIQUE_TOKEN_HEADER.toLowerCase(),\n SHOPIFY_VISIT_TOKEN_HEADER.toLowerCase(),\n SHOPIFY_STOREFRONT_Y_HEADER.toLowerCase(),\n SHOPIFY_STOREFRONT_S_HEADER.toLowerCase(),\n]);\nconst CACHE_ENABLED_GRAPHQL_CLIENTS = new WeakSet<StorefrontClient[\"graphql\"]>();\nconst SHOULD_CACHE_RESULT = Symbol();\n\ntype ShouldCacheResult = (body: object) => boolean;\n\nclass StorefrontCacheConfigError extends Error {}\n\nexport function withStorefrontClientCache<TOptions extends object>(\n client: Pick<StorefrontClient, \"graphql\">,\n options: TOptions,\n strategy: CachingStrategy,\n shouldCacheResult?: ShouldCacheResult,\n): TOptions {\n if (!CACHE_ENABLED_GRAPHQL_CLIENTS.has(client.graphql)) return options;\n\n return { ...options, cache: strategy, [SHOULD_CACHE_RESULT]: shouldCacheResult };\n}\n\n/**\n * Creates a type-safe Storefront API client.\n *\n * Pick a `type` based on where your code runs and whether buyer context is available:\n *\n * | | public | private | private_no_buyer_context |\n * |-----------------|--------------------------------|-------------------------------|--------------------------|\n * | Runs in | Browser or mobile | Server (SSR) | Server (background) |\n * | Token | Public access token, or none | Private access token | Private access token |\n * | Buyer context | Browser request context | You forward trusted `buyerIp` | None |\n * | Best for | Client-side fetches | SSR with buyer context | Webhooks, background jobs |\n *\n * Pass `requestContext` so the client's SFAPI requests carry request-scoped\n * headers, follow the incoming request's abort signal, and read the resolved\n * `i18n` used for Storefront API variable injection and localized routes.\n *\n * Token-based access is required for some Storefront API fields, including\n * product tags, metaobjects, metafields, menus, and customers. Use a private\n * client only in trusted server code where the token cannot leak to browsers.\n *\n * @see {@link https://shopify.dev/docs/api/storefront#authentication | Storefront API authentication}\n */\nexport function createStorefrontClient<\n const Type extends ClientType,\n const RequestContext extends ShopifyRequestContext,\n const CacheConfig extends CacheInstance | undefined = undefined,\n>(\n args: CreateStorefrontClientArgs<RequestContext, Type, CacheConfig>,\n): StorefrontClient<\n // Tuple-wrapped to stay non-distributive so an explicit\n // `CacheInstance | undefined` type argument yields one client type, not a\n // union. Note a *value* of that type still unlocks cache options: optional\n // property inference strips `undefined` before it reaches CacheConfig, so\n // definiteness is undecidable here — the runtime guard rejects cache\n // options when no cache instance was actually configured.\n [CacheConfig] extends [CacheInstance] ? { cache?: CachingStrategy } : {},\n Type,\n RequestContext\n>;\nexport function createStorefrontClient(args: CreateStorefrontClientArgs): StorefrontClient {\n const { config, requestContext, type: clientType } = args;\n\n if (!config.storeDomain) {\n throw new Error(\"storeDomain is required and must be non-empty\");\n }\n\n const storeUrl = normalizeStoreDomain(config.storeDomain);\n const apiVersion = config.apiVersion ?? STOREFRONT_API_VERSION;\n const apiUrl = `${storeUrl}/api/${apiVersion}/graphql.json`;\n\n const originFetch = config.fetch ?? globalThis.fetch;\n if (typeof originFetch !== \"function\") {\n throw new Error(\n \"No fetch function available. Pass a fetch option or ensure globalThis.fetch exists.\",\n );\n }\n const resolvedFetch = config.cache\n ? createFetchWithCache({\n cache: config.cache,\n waitUntil: config.waitUntil,\n fetch: originFetch as typeof globalThis.fetch,\n })\n : originFetch;\n\n const timeoutInMs = config.defaultTimeoutInMs ?? DEFAULT_TIMEOUT_IN_MS;\n if (timeoutInMs < 0 || Number.isNaN(timeoutInMs)) {\n throw new Error(\"defaultTimeoutInMs must be a non-negative number\");\n }\n\n const staticHeaders: Record<string, string> = {\n \"content-type\": \"application/json\",\n };\n if (config.storefrontId) {\n staticHeaders[STOREFRONT_ID_HEADER] = config.storefrontId;\n }\n\n switch (clientType) {\n case \"private\":\n case \"private_no_buyer_context\":\n if (typeof document !== \"undefined\") {\n throw new Error(\n \"private storefront tokens cannot be used in a browser context. Use a publicStorefrontToken instead.\",\n );\n }\n if (!config.privateStorefrontToken) {\n throw new Error(\"privateStorefrontToken is required\");\n }\n staticHeaders[STOREFRONT_PRIVATE_TOKEN_HEADER] = config.privateStorefrontToken;\n break;\n case \"public\":\n if (config.publicStorefrontToken !== undefined && !config.publicStorefrontToken) {\n throw new Error(\"publicStorefrontToken must be non-empty when provided\");\n }\n if (config.publicStorefrontToken) {\n staticHeaders[STOREFRONT_ACCESS_TOKEN_HEADER] = config.publicStorefrontToken;\n }\n break;\n default:\n clientType satisfies never;\n throw new Error(`Unsupported client type: ${clientType}`);\n }\n\n const i18n = requestContext.i18n;\n const requestHeaders = new Headers(staticHeaders);\n requestContext.applyStorefrontRequestHeaders(requestHeaders);\n\n if (clientType === \"private\") {\n const { buyerIp } = requestContext;\n if (!buyerIp) {\n throw new TypeError(\"requestContext.buyerIp is required for private Storefront API clients\");\n }\n requestHeaders.set(STOREFRONT_BUYER_IP_HEADER, buyerIp);\n }\n\n async function graphql(\n doc: DocLike | string,\n ...rest: [options?: Record<string, unknown>]\n ): Promise<StorefrontGraphqlResult<DocLike>> {\n const opts = (rest[0] ?? {}) as {\n variables?: Record<string, unknown>;\n signal?: AbortSignal;\n cache?: CachingStrategy;\n [SHOULD_CACHE_RESULT]?: ShouldCacheResult;\n };\n\n const queryText = typeof doc === \"string\" ? doc : (doc as unknown as string);\n if (typeof queryText !== \"string\") {\n throw new StorefrontApiError(\n \"Expected gql() to return a string at runtime. Check gql.tada version compatibility.\",\n );\n }\n\n if (opts.cache && !config.cache) {\n throw new StorefrontCacheConfigError(\n \"Storefront API cache options require a cache configured in createStorefrontClient().\",\n );\n }\n\n if (opts.cache?.mode === \"private\") {\n throw new StorefrontCacheConfigError(\n \"Storefront API cache mode 'private' is not supported yet. Use Cache() without private mode, or cache buyer-specific work with createRunWithCache() and an explicit private key.\",\n );\n }\n\n const variables = buildVariables(queryText, opts.variables, i18n);\n const externalSignals = [requestContext.signal, opts.signal].filter(\n (signal): signal is AbortSignal => Boolean(signal),\n );\n\n const earlyAbortSignal = externalSignals.find((signal) => signal.aborted);\n if (earlyAbortSignal) {\n throw (\n earlyAbortSignal.reason ??\n new DOMException(\"signal is aborted without reason\", \"AbortError\")\n );\n }\n\n let requestId: string | undefined;\n let responseHeaders: Headers;\n let responseText: string;\n let status: number;\n let timeoutSignal: AbortSignal | undefined;\n\n if (timeoutInMs > 0) {\n timeoutSignal = AbortSignal.timeout(timeoutInMs);\n externalSignals.push(timeoutSignal);\n }\n\n try {\n const init: PlainRequestInit = {\n method: \"POST\",\n headers: new Headers(requestHeaders),\n body: JSON.stringify({ query: queryText, variables }),\n signal: externalSignals.length > 0 ? AbortSignal.any(externalSignals) : undefined,\n };\n\n const cacheOptions = createStorefrontCacheOptions(\n apiUrl,\n init,\n queryText,\n opts.cache,\n opts[SHOULD_CACHE_RESULT],\n );\n const response = await (resolvedFetch as ResolvedStorefrontFetch)(apiUrl, init, cacheOptions);\n\n responseHeaders = response.headers;\n const hydrogenCacheStatus = getHydrogenCacheStatus(responseHeaders);\n if (hydrogenCacheStatus !== \"hit\") {\n requestContext.captureSubrequestHeaders(responseHeaders);\n }\n\n requestId = responseHeaders.get(\"x-request-id\") ?? undefined;\n status = response.status;\n\n if (!response.ok) {\n const bodyHint = await response.text().catch(() => \"\");\n throw new StorefrontApiError(\n `SFAPI responded with ${status}${bodyHint ? `: ${bodyHint.slice(0, 200)}` : \"\"}`,\n { status, requestId, queryText, variables },\n );\n }\n\n responseText = await response.text();\n } catch (error) {\n if (error instanceof StorefrontApiError) throw error;\n if (error instanceof StorefrontTimeoutError) throw error;\n if (error instanceof StorefrontCacheConfigError) throw error;\n if (\n error instanceof DOMException &&\n error.name === \"TimeoutError\" &&\n timeoutSignal?.aborted\n ) {\n throw new StorefrontTimeoutError(timeoutInMs);\n }\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n throw new StorefrontApiError(\"SFAPI request failed\", { cause: error });\n }\n\n let json: unknown;\n try {\n json = JSON.parse(responseText);\n } catch (cause) {\n throw new StorefrontApiError(\n `Failed to parse SFAPI response as JSON (${apiUrl}, status ${status})` +\n (responseText ? `: ${responseText.slice(0, 200)}` : \"\"),\n { status, requestId, cause, queryText, variables },\n );\n }\n\n if (typeof json !== \"object\" || json === null || Array.isArray(json)) {\n throw new StorefrontApiError(\n `SFAPI returned unexpected JSON type: ${Array.isArray(json) ? \"array\" : typeof json}`,\n { status, requestId, queryText, variables },\n );\n }\n\n const body = json as { data?: unknown; errors?: GraphQLFormattedError[] };\n // Branch to populate the two arms of the StorefrontGraphqlResult union: callers\n // narrowing on `errors` get a non-null `data` only when no errors are present.\n if (body.errors) {\n return {\n data: body.data ?? null,\n errors: body.errors,\n headers: responseHeaders,\n };\n }\n return {\n data: body.data,\n headers: responseHeaders,\n };\n }\n\n const client: StorefrontClient = {\n type: clientType,\n i18n,\n graphql: graphql as StorefrontClient[\"graphql\"],\n apiUrl,\n storeUrl,\n storefrontId: config.storefrontId,\n requestContext,\n };\n\n if (config.cache) CACHE_ENABLED_GRAPHQL_CLIENTS.add(client.graphql);\n\n return client;\n}\n\nfunction buildVariables(\n queryText: string,\n userVariables: Record<string, unknown> | undefined,\n resolvedI18n: I18nConfig,\n): Record<string, unknown> {\n const variables: Record<string, unknown> = { ...userVariables };\n\n if (COUNTRY_VAR_RE.test(queryText)) {\n variables.country = resolvedI18n.country;\n }\n if (LANGUAGE_VAR_RE.test(queryText)) {\n variables.language = resolvedI18n.language;\n }\n\n return variables;\n}\n\ntype PlainRequestInit = RequestInit & { headers: Headers; body: string; method: string };\n\nfunction createStorefrontCacheOptions(\n apiUrl: string,\n init: PlainRequestInit,\n queryText: string,\n strategy: CachingStrategy | undefined,\n shouldCacheResult: ShouldCacheResult | undefined,\n): FetchCacheOptions | undefined {\n if (!strategy) return undefined;\n\n // Runtime only needs a safety belt for untyped callers. False positives are\n // acceptable because they bypass cache rather than risking mutation caching.\n if (/\\bmutation\\b/.test(queryText)) return undefined;\n\n return {\n key: createStorefrontCacheKey(apiUrl, init),\n strategy,\n shouldCacheResponse: async ({ json }: FetchCacheResponseContext) => {\n if (!json) return false;\n\n const body = await json().catch(() => undefined);\n if (typeof body !== \"object\" || body == null || Array.isArray(body) || \"errors\" in body) {\n return false;\n }\n\n return shouldCacheResult?.(body) ?? true;\n },\n };\n}\n\nfunction createStorefrontCacheKey(apiUrl: string, init: PlainRequestInit) {\n return [\n \"storefront-api\",\n apiUrl,\n init.method,\n init.body,\n [...init.headers]\n .filter(([name]) => {\n const normalized = name.toLowerCase();\n return (\n REQUEST_CACHE_KEY_HEADERS.has(normalized) && !REQUEST_IDENTITY_HEADERS.has(normalized)\n );\n })\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([name, value]) => `${name}:${value}`)\n .join(\"\\n\"),\n ] as const;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../../src/client/client.ts"],"sourcesContent":["import type { TadaDocumentNode } from \"gql.tada\";\n\nimport { getHydrogenCacheStatus } from \"../core/cache/cache-status\";\nimport { createFetchWithCache } from \"../core/cache/fetch-with-cache\";\nimport type { FetchCacheOptions, FetchCacheResponseContext } from \"../core/cache/fetch-with-cache\";\nimport type { CacheInstance } from \"../core/cache/run-with-cache\";\nimport type { CachingStrategy } from \"../core/cache/strategies\";\nimport { DEFAULT_TIMEOUT_IN_MS, STOREFRONT_API_VERSION } from \"../core/constants\";\nimport {\n HYDROGEN_VERSION_HEADER,\n REQUEST_GROUP_ID_HEADER,\n SDK_VARIANT_HEADER,\n SDK_VARIANT_SOURCE_HEADER,\n SDK_VERSION_HEADER,\n SHOPIFY_STOREFRONT_S_HEADER,\n SHOPIFY_STOREFRONT_Y_HEADER,\n STOREFRONT_ACCESS_TOKEN_HEADER,\n STOREFRONT_BUYER_IP_HEADER,\n STOREFRONT_ID_HEADER,\n STOREFRONT_PRIVATE_TOKEN_HEADER,\n SHOPIFY_UNIQUE_TOKEN_HEADER,\n SHOPIFY_VISIT_TOKEN_HEADER,\n} from \"../core/headers\";\nimport type { I18nConfig, ShopifyRequestContext } from \"../core/request-context\";\nimport { normalizeStoreDomain } from \"../core/url\";\nimport type { AnyStorefrontQueryString } from \"../graphql\";\nimport { StorefrontApiError, StorefrontTimeoutError } from \"./errors\";\nimport type {\n ClientType,\n CreateStorefrontClientArgs,\n GraphQLFormattedError,\n StorefrontClient,\n StorefrontGraphqlResult,\n} from \"./types\";\n\ntype DocLike = TadaDocumentNode<unknown, unknown> | AnyStorefrontQueryString;\ntype FetchInput = Parameters<typeof globalThis.fetch>[0];\ntype FetchInit = Parameters<typeof globalThis.fetch>[1];\ntype ResolvedStorefrontFetch = (\n input: FetchInput,\n init: FetchInit,\n cacheOptions: FetchCacheOptions | undefined,\n) => Promise<Response>;\n\nconst COUNTRY_VAR_RE = /\\$country\\s*:/;\nconst LANGUAGE_VAR_RE = /\\$language\\s*:/;\nconst REQUEST_CACHE_KEY_HEADERS = new Set([\n \"content-type\",\n \"user-agent\",\n SDK_VARIANT_HEADER.toLowerCase(),\n SDK_VARIANT_SOURCE_HEADER.toLowerCase(),\n SDK_VERSION_HEADER.toLowerCase(),\n HYDROGEN_VERSION_HEADER.toLowerCase(),\n STOREFRONT_ACCESS_TOKEN_HEADER.toLowerCase(),\n STOREFRONT_PRIVATE_TOKEN_HEADER.toLowerCase(),\n]);\nconst REQUEST_IDENTITY_HEADERS = new Set([\n \"cookie\",\n REQUEST_GROUP_ID_HEADER.toLowerCase(),\n STOREFRONT_BUYER_IP_HEADER.toLowerCase(),\n SHOPIFY_UNIQUE_TOKEN_HEADER.toLowerCase(),\n SHOPIFY_VISIT_TOKEN_HEADER.toLowerCase(),\n SHOPIFY_STOREFRONT_Y_HEADER.toLowerCase(),\n SHOPIFY_STOREFRONT_S_HEADER.toLowerCase(),\n]);\nconst CACHE_ENABLED_GRAPHQL_CLIENTS = new WeakSet<StorefrontClient[\"graphql\"]>();\nconst SHOULD_CACHE_RESULT = Symbol();\n\ntype ShouldCacheResult = (body: object) => boolean;\n\nclass StorefrontCacheConfigError extends Error {}\n\nexport function withStorefrontClientCache<TOptions extends object>(\n client: Pick<StorefrontClient, \"graphql\">,\n options: TOptions,\n strategy: CachingStrategy,\n shouldCacheResult?: ShouldCacheResult,\n): TOptions {\n if (!CACHE_ENABLED_GRAPHQL_CLIENTS.has(client.graphql)) return options;\n\n return { ...options, cache: strategy, [SHOULD_CACHE_RESULT]: shouldCacheResult };\n}\n\n/**\n * Creates a Storefront API client that runs typed queries and mutations against your store.\n *\n * Set `type` based on where your code runs and whether customer context is available:\n *\n * | | `public` | `private` | `private_no_buyer_context` |\n * |------------------|------------------------------|-------------------------------|------------------------------|\n * | Runs in | Browser or mobile | Server-side rendering | Background server code |\n * | Token | Public access token, or none | Private access token | Private access token |\n * | Customer context | Browser request context | You forward trusted `buyerIp` | None |\n * | Best for | Client-side fetches | SSR with customer context | Webhooks, background jobs |\n *\n * Pass a request context from `createShopifyRequestContext`. The client forwards the incoming\n * request's cookies and `Sec-GPC` header to the Storefront API, and fills `$country`\n * and `$language` variables from the context's locale. The client reads the request headers once,\n * when you create the client. Create a client for each incoming request, and create a client at\n * module scope only with a static request context.\n *\n * Keep private clients in server code, where the token can't reach browsers. The function throws an\n * error when you create either private client type in a browser. The function throws a `TypeError`\n * when the request context of a `private` client has no `buyerIp`.\n *\n * @see [Storefront API authentication](https://shopify.dev/docs/api/storefront#authentication)\n * @publicDocs\n */\nexport function createStorefrontClient<\n const Type extends ClientType,\n const RequestContext extends ShopifyRequestContext,\n const CacheConfig extends CacheInstance | undefined = undefined,\n>(\n args: CreateStorefrontClientArgs<RequestContext, Type, CacheConfig>,\n): StorefrontClient<\n // Tuple-wrapped to stay non-distributive so an explicit\n // `CacheInstance | undefined` type argument yields one client type, not a\n // union. Note a *value* of that type still unlocks cache options: optional\n // property inference strips `undefined` before it reaches CacheConfig, so\n // definiteness is undecidable here — the runtime guard rejects cache\n // options when no cache instance was actually configured.\n [CacheConfig] extends [CacheInstance] ? { cache?: CachingStrategy } : {},\n Type,\n RequestContext\n>;\n/**\n * @param args The client type, request context, and store configuration for the new client.\n * @returns The client, with a `graphql()` method that runs queries and mutations.\n */\nexport function createStorefrontClient(args: CreateStorefrontClientArgs): StorefrontClient {\n const { config, requestContext, type: clientType } = args;\n\n if (!config.storeDomain) {\n throw new Error(\"storeDomain is required and must be non-empty\");\n }\n\n const storeUrl = normalizeStoreDomain(config.storeDomain);\n const apiVersion = config.apiVersion ?? STOREFRONT_API_VERSION;\n const apiUrl = `${storeUrl}/api/${apiVersion}/graphql.json`;\n\n const originFetch = config.fetch ?? globalThis.fetch;\n if (typeof originFetch !== \"function\") {\n throw new Error(\n \"No fetch function available. Pass a fetch option or ensure globalThis.fetch exists.\",\n );\n }\n const resolvedFetch = config.cache\n ? createFetchWithCache({\n cache: config.cache,\n waitUntil: config.waitUntil,\n fetch: originFetch as typeof globalThis.fetch,\n })\n : originFetch;\n\n const timeoutInMs = config.defaultTimeoutInMs ?? DEFAULT_TIMEOUT_IN_MS;\n if (timeoutInMs < 0 || Number.isNaN(timeoutInMs)) {\n throw new Error(\"defaultTimeoutInMs must be a non-negative number\");\n }\n\n const staticHeaders: Record<string, string> = {\n \"content-type\": \"application/json\",\n };\n if (config.storefrontId) {\n staticHeaders[STOREFRONT_ID_HEADER] = config.storefrontId;\n }\n\n switch (clientType) {\n case \"private\":\n case \"private_no_buyer_context\":\n if (typeof document !== \"undefined\") {\n throw new Error(\n \"private storefront tokens cannot be used in a browser context. Use a publicStorefrontToken instead.\",\n );\n }\n if (!config.privateStorefrontToken) {\n throw new Error(\"privateStorefrontToken is required\");\n }\n staticHeaders[STOREFRONT_PRIVATE_TOKEN_HEADER] = config.privateStorefrontToken;\n break;\n case \"public\":\n if (config.publicStorefrontToken !== undefined && !config.publicStorefrontToken) {\n throw new Error(\"publicStorefrontToken must be non-empty when provided\");\n }\n if (config.publicStorefrontToken) {\n staticHeaders[STOREFRONT_ACCESS_TOKEN_HEADER] = config.publicStorefrontToken;\n }\n break;\n default:\n clientType satisfies never;\n throw new Error(`Unsupported client type: ${clientType}`);\n }\n\n const i18n = requestContext.i18n;\n const requestHeaders = new Headers(staticHeaders);\n requestContext.applyStorefrontRequestHeaders(requestHeaders);\n\n if (clientType === \"private\") {\n const { buyerIp } = requestContext;\n if (!buyerIp) {\n throw new TypeError(\"requestContext.buyerIp is required for private Storefront API clients\");\n }\n requestHeaders.set(STOREFRONT_BUYER_IP_HEADER, buyerIp);\n }\n\n async function graphql(\n doc: DocLike | string,\n ...rest: [options?: Record<string, unknown>]\n ): Promise<StorefrontGraphqlResult<DocLike>> {\n const opts = (rest[0] ?? {}) as {\n variables?: Record<string, unknown>;\n signal?: AbortSignal;\n cache?: CachingStrategy;\n [SHOULD_CACHE_RESULT]?: ShouldCacheResult;\n };\n\n const queryText = typeof doc === \"string\" ? doc : (doc as unknown as string);\n if (typeof queryText !== \"string\") {\n throw new StorefrontApiError(\n \"Expected gql() to return a string at runtime. Check gql.tada version compatibility.\",\n );\n }\n\n if (opts.cache && !config.cache) {\n throw new StorefrontCacheConfigError(\n \"Storefront API cache options require a cache configured in createStorefrontClient().\",\n );\n }\n\n if (opts.cache?.mode === \"private\") {\n throw new StorefrontCacheConfigError(\n \"Storefront API cache mode 'private' is not supported yet. Use Cache() without private mode, or cache buyer-specific work with createRunWithCache() and an explicit private key.\",\n );\n }\n\n const variables = buildVariables(queryText, opts.variables, i18n);\n const externalSignals = [requestContext.signal, opts.signal].filter(\n (signal): signal is AbortSignal => Boolean(signal),\n );\n\n const earlyAbortSignal = externalSignals.find((signal) => signal.aborted);\n if (earlyAbortSignal) {\n throw (\n earlyAbortSignal.reason ??\n new DOMException(\"signal is aborted without reason\", \"AbortError\")\n );\n }\n\n let requestId: string | undefined;\n let responseHeaders: Headers;\n let responseText: string;\n let status: number;\n let timeoutSignal: AbortSignal | undefined;\n\n if (timeoutInMs > 0) {\n timeoutSignal = AbortSignal.timeout(timeoutInMs);\n externalSignals.push(timeoutSignal);\n }\n\n try {\n const init: PlainRequestInit = {\n method: \"POST\",\n headers: new Headers(requestHeaders),\n body: JSON.stringify({ query: queryText, variables }),\n signal: externalSignals.length > 0 ? AbortSignal.any(externalSignals) : undefined,\n };\n\n const cacheOptions = createStorefrontCacheOptions(\n apiUrl,\n init,\n queryText,\n opts.cache,\n opts[SHOULD_CACHE_RESULT],\n timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT_IN_MS,\n );\n const response = await (resolvedFetch as ResolvedStorefrontFetch)(apiUrl, init, cacheOptions);\n\n responseHeaders = response.headers;\n const hydrogenCacheStatus = getHydrogenCacheStatus(responseHeaders);\n if (hydrogenCacheStatus !== \"hit\") {\n requestContext.captureSubrequestHeaders(responseHeaders);\n }\n\n requestId = responseHeaders.get(\"x-request-id\") ?? undefined;\n status = response.status;\n\n if (!response.ok) {\n const bodyHint = await response.text().catch(() => \"\");\n throw new StorefrontApiError(\n `SFAPI responded with ${status}${bodyHint ? `: ${bodyHint.slice(0, 200)}` : \"\"}`,\n { status, requestId, queryText, variables },\n );\n }\n\n responseText = await response.text();\n } catch (error) {\n if (error instanceof StorefrontApiError) throw error;\n if (error instanceof StorefrontTimeoutError) throw error;\n if (error instanceof StorefrontCacheConfigError) throw error;\n if (\n error instanceof DOMException &&\n error.name === \"TimeoutError\" &&\n timeoutSignal?.aborted\n ) {\n throw new StorefrontTimeoutError(timeoutInMs);\n }\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n throw new StorefrontApiError(\"SFAPI request failed\", { cause: error });\n }\n\n let json: unknown;\n try {\n json = JSON.parse(responseText);\n } catch (cause) {\n throw new StorefrontApiError(\n `Failed to parse SFAPI response as JSON (${apiUrl}, status ${status})` +\n (responseText ? `: ${responseText.slice(0, 200)}` : \"\"),\n { status, requestId, cause, queryText, variables },\n );\n }\n\n if (typeof json !== \"object\" || json === null || Array.isArray(json)) {\n throw new StorefrontApiError(\n `SFAPI returned unexpected JSON type: ${Array.isArray(json) ? \"array\" : typeof json}`,\n { status, requestId, queryText, variables },\n );\n }\n\n const body = json as { data?: unknown; errors?: GraphQLFormattedError[] };\n // Branch to populate the two arms of the StorefrontGraphqlResult union: callers\n // narrowing on `errors` get a non-null `data` only when no errors are present.\n if (body.errors) {\n return {\n data: body.data ?? null,\n errors: body.errors,\n headers: responseHeaders,\n };\n }\n return {\n data: body.data,\n headers: responseHeaders,\n };\n }\n\n const client: StorefrontClient = {\n type: clientType,\n i18n,\n graphql: graphql as StorefrontClient[\"graphql\"],\n apiUrl,\n storeUrl,\n storefrontId: config.storefrontId,\n requestContext,\n };\n\n if (config.cache) CACHE_ENABLED_GRAPHQL_CLIENTS.add(client.graphql);\n\n return client;\n}\n\nfunction buildVariables(\n queryText: string,\n userVariables: Record<string, unknown> | undefined,\n resolvedI18n: I18nConfig,\n): Record<string, unknown> {\n const variables: Record<string, unknown> = { ...userVariables };\n\n if (COUNTRY_VAR_RE.test(queryText)) {\n variables.country = resolvedI18n.country;\n }\n if (LANGUAGE_VAR_RE.test(queryText)) {\n variables.language = resolvedI18n.language;\n }\n\n return variables;\n}\n\ntype PlainRequestInit = RequestInit & { headers: Headers; body: string; method: string };\n\nfunction createStorefrontCacheOptions(\n apiUrl: string,\n init: PlainRequestInit,\n queryText: string,\n strategy: CachingStrategy | undefined,\n shouldCacheResult: ShouldCacheResult | undefined,\n backgroundTimeoutInMs: number,\n): FetchCacheOptions | undefined {\n if (!strategy) return undefined;\n\n // Runtime only needs a safety belt for untyped callers. False positives are\n // acceptable because they bypass cache rather than risking mutation caching.\n if (/\\bmutation\\b/.test(queryText)) return undefined;\n\n return {\n key: createStorefrontCacheKey(apiUrl, init),\n strategy,\n shouldCacheResponse: async ({ json }: FetchCacheResponseContext) => {\n if (!json) return false;\n\n const body = await json().catch(() => undefined);\n if (typeof body !== \"object\" || body == null || Array.isArray(body) || \"errors\" in body) {\n return false;\n }\n\n return shouldCacheResult?.(body) ?? true;\n },\n // A refresh outlives the request that triggered it, so request and caller\n // aborts must not cancel it. It still needs a deadline, even when the caller\n // disabled the client timeout to own the foreground one.\n backgroundSignal: () => AbortSignal.timeout(backgroundTimeoutInMs),\n };\n}\n\nfunction createStorefrontCacheKey(apiUrl: string, init: PlainRequestInit) {\n return [\n \"storefront-api\",\n apiUrl,\n init.method,\n init.body,\n [...init.headers]\n .filter(([name]) => {\n const normalized = name.toLowerCase();\n return (\n REQUEST_CACHE_KEY_HEADERS.has(normalized) && !REQUEST_IDENTITY_HEADERS.has(normalized)\n );\n })\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([name, value]) => `${name}:${value}`)\n .join(\"\\n\"),\n ] as const;\n}\n"],"mappings":"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"}
@@ -5,42 +5,64 @@ interface StorefrontApiErrorOptions {
5
5
  cause?: unknown;
6
6
  queryText?: string;
7
7
  variables?: Record<string, unknown>;
8
- locations?: ReadonlyArray<{
9
- line: number;
10
- column: number;
11
- }>;
12
- path?: ReadonlyArray<string | number>;
13
- extensions?: Record<string, unknown>;
14
8
  }
9
+ /**
10
+ * Signals that a Storefront API request failed. The client throws the error for an HTTP error
11
+ * status, a network failure, or a response body that isn't a JSON object.
12
+ *
13
+ * GraphQL errors, including `THROTTLED`, don't throw. Read GraphQL errors from the result's
14
+ * `errors` field.
15
+ *
16
+ * When the request context's signal or the call's `signal` aborts before the call, the client
17
+ * throws the abort reason and sends no request. An `AbortError` during the request reaches your
18
+ * code unchanged. Other abort reasons during the request throw a `StorefrontApiError`.
19
+ *
20
+ * @publicDocs
21
+ */
15
22
  declare class StorefrontApiError extends Error {
23
+ /** The `x-request-id` header of the Storefront API response, when the response has one. Include the ID in support requests. */
16
24
  readonly requestId?: string;
25
+ /** The HTTP status code of the response, when the request reached the server. */
17
26
  readonly status?: number;
27
+ /** The GraphQL query that failed. Hydrogen sets the value only in development builds. */
18
28
  readonly queryText?: string;
29
+ /** The variables that the client sent with the request. Hydrogen sets the value only in development builds. */
19
30
  readonly variables?: Record<string, unknown>;
20
- readonly locations?: ReadonlyArray<{
21
- line: number;
22
- column: number;
23
- }>;
24
- readonly path?: ReadonlyArray<string | number>;
25
- readonly extensions?: Record<string, unknown>;
31
+ /** `StorefrontApiError`. `StorefrontTimeoutError` sets its own class name. */
32
+ name: string;
33
+ /** Describes the failure, such as the HTTP status that the API returned. */
34
+ message: string;
35
+ /** The stack trace, when the JavaScript runtime records one. */
36
+ stack?: string;
26
37
  constructor(message: string, options?: StorefrontApiErrorOptions);
38
+ /** Makes `Object.prototype.toString` report the error name, such as `[object StorefrontApiError]`. */
27
39
  get [Symbol.toStringTag](): string;
28
- toString(): string;
40
+ /** Returns the error name, message, request ID, and status for logging. The output leaves out the query text, variables, cause, and stack. */
29
41
  toJSON(): {
30
42
  name: string;
31
43
  message: string;
32
44
  requestId?: string;
33
45
  status?: number;
34
- locations?: ReadonlyArray<{
35
- line: number;
36
- column: number;
37
- }>;
38
- path?: ReadonlyArray<string | number>;
39
- extensions?: Record<string, unknown>;
40
46
  };
41
47
  }
48
+ /**
49
+ * Signals that a Storefront API request ran longer than the client's `defaultTimeoutInMs` setting.
50
+ * Aborts from the request context or the call's `signal` throw other errors.
51
+ *
52
+ * `StorefrontTimeoutError` extends `StorefrontApiError`. Check for `StorefrontTimeoutError` first when
53
+ * you handle both errors.
54
+ *
55
+ * @publicDocs
56
+ */
42
57
  declare class StorefrontTimeoutError extends StorefrontApiError {
58
+ /** The timeout that the request exceeded, in milliseconds. */
43
59
  readonly timeoutInMs: number;
60
+ /** Always `StorefrontTimeoutError`. */
61
+ name: string;
62
+ /** States the timeout, such as `"Storefront API request timed out after 30000ms"`. */
63
+ message: string;
64
+ /** The stack trace, when the JavaScript runtime records one. */
65
+ stack?: string;
44
66
  constructor(timeoutInMs: number, options?: Omit<StorefrontApiErrorOptions, "cause">);
45
67
  }
46
68
  //#endregion
@@ -1,34 +1,37 @@
1
1
  //#region src/client/errors.ts
2
+ /**
3
+ * Signals that a Storefront API request failed. The client throws the error for an HTTP error
4
+ * status, a network failure, or a response body that isn't a JSON object.
5
+ *
6
+ * GraphQL errors, including `THROTTLED`, don't throw. Read GraphQL errors from the result's
7
+ * `errors` field.
8
+ *
9
+ * When the request context's signal or the call's `signal` aborts before the call, the client
10
+ * throws the abort reason and sends no request. An `AbortError` during the request reaches your
11
+ * code unchanged. Other abort reasons during the request throw a `StorefrontApiError`.
12
+ *
13
+ * @publicDocs
14
+ */
2
15
  var StorefrontApiError = class extends Error {
16
+ /** The `x-request-id` header of the Storefront API response, when the response has one. Include the ID in support requests. */
3
17
  requestId;
18
+ /** The HTTP status code of the response, when the request reached the server. */
4
19
  status;
20
+ /** The GraphQL query that failed. Hydrogen sets the value only in development builds. */
5
21
  queryText;
22
+ /** The variables that the client sent with the request. Hydrogen sets the value only in development builds. */
6
23
  variables;
7
- locations;
8
- path;
9
- extensions;
10
24
  constructor(message, options) {
11
25
  super(message, options?.cause ? { cause: options.cause } : void 0);
12
26
  this.name = "StorefrontApiError";
13
27
  this.requestId = options?.requestId;
14
28
  this.status = options?.status;
15
- this.locations = options?.locations;
16
- this.path = options?.path;
17
- this.extensions = options?.extensions;
18
29
  }
30
+ /** Makes `Object.prototype.toString` report the error name, such as `[object StorefrontApiError]`. */
19
31
  get [Symbol.toStringTag]() {
20
32
  return this.name;
21
33
  }
22
- toString() {
23
- let result = `${this.name}: ${this.message}`;
24
- if (this.path) try {
25
- result += ` | path: ${JSON.stringify(this.path)}`;
26
- } catch {}
27
- if (this.extensions) try {
28
- result += ` | extensions: ${JSON.stringify(this.extensions)}`;
29
- } catch {}
30
- return result;
31
- }
34
+ /** Returns the error name, message, request ID, and status for logging. The output leaves out the query text, variables, cause, and stack. */
32
35
  toJSON() {
33
36
  return {
34
37
  name: this.name,
@@ -38,7 +41,17 @@ var StorefrontApiError = class extends Error {
38
41
  };
39
42
  }
40
43
  };
44
+ /**
45
+ * Signals that a Storefront API request ran longer than the client's `defaultTimeoutInMs` setting.
46
+ * Aborts from the request context or the call's `signal` throw other errors.
47
+ *
48
+ * `StorefrontTimeoutError` extends `StorefrontApiError`. Check for `StorefrontTimeoutError` first when
49
+ * you handle both errors.
50
+ *
51
+ * @publicDocs
52
+ */
41
53
  var StorefrontTimeoutError = class extends StorefrontApiError {
54
+ /** The timeout that the request exceeded, in milliseconds. */
42
55
  timeoutInMs;
43
56
  constructor(timeoutInMs, options) {
44
57
  super(`Storefront API request timed out after ${timeoutInMs}ms`, options);