@shopify/hydrogen 2026.10.0-preview.2 → 2026.10.0-preview.4

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 (404) hide show
  1. package/README.md +34 -3
  2. package/dist/cli/index.mjs +578 -85
  3. package/dist/client/client.mjs +2 -2
  4. package/dist/client/errors.d.mts +19 -19
  5. package/dist/client/errors.mjs +19 -16
  6. package/dist/client/errors.mjs.map +1 -1
  7. package/dist/client/types.d.mts +91 -10
  8. package/dist/core/analytics/cart-tracker.d.mts +14 -0
  9. package/dist/core/analytics/cart-tracker.mjs +15 -1
  10. package/dist/core/analytics/cart-tracker.mjs.map +1 -1
  11. package/dist/core/analytics/custom-consent.mjs +12 -0
  12. package/dist/core/analytics/custom-consent.mjs.map +1 -0
  13. package/dist/core/analytics/events.d.mts +7 -0
  14. package/dist/core/analytics/events.mjs +7 -0
  15. package/dist/core/analytics/events.mjs.map +1 -1
  16. package/dist/core/analytics/index.d.mts +1 -1
  17. package/dist/core/analytics/types.d.mts +152 -16
  18. package/dist/core/cart/actions.d.mts +47 -3
  19. package/dist/core/cart/actions.mjs +29 -0
  20. package/dist/core/cart/actions.mjs.map +1 -1
  21. package/dist/core/cart/attach-quantity-input.d.mts +26 -0
  22. package/dist/core/cart/attach-quantity-input.mjs +26 -0
  23. package/dist/core/cart/attach-quantity-input.mjs.map +1 -1
  24. package/dist/core/cart/cart.d.mts +106 -6
  25. package/dist/core/cart/cart.mjs +57 -6
  26. package/dist/core/cart/cart.mjs.map +1 -1
  27. package/dist/core/cart/cookie.d.mts +15 -0
  28. package/dist/core/cart/cookie.mjs +15 -0
  29. package/dist/core/cart/cookie.mjs.map +1 -1
  30. package/dist/core/cart/form.d.mts +61 -0
  31. package/dist/core/cart/form.mjs +18 -0
  32. package/dist/core/cart/form.mjs.map +1 -1
  33. package/dist/core/cart/get-cart.d.mts +43 -2
  34. package/dist/core/cart/get-cart.mjs +33 -0
  35. package/dist/core/cart/get-cart.mjs.map +1 -1
  36. package/dist/core/cart/index.d.mts +1 -1
  37. package/dist/core/cart/quantity.d.mts +17 -0
  38. package/dist/core/cart/quantity.mjs +17 -0
  39. package/dist/core/cart/quantity.mjs.map +1 -1
  40. package/dist/core/cart/queries.d.mts +402 -369
  41. package/dist/core/cart/queries.mjs +2 -1
  42. package/dist/core/cart/queries.mjs.map +1 -1
  43. package/dist/core/cart/server-handlers.d.mts +69 -0
  44. package/dist/core/cart/server-handlers.mjs +2 -10
  45. package/dist/core/cart/server-handlers.mjs.map +1 -1
  46. package/dist/core/cart/state.d.mts +133 -0
  47. package/dist/core/cart/state.mjs +8 -0
  48. package/dist/core/cart/state.mjs.map +1 -1
  49. package/dist/core/collection/collection.d.mts +18 -2
  50. package/dist/core/collection/collection.mjs +11 -0
  51. package/dist/core/collection/collection.mjs.map +1 -1
  52. package/dist/core/collection/index.d.mts +1 -1
  53. package/dist/core/collection/reconciler.d.mts +13 -4
  54. package/dist/core/collection/reconciler.mjs.map +1 -1
  55. package/dist/core/collection/state.d.mts +7 -2
  56. package/dist/core/collection/state.mjs.map +1 -1
  57. package/dist/core/collection/url.d.mts +3 -0
  58. package/dist/core/collection/url.mjs.map +1 -1
  59. package/dist/core/constants.d.mts +1 -1
  60. package/dist/core/constants.mjs +2 -2
  61. package/dist/core/constants.mjs.map +1 -1
  62. package/dist/core/headers.mjs +2 -2
  63. package/dist/core/headers.mjs.map +1 -1
  64. package/dist/core/index.d.mts +5 -5
  65. package/dist/core/logging/logging.mjs.map +1 -1
  66. package/dist/core/money/format.mjs +6 -3
  67. package/dist/core/money/format.mjs.map +1 -1
  68. package/dist/core/observable.mjs +0 -3
  69. package/dist/core/observable.mjs.map +1 -1
  70. package/dist/core/predictive-search/form.d.mts +1 -0
  71. package/dist/core/predictive-search/form.mjs +1 -0
  72. package/dist/core/predictive-search/form.mjs.map +1 -1
  73. package/dist/core/predictive-search/index.d.mts +1 -1
  74. package/dist/core/predictive-search/queries.d.mts +40 -4
  75. package/dist/core/predictive-search/queries.mjs +7 -1
  76. package/dist/core/predictive-search/queries.mjs.map +1 -1
  77. package/dist/core/predictive-search/search.d.mts +43 -10
  78. package/dist/core/predictive-search/search.mjs +34 -0
  79. package/dist/core/predictive-search/search.mjs.map +1 -1
  80. package/dist/core/predictive-search/server-handlers.d.mts +31 -5
  81. package/dist/core/predictive-search/server-handlers.mjs +5 -3
  82. package/dist/core/predictive-search/server-handlers.mjs.map +1 -1
  83. package/dist/core/predictive-search/store.d.mts +3 -1
  84. package/dist/core/predictive-search/store.mjs.map +1 -1
  85. package/dist/core/predictive-search/url.d.mts +50 -8
  86. package/dist/core/predictive-search/url.mjs +9 -0
  87. package/dist/core/predictive-search/url.mjs.map +1 -1
  88. package/dist/core/product/form.d.mts +21 -2
  89. package/dist/core/product/form.mjs +13 -0
  90. package/dist/core/product/form.mjs.map +1 -1
  91. package/dist/core/product/index.d.mts +1 -1
  92. package/dist/core/product/options.d.mts +2 -2
  93. package/dist/core/product/options.mjs +2 -2
  94. package/dist/core/product/options.mjs.map +1 -1
  95. package/dist/core/product/product-form.d.mts +33 -1
  96. package/dist/core/product/product-form.mjs +30 -0
  97. package/dist/core/product/product-form.mjs.map +1 -1
  98. package/dist/core/product/state.d.mts +41 -0
  99. package/dist/core/product/url.d.mts +6 -0
  100. package/dist/core/product/url.mjs.map +1 -1
  101. package/dist/core/request-context.d.mts +8 -5
  102. package/dist/core/request-context.mjs +17 -39
  103. package/dist/core/request-context.mjs.map +1 -1
  104. package/dist/core/request-routing/handle-shopify-routes.mjs +4 -1
  105. package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  106. package/dist/core/request-routing/interceptors/ajax-api.mjs +1 -1
  107. package/dist/core/request-routing/interceptors/api-proxy.mjs +1 -1
  108. package/dist/core/request-routing/interceptors/checkout.mjs +22 -6
  109. package/dist/core/request-routing/interceptors/checkout.mjs.map +1 -1
  110. package/dist/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
  111. package/dist/core/request-routing/interceptors/proxy.mjs +36 -10
  112. package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
  113. package/dist/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
  114. package/dist/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
  115. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
  116. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
  117. package/dist/core/request-routing/interceptors/ucp-mcp-proxy.mjs +35 -0
  118. package/dist/core/request-routing/interceptors/ucp-mcp-proxy.mjs.map +1 -0
  119. package/dist/core/request-routing/interceptors/well-known.mjs +1 -1
  120. package/dist/core/shopify-scripts/analytics.mjs +2 -2
  121. package/dist/core/shopify-scripts/analytics.mjs.map +1 -1
  122. package/dist/core/shopify-scripts/constants.mjs +4 -2
  123. package/dist/core/shopify-scripts/constants.mjs.map +1 -1
  124. package/dist/core/shopify-scripts/global.mjs +9 -4
  125. package/dist/core/shopify-scripts/global.mjs.map +1 -1
  126. package/dist/core/shopify-scripts/index.d.mts +1 -0
  127. package/dist/core/shopify-scripts/index.mjs +14 -12
  128. package/dist/core/shopify-scripts/index.mjs.map +1 -1
  129. package/dist/core/shopify-scripts/initialize.d.mts +1 -0
  130. package/dist/core/shopify-scripts/initialize.mjs +10 -3
  131. package/dist/core/shopify-scripts/initialize.mjs.map +1 -1
  132. package/dist/core/shopify-scripts/types.d.mts +6 -0
  133. package/dist/core/url.mjs +21 -2
  134. package/dist/core/url.mjs.map +1 -1
  135. package/dist/customer-account/client.d.mts +128 -4
  136. package/dist/customer-account/client.mjs +27 -1
  137. package/dist/customer-account/client.mjs.map +1 -1
  138. package/dist/customer-account/errors.d.mts +46 -0
  139. package/dist/customer-account/errors.mjs +46 -0
  140. package/dist/customer-account/errors.mjs.map +1 -1
  141. package/dist/customer-account/graphql.d.mts +68 -0
  142. package/dist/customer-account/graphql.mjs +47 -0
  143. package/dist/customer-account/graphql.mjs.map +1 -1
  144. package/dist/customer-account/session.d.mts +114 -23
  145. package/dist/customer-account/session.mjs +27 -12
  146. package/dist/customer-account/session.mjs.map +1 -1
  147. package/dist/customer-account/type-resolver.d.mts +23 -0
  148. package/dist/customer-account-api-types.d.ts +570 -374
  149. package/dist/customer-account.schema.json +2981 -1527
  150. package/dist/development/client/client.mjs +2 -2
  151. package/dist/development/client/errors.d.mts +19 -19
  152. package/dist/development/client/errors.mjs +20 -20
  153. package/dist/development/client/errors.mjs.map +1 -1
  154. package/dist/development/client/types.d.mts +91 -10
  155. package/dist/development/core/analytics/cart-tracker.d.mts +14 -0
  156. package/dist/development/core/analytics/cart-tracker.mjs +15 -1
  157. package/dist/development/core/analytics/cart-tracker.mjs.map +1 -1
  158. package/dist/development/core/analytics/custom-consent.mjs +12 -0
  159. package/dist/development/core/analytics/custom-consent.mjs.map +1 -0
  160. package/dist/development/core/analytics/events.d.mts +7 -0
  161. package/dist/development/core/analytics/events.mjs +7 -0
  162. package/dist/development/core/analytics/events.mjs.map +1 -1
  163. package/dist/development/core/analytics/index.d.mts +1 -1
  164. package/dist/development/core/analytics/types.d.mts +152 -16
  165. package/dist/development/core/cart/actions.d.mts +47 -3
  166. package/dist/development/core/cart/actions.mjs +29 -0
  167. package/dist/development/core/cart/actions.mjs.map +1 -1
  168. package/dist/development/core/cart/attach-quantity-input.d.mts +26 -0
  169. package/dist/development/core/cart/attach-quantity-input.mjs +26 -0
  170. package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -1
  171. package/dist/development/core/cart/cart.d.mts +106 -6
  172. package/dist/development/core/cart/cart.mjs +57 -6
  173. package/dist/development/core/cart/cart.mjs.map +1 -1
  174. package/dist/development/core/cart/cookie.d.mts +15 -0
  175. package/dist/development/core/cart/cookie.mjs +15 -0
  176. package/dist/development/core/cart/cookie.mjs.map +1 -1
  177. package/dist/development/core/cart/form.d.mts +61 -0
  178. package/dist/development/core/cart/form.mjs +18 -0
  179. package/dist/development/core/cart/form.mjs.map +1 -1
  180. package/dist/development/core/cart/get-cart.d.mts +43 -2
  181. package/dist/development/core/cart/get-cart.mjs +33 -0
  182. package/dist/development/core/cart/get-cart.mjs.map +1 -1
  183. package/dist/development/core/cart/index.d.mts +1 -1
  184. package/dist/development/core/cart/quantity.d.mts +17 -0
  185. package/dist/development/core/cart/quantity.mjs +17 -0
  186. package/dist/development/core/cart/quantity.mjs.map +1 -1
  187. package/dist/development/core/cart/queries.d.mts +345 -312
  188. package/dist/development/core/cart/queries.mjs +2 -1
  189. package/dist/development/core/cart/queries.mjs.map +1 -1
  190. package/dist/development/core/cart/server-handlers.d.mts +69 -0
  191. package/dist/development/core/cart/server-handlers.mjs +2 -10
  192. package/dist/development/core/cart/server-handlers.mjs.map +1 -1
  193. package/dist/development/core/cart/state.d.mts +133 -0
  194. package/dist/development/core/cart/state.mjs +8 -0
  195. package/dist/development/core/cart/state.mjs.map +1 -1
  196. package/dist/development/core/collection/collection.d.mts +18 -2
  197. package/dist/development/core/collection/collection.mjs +11 -0
  198. package/dist/development/core/collection/collection.mjs.map +1 -1
  199. package/dist/development/core/collection/index.d.mts +1 -1
  200. package/dist/development/core/collection/reconciler.d.mts +13 -4
  201. package/dist/development/core/collection/reconciler.mjs.map +1 -1
  202. package/dist/development/core/collection/state.d.mts +7 -2
  203. package/dist/development/core/collection/state.mjs.map +1 -1
  204. package/dist/development/core/collection/url.d.mts +3 -0
  205. package/dist/development/core/collection/url.mjs.map +1 -1
  206. package/dist/development/core/constants.mjs +1 -1
  207. package/dist/development/core/constants.mjs.map +1 -1
  208. package/dist/development/core/development.d.mts +5 -5
  209. package/dist/development/core/headers.mjs +2 -2
  210. package/dist/development/core/headers.mjs.map +1 -1
  211. package/dist/development/core/index.d.mts +4 -4
  212. package/dist/development/core/logging/logging.mjs.map +1 -1
  213. package/dist/development/core/money/format.mjs +6 -3
  214. package/dist/development/core/money/format.mjs.map +1 -1
  215. package/dist/development/core/observable.mjs +0 -3
  216. package/dist/development/core/observable.mjs.map +1 -1
  217. package/dist/development/core/predictive-search/form.d.mts +1 -0
  218. package/dist/development/core/predictive-search/form.mjs +1 -0
  219. package/dist/development/core/predictive-search/form.mjs.map +1 -1
  220. package/dist/development/core/predictive-search/index.d.mts +1 -1
  221. package/dist/development/core/predictive-search/queries.d.mts +38 -2
  222. package/dist/development/core/predictive-search/queries.mjs +7 -1
  223. package/dist/development/core/predictive-search/queries.mjs.map +1 -1
  224. package/dist/development/core/predictive-search/search.d.mts +43 -10
  225. package/dist/development/core/predictive-search/search.mjs +34 -0
  226. package/dist/development/core/predictive-search/search.mjs.map +1 -1
  227. package/dist/development/core/predictive-search/server-handlers.d.mts +31 -5
  228. package/dist/development/core/predictive-search/server-handlers.mjs +5 -3
  229. package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -1
  230. package/dist/development/core/predictive-search/store.d.mts +3 -1
  231. package/dist/development/core/predictive-search/store.mjs.map +1 -1
  232. package/dist/development/core/predictive-search/url.d.mts +50 -8
  233. package/dist/development/core/predictive-search/url.mjs +9 -0
  234. package/dist/development/core/predictive-search/url.mjs.map +1 -1
  235. package/dist/development/core/product/form.d.mts +21 -2
  236. package/dist/development/core/product/form.mjs +13 -0
  237. package/dist/development/core/product/form.mjs.map +1 -1
  238. package/dist/development/core/product/index.d.mts +1 -1
  239. package/dist/development/core/product/options.d.mts +2 -2
  240. package/dist/development/core/product/options.mjs +2 -2
  241. package/dist/development/core/product/options.mjs.map +1 -1
  242. package/dist/development/core/product/product-form.d.mts +33 -1
  243. package/dist/development/core/product/product-form.mjs +30 -0
  244. package/dist/development/core/product/product-form.mjs.map +1 -1
  245. package/dist/development/core/product/state.d.mts +41 -0
  246. package/dist/development/core/product/url.d.mts +6 -0
  247. package/dist/development/core/product/url.mjs.map +1 -1
  248. package/dist/development/core/request-context.d.mts +8 -5
  249. package/dist/development/core/request-context.mjs +17 -39
  250. package/dist/development/core/request-context.mjs.map +1 -1
  251. package/dist/development/core/request-routing/handle-shopify-routes.mjs +4 -1
  252. package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  253. package/dist/development/core/request-routing/interceptors/ajax-api.mjs +1 -1
  254. package/dist/development/core/request-routing/interceptors/api-proxy.mjs +1 -1
  255. package/dist/development/core/request-routing/interceptors/checkout.mjs +22 -6
  256. package/dist/development/core/request-routing/interceptors/checkout.mjs.map +1 -1
  257. package/dist/development/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
  258. package/dist/development/core/request-routing/interceptors/proxy.mjs +36 -10
  259. package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
  260. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
  261. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
  262. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
  263. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
  264. package/dist/development/core/request-routing/interceptors/ucp-mcp-proxy.mjs +35 -0
  265. package/dist/development/core/request-routing/interceptors/ucp-mcp-proxy.mjs.map +1 -0
  266. package/dist/development/core/request-routing/interceptors/well-known.mjs +1 -1
  267. package/dist/development/core/shopify-scripts/analytics.mjs +2 -2
  268. package/dist/development/core/shopify-scripts/analytics.mjs.map +1 -1
  269. package/dist/development/core/shopify-scripts/constants.mjs +4 -2
  270. package/dist/development/core/shopify-scripts/constants.mjs.map +1 -1
  271. package/dist/development/core/shopify-scripts/global.mjs +9 -4
  272. package/dist/development/core/shopify-scripts/global.mjs.map +1 -1
  273. package/dist/development/core/shopify-scripts/index.d.mts +1 -0
  274. package/dist/development/core/shopify-scripts/index.mjs +14 -12
  275. package/dist/development/core/shopify-scripts/index.mjs.map +1 -1
  276. package/dist/development/core/shopify-scripts/initialize.d.mts +1 -0
  277. package/dist/development/core/shopify-scripts/initialize.mjs +10 -3
  278. package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -1
  279. package/dist/development/core/shopify-scripts/types.d.mts +6 -0
  280. package/dist/development/core/url.mjs +21 -2
  281. package/dist/development/core/url.mjs.map +1 -1
  282. package/dist/development/customer-account/session.d.mts +69 -13
  283. package/dist/development/customer-account/session.mjs +4 -0
  284. package/dist/development/customer-account/session.mjs.map +1 -1
  285. package/dist/development/globals.d.mts +6 -5
  286. package/dist/development/graphql/generated/graphql-env.d.mts +459 -16
  287. package/dist/development/graphql/generated/storefront-api-types.d.mts +1 -1
  288. package/dist/development/graphql/graphql.d.mts +33 -0
  289. package/dist/development/graphql/graphql.mjs +30 -0
  290. package/dist/development/graphql/graphql.mjs.map +1 -1
  291. package/dist/development/graphql/type-resolver.d.mts +14 -0
  292. package/dist/development/react/cart.d.mts +85 -14
  293. package/dist/development/react/cart.mjs +123 -3
  294. package/dist/development/react/cart.mjs.map +1 -1
  295. package/dist/development/react/collection.d.mts +2 -4
  296. package/dist/development/react/collection.mjs.map +1 -1
  297. package/dist/development/react/index.d.mts +8 -6
  298. package/dist/development/react/index.mjs +2 -2
  299. package/dist/development/react/predictive-search.d.mts +68 -7
  300. package/dist/development/react/predictive-search.mjs +54 -12
  301. package/dist/development/react/predictive-search.mjs.map +1 -1
  302. package/dist/development/react/product.d.mts +1 -0
  303. package/dist/development/react/product.mjs +5 -2
  304. package/dist/development/react/product.mjs.map +1 -1
  305. package/dist/development/react/shopify-scripts.mjs +1 -0
  306. package/dist/development/react/shopify-scripts.mjs.map +1 -1
  307. package/dist/development/vue/cart.d.mts +85 -6
  308. package/dist/development/vue/cart.mjs +95 -0
  309. package/dist/development/vue/cart.mjs.map +1 -1
  310. package/dist/development/vue/collection.d.mts +83 -5
  311. package/dist/development/vue/collection.mjs +52 -0
  312. package/dist/development/vue/collection.mjs.map +1 -1
  313. package/dist/development/vue/index.d.mts +6 -4
  314. package/dist/development/vue/predictive-search.d.mts +53 -6
  315. package/dist/development/vue/predictive-search.mjs +33 -0
  316. package/dist/development/vue/predictive-search.mjs.map +1 -1
  317. package/dist/development/vue/product.d.mts +6 -2
  318. package/dist/development/vue/product.mjs.map +1 -1
  319. package/dist/development/vue/shopify-scripts.d.mts +30 -3
  320. package/dist/development/vue/shopify-scripts.mjs +6 -0
  321. package/dist/development/vue/shopify-scripts.mjs.map +1 -1
  322. package/dist/globals.d.mts +6 -5
  323. package/dist/graphql/generated/customer-account-graphql-env.d.mts +1717 -1090
  324. package/dist/graphql/generated/graphql-env.d.mts +459 -16
  325. package/dist/graphql/generated/storefront-api-types.d.mts +1 -1
  326. package/dist/graphql/graphql.d.mts +33 -0
  327. package/dist/graphql/graphql.mjs +30 -0
  328. package/dist/graphql/graphql.mjs.map +1 -1
  329. package/dist/graphql/type-resolver.d.mts +14 -0
  330. package/dist/react/cart.d.mts +85 -14
  331. package/dist/react/cart.mjs +123 -3
  332. package/dist/react/cart.mjs.map +1 -1
  333. package/dist/react/collection.d.mts +2 -4
  334. package/dist/react/collection.mjs.map +1 -1
  335. package/dist/react/index.d.mts +8 -6
  336. package/dist/react/index.mjs +2 -2
  337. package/dist/react/predictive-search.d.mts +68 -7
  338. package/dist/react/predictive-search.mjs +54 -12
  339. package/dist/react/predictive-search.mjs.map +1 -1
  340. package/dist/react/product.d.mts +1 -0
  341. package/dist/react/product.mjs +5 -2
  342. package/dist/react/product.mjs.map +1 -1
  343. package/dist/react/shopify-scripts.mjs +1 -0
  344. package/dist/react/shopify-scripts.mjs.map +1 -1
  345. package/dist/storefront-api-types.d.ts +165 -20
  346. package/dist/storefront.schema.json +782 -57
  347. package/dist/vite/customer-account.mjs +191 -0
  348. package/dist/vite/customer-account.mjs.map +1 -0
  349. package/dist/vite/local-https.mjs +13 -29
  350. package/dist/vite/local-https.mjs.map +1 -1
  351. package/dist/vite/mkcert.mjs +1 -1
  352. package/dist/vue/cart.d.mts +85 -6
  353. package/dist/vue/cart.mjs +95 -0
  354. package/dist/vue/cart.mjs.map +1 -1
  355. package/dist/vue/collection.d.mts +83 -5
  356. package/dist/vue/collection.mjs +52 -0
  357. package/dist/vue/collection.mjs.map +1 -1
  358. package/dist/vue/index.d.mts +6 -4
  359. package/dist/vue/predictive-search.d.mts +53 -6
  360. package/dist/vue/predictive-search.mjs +33 -0
  361. package/dist/vue/predictive-search.mjs.map +1 -1
  362. package/dist/vue/product.d.mts +6 -2
  363. package/dist/vue/product.mjs.map +1 -1
  364. package/dist/vue/shopify-scripts.d.mts +31 -4
  365. package/dist/vue/shopify-scripts.mjs +6 -0
  366. package/dist/vue/shopify-scripts.mjs.map +1 -1
  367. package/package.json +5 -2
  368. package/skills/hydrogen-analytics/SKILL.md +6 -5
  369. package/skills/hydrogen-analytics/references/custom-consent.md +52 -0
  370. package/skills/hydrogen-analytics/references/react.md +1 -1
  371. package/skills/hydrogen-cart-metafields/SKILL.md +133 -0
  372. package/skills/hydrogen-cart-ui/SKILL.md +2 -1
  373. package/skills/hydrogen-customer-account/SKILL.md +3 -3
  374. package/skills/hydrogen-local-https/SKILL.md +22 -7
  375. package/skills/hydrogen-request-handlers/SKILL.md +5 -5
  376. package/skills/hydrogen-request-handlers/references/nuxt.md +0 -6
  377. package/skills/hydrogen-setup/references/analytics.md +24 -23
  378. package/skills/hydrogen-setup/steps/1-inspect-app.md +1 -1
  379. package/skills/hydrogen-setup/steps/11-verify.md +1 -0
  380. package/skills/hydrogen-setup/steps/2-scaffold.md +22 -1
  381. package/skills/hydrogen-smoke-test/SKILL.md +2 -2
  382. package/skills/hydrogen-storefront-client/SKILL.md +4 -2
  383. package/skills/hydrogen-variant-form/SKILL.md +10 -4
  384. package/ts-plugin/package.json +5 -0
  385. package/dist/core/shopify-scripts/consent.mjs +0 -17
  386. package/dist/core/shopify-scripts/consent.mjs.map +0 -1
  387. package/dist/core/shopify-scripts/deprecated-cookies.mjs +0 -97
  388. package/dist/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
  389. package/dist/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
  390. package/dist/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
  391. package/dist/core/shopify-scripts/utils/tracking-values.mjs +0 -65
  392. package/dist/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
  393. package/dist/core/shopify-scripts/utils/uuid.mjs +0 -17
  394. package/dist/core/shopify-scripts/utils/uuid.mjs.map +0 -1
  395. package/dist/development/core/shopify-scripts/consent.mjs +0 -17
  396. package/dist/development/core/shopify-scripts/consent.mjs.map +0 -1
  397. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs +0 -97
  398. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
  399. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
  400. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
  401. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs +0 -65
  402. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
  403. package/dist/development/core/shopify-scripts/utils/uuid.mjs +0 -17
  404. package/dist/development/core/shopify-scripts/utils/uuid.mjs.map +0 -1
@@ -1,7 +1,23 @@
1
1
  //#region src/customer-account/errors.ts
2
+ /**
3
+ * Base error for Customer Account API failures. Thrown by
4
+ * `CustomerAccountClient.graphql()` when the API responds with a non-OK
5
+ * HTTP status, when the response body cannot be parsed as JSON, or when the
6
+ * parsed response is missing `data`. Also thrown (without subclassing) for
7
+ * OAuth token-request timeouts and network failures.
8
+ *
9
+ * Not all fields are populated on every instance. `status` is present
10
+ * whenever an HTTP response was received. `requestId` is set when the
11
+ * `x-request-id` response header exists. Both are absent on network
12
+ * failures where no response arrived. `retryAfter` is populated only on
13
+ * non-OK responses.
14
+ */
2
15
  var CustomerAccountApiError = class extends Error {
16
+ /** HTTP status code from the API response. Present whenever an HTTP response was received (including parse failures), absent on network failures where no response arrived. */
3
17
  status;
18
+ /** Value of the `x-request-id` response header, when the API returned one. Useful for Shopify support requests. */
4
19
  requestId;
20
+ /** Value of the `retry-after` response header, when present. Indicates when the client should retry a rate-limited request. */
5
21
  retryAfter;
6
22
  constructor(message, options) {
7
23
  super(message, options?.cause ? { cause: options.cause } : void 0);
@@ -11,13 +27,27 @@ var CustomerAccountApiError = class extends Error {
11
27
  this.retryAfter = options?.retryAfter;
12
28
  }
13
29
  };
30
+ /**
31
+ * Client-side pre-flight check. Thrown when the access token is missing,
32
+ * empty, has leading or trailing whitespace, or contains ASCII control
33
+ * characters (including DEL). Also thrown when the options object is
34
+ * missing. A token the server rejects (e.g. expired or revoked) produces
35
+ * the base {@link CustomerAccountApiError} with `status: 401` instead.
36
+ */
14
37
  var CustomerAccountAuthenticationError = class extends CustomerAccountApiError {
15
38
  constructor(message = "Customer Account API access token is required") {
16
39
  super(message);
17
40
  this.name = "CustomerAccountAuthenticationError";
18
41
  }
19
42
  };
43
+ /**
44
+ * Thrown when a `CustomerAccountClient.graphql()` request exceeds
45
+ * `defaultTimeoutInMs`. (OAuth token-request timeouts throw the base
46
+ * {@link CustomerAccountApiError} instead.) Extends
47
+ * {@link CustomerAccountApiError}.
48
+ */
20
49
  var CustomerAccountTimeoutError = class extends CustomerAccountApiError {
50
+ /** The timeout threshold (in milliseconds) that was exceeded. */
21
51
  timeoutInMs;
22
52
  constructor(timeoutInMs) {
23
53
  super(`Customer Account API request timed out after ${timeoutInMs}ms`);
@@ -25,7 +55,23 @@ var CustomerAccountTimeoutError = class extends CustomerAccountApiError {
25
55
  this.timeoutInMs = timeoutInMs;
26
56
  }
27
57
  };
58
+ /**
59
+ * Thrown during the Customer Account OAuth authorization code exchange
60
+ * or id_token validation in `handleOAuthCallback`. Refresh failures never
61
+ * surface this error (they are caught internally and returned as
62
+ * `undefined`). Extends `Error` directly, **not**
63
+ * {@link CustomerAccountApiError}. A `catch` block for
64
+ * `CustomerAccountApiError` will not catch this error.
65
+ *
66
+ * Use the `code` field for programmatic handling. Known codes:
67
+ * `"missing_callback_params"`, `"state_mismatch"`,
68
+ * `"token_exchange_rejected"`, `"token_exchange_failed"`,
69
+ * `"invalid_token_response"`, `"nonce_mismatch"`, `"issuer_mismatch"`,
70
+ * `"audience_mismatch"`, `"expired_id_token"`, `"invalid_id_token"`,
71
+ * `"missing_pending_login"`.
72
+ */
28
73
  var CustomerAccountOAuthError = class extends Error {
74
+ /** Machine-readable error identifier for programmatic handling. */
29
75
  code;
30
76
  constructor(code, message, options) {
31
77
  super(message, options?.cause ? { cause: options.cause } : void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../../src/customer-account/errors.ts"],"sourcesContent":["export class CustomerAccountApiError extends Error {\n readonly status?: number;\n readonly requestId?: string;\n readonly retryAfter?: string;\n\n constructor(\n message: string,\n options?: { status?: number; requestId?: string; retryAfter?: string; cause?: unknown },\n ) {\n super(message, options?.cause ? { cause: options.cause } : undefined);\n this.name = \"CustomerAccountApiError\";\n this.status = options?.status;\n this.requestId = options?.requestId;\n this.retryAfter = options?.retryAfter;\n }\n}\n\nexport class CustomerAccountAuthenticationError extends CustomerAccountApiError {\n constructor(message = \"Customer Account API access token is required\") {\n super(message);\n this.name = \"CustomerAccountAuthenticationError\";\n }\n}\n\nexport class CustomerAccountTimeoutError extends CustomerAccountApiError {\n readonly timeoutInMs: number;\n\n constructor(timeoutInMs: number) {\n super(`Customer Account API request timed out after ${timeoutInMs}ms`);\n this.name = \"CustomerAccountTimeoutError\";\n this.timeoutInMs = timeoutInMs;\n }\n}\n\nexport class CustomerAccountOAuthError extends Error {\n readonly code: string;\n\n constructor(code: string, message: string, options?: { cause?: unknown }) {\n super(message, options?.cause ? { cause: options.cause } : undefined);\n this.name = \"CustomerAccountOAuthError\";\n this.code = code;\n }\n}\n"],"mappings":";AAAA,IAAa,0BAAb,cAA6C,MAAM;CACjD,AAAS;CACT,AAAS;CACT,AAAS;CAET,YACE,SACA,SACA;AACA,QAAM,SAAS,SAAS,QAAQ,EAAE,OAAO,QAAQ,OAAO,GAAG,OAAU;AACrE,OAAK,OAAO;AACZ,OAAK,SAAS,SAAS;AACvB,OAAK,YAAY,SAAS;AAC1B,OAAK,aAAa,SAAS;;;AAI/B,IAAa,qCAAb,cAAwD,wBAAwB;CAC9E,YAAY,UAAU,iDAAiD;AACrE,QAAM,QAAQ;AACd,OAAK,OAAO;;;AAIhB,IAAa,8BAAb,cAAiD,wBAAwB;CACvE,AAAS;CAET,YAAY,aAAqB;AAC/B,QAAM,gDAAgD,YAAY,IAAI;AACtE,OAAK,OAAO;AACZ,OAAK,cAAc;;;AAIvB,IAAa,4BAAb,cAA+C,MAAM;CACnD,AAAS;CAET,YAAY,MAAc,SAAiB,SAA+B;AACxE,QAAM,SAAS,SAAS,QAAQ,EAAE,OAAO,QAAQ,OAAO,GAAG,OAAU;AACrE,OAAK,OAAO;AACZ,OAAK,OAAO"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../../src/customer-account/errors.ts"],"sourcesContent":["/**\n * Base error for Customer Account API failures. Thrown by\n * `CustomerAccountClient.graphql()` when the API responds with a non-OK\n * HTTP status, when the response body cannot be parsed as JSON, or when the\n * parsed response is missing `data`. Also thrown (without subclassing) for\n * OAuth token-request timeouts and network failures.\n *\n * Not all fields are populated on every instance. `status` is present\n * whenever an HTTP response was received. `requestId` is set when the\n * `x-request-id` response header exists. Both are absent on network\n * failures where no response arrived. `retryAfter` is populated only on\n * non-OK responses.\n */\nexport class CustomerAccountApiError extends Error {\n /** HTTP status code from the API response. Present whenever an HTTP response was received (including parse failures), absent on network failures where no response arrived. */\n readonly status?: number;\n /** Value of the `x-request-id` response header, when the API returned one. Useful for Shopify support requests. */\n readonly requestId?: string;\n /** Value of the `retry-after` response header, when present. Indicates when the client should retry a rate-limited request. */\n readonly retryAfter?: string;\n\n constructor(\n message: string,\n options?: { status?: number; requestId?: string; retryAfter?: string; cause?: unknown },\n ) {\n super(message, options?.cause ? { cause: options.cause } : undefined);\n this.name = \"CustomerAccountApiError\";\n this.status = options?.status;\n this.requestId = options?.requestId;\n this.retryAfter = options?.retryAfter;\n }\n}\n\n/**\n * Client-side pre-flight check. Thrown when the access token is missing,\n * empty, has leading or trailing whitespace, or contains ASCII control\n * characters (including DEL). Also thrown when the options object is\n * missing. A token the server rejects (e.g. expired or revoked) produces\n * the base {@link CustomerAccountApiError} with `status: 401` instead.\n */\nexport class CustomerAccountAuthenticationError extends CustomerAccountApiError {\n constructor(message = \"Customer Account API access token is required\") {\n super(message);\n this.name = \"CustomerAccountAuthenticationError\";\n }\n}\n\n/**\n * Thrown when a `CustomerAccountClient.graphql()` request exceeds\n * `defaultTimeoutInMs`. (OAuth token-request timeouts throw the base\n * {@link CustomerAccountApiError} instead.) Extends\n * {@link CustomerAccountApiError}.\n */\nexport class CustomerAccountTimeoutError extends CustomerAccountApiError {\n /** The timeout threshold (in milliseconds) that was exceeded. */\n readonly timeoutInMs: number;\n\n constructor(timeoutInMs: number) {\n super(`Customer Account API request timed out after ${timeoutInMs}ms`);\n this.name = \"CustomerAccountTimeoutError\";\n this.timeoutInMs = timeoutInMs;\n }\n}\n\n/**\n * Thrown during the Customer Account OAuth authorization code exchange\n * or id_token validation in `handleOAuthCallback`. Refresh failures never\n * surface this error (they are caught internally and returned as\n * `undefined`). Extends `Error` directly, **not**\n * {@link CustomerAccountApiError}. A `catch` block for\n * `CustomerAccountApiError` will not catch this error.\n *\n * Use the `code` field for programmatic handling. Known codes:\n * `\"missing_callback_params\"`, `\"state_mismatch\"`,\n * `\"token_exchange_rejected\"`, `\"token_exchange_failed\"`,\n * `\"invalid_token_response\"`, `\"nonce_mismatch\"`, `\"issuer_mismatch\"`,\n * `\"audience_mismatch\"`, `\"expired_id_token\"`, `\"invalid_id_token\"`,\n * `\"missing_pending_login\"`.\n */\nexport class CustomerAccountOAuthError extends Error {\n /** Machine-readable error identifier for programmatic handling. */\n readonly code: string;\n\n constructor(code: string, message: string, options?: { cause?: unknown }) {\n super(message, options?.cause ? { cause: options.cause } : undefined);\n this.name = \"CustomerAccountOAuthError\";\n this.code = code;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,IAAa,0BAAb,cAA6C,MAAM;;CAEjD,AAAS;;CAET,AAAS;;CAET,AAAS;CAET,YACE,SACA,SACA;AACA,QAAM,SAAS,SAAS,QAAQ,EAAE,OAAO,QAAQ,OAAO,GAAG,OAAU;AACrE,OAAK,OAAO;AACZ,OAAK,SAAS,SAAS;AACvB,OAAK,YAAY,SAAS;AAC1B,OAAK,aAAa,SAAS;;;;;;;;;;AAW/B,IAAa,qCAAb,cAAwD,wBAAwB;CAC9E,YAAY,UAAU,iDAAiD;AACrE,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;;;;AAUhB,IAAa,8BAAb,cAAiD,wBAAwB;;CAEvE,AAAS;CAET,YAAY,aAAqB;AAC/B,QAAM,gDAAgD,YAAY,IAAI;AACtE,OAAK,OAAO;AACZ,OAAK,cAAc;;;;;;;;;;;;;;;;;;AAmBvB,IAAa,4BAAb,cAA+C,MAAM;;CAEnD,AAAS;CAET,YAAY,MAAc,SAAiB,SAA+B;AACxE,QAAM,SAAS,SAAS,QAAQ,EAAE,OAAO,QAAQ,OAAO,GAAG,OAAU;AACrE,OAAK,OAAO;AACZ,OAAK,OAAO"}
@@ -9,20 +9,88 @@ type CustomerAccountTadaGql = initGraphQLTada<{
9
9
  scalars: CustomerAccountScalars;
10
10
  }>;
11
11
  declare const CUSTOMER_ACCOUNT_DOCUMENT: unique symbol;
12
+ /**
13
+ * Branded document type returned by {@link gql}.
14
+ *
15
+ * Carries the GraphQL source as a type-level string literal so that
16
+ * `InferResult` and `InferVariables` can derive typed responses. The
17
+ * `[CUSTOMER_ACCOUNT_DOCUMENT]` brand is private and cannot be constructed
18
+ * externally — only `gql()` produces valid instances. Passing a plain object
19
+ * with a `source` property to `CustomerAccountClient.graphql()` will throw
20
+ * a `TypeError` at runtime.
21
+ */
12
22
  type CustomerAccountDocument<Result = unknown, Variables = never, Source extends string = string> = DocumentDecoration<Result, Variables> & {
13
23
  readonly [CUSTOMER_ACCOUNT_DOCUMENT]: true;
14
24
  readonly source: Source;
15
25
  };
26
+ /** Widened alias for {@link CustomerAccountDocument}. Use as a constraint when accepting any Customer Account document. */
16
27
  type AnyCustomerAccountDocument = CustomerAccountDocument<unknown, never, string>;
28
+ /** Extracts the source string literal type from a {@link CustomerAccountDocument}. */
17
29
  type SourceOf<Doc> = Doc extends {
18
30
  readonly source: infer Source extends string;
19
31
  } ? Source : never;
32
+ /**
33
+ * Recursively concatenates the source strings of an array of fragment
34
+ * documents at the type level, joining each with a newline. An empty
35
+ * tuple produces `""`.
36
+ */
20
37
  type FragmentSources<Fragments extends readonly AnyCustomerAccountDocument[]> = Fragments extends readonly [] ? "" : Fragments extends readonly [infer Only extends AnyCustomerAccountDocument] ? SourceOf<Only> : Fragments extends readonly [infer First extends AnyCustomerAccountDocument, ...infer Rest extends readonly AnyCustomerAccountDocument[]] ? `${SourceOf<First>}\n${FragmentSources<Rest>}` : string;
38
+ /**
39
+ * Combines an operation source with its {@link FragmentSources} at the type
40
+ * level. When `Fragments` is empty the result is just `Source`.
41
+ */
21
42
  type ComposedSource<Source extends string, Fragments extends readonly AnyCustomerAccountDocument[]> = FragmentSources<Fragments> extends "" ? Source : `${Source}\n${FragmentSources<Fragments>}`;
22
43
  type CustomerAccountGql = {
23
44
  <const Source extends string>(source: Source): CustomerAccountDocument<InferResult<Source>, InferVariables<Source>, Source>;
24
45
  <const Source extends string, const Fragments extends readonly AnyCustomerAccountDocument[], const DocumentSource extends string = ComposedSource<Source, Fragments>>(source: Source, fragments: Fragments): CustomerAccountDocument<InferResult<DocumentSource>, InferVariables<DocumentSource>, DocumentSource>;
25
46
  } & CustomerAccountTadaGql;
47
+ /**
48
+ * Creates a branded {@link CustomerAccountDocument} from a GraphQL source
49
+ * string. This is a regular function call, not a tagged template literal.
50
+ *
51
+ * The returned document is branded with a private Symbol that
52
+ * `CustomerAccountClient.graphql()` validates at runtime — passing a plain
53
+ * object with a `source` property will throw a `TypeError`.
54
+ *
55
+ * An optional second argument accepts an array of fragment documents. Fragments
56
+ * are deduplicated by source string identity (`Set` check, not by fragment
57
+ * name) and appended to the operation source.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * import { gql } from "@shopify/hydrogen/customer-account";
62
+ *
63
+ * const CUSTOMER_QUERY = gql(`
64
+ * query CustomerDetails {
65
+ * customer {
66
+ * firstName
67
+ * lastName
68
+ * emailAddress { emailAddress }
69
+ * }
70
+ * }
71
+ * `);
72
+ * ```
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * const ORDER_FIELDS = gql(`
77
+ * fragment OrderFields on Order {
78
+ * id
79
+ * totalPrice { amount currencyCode }
80
+ * }
81
+ * `);
82
+ *
83
+ * const ORDERS_QUERY = gql(`
84
+ * query CustomerOrders {
85
+ * customer {
86
+ * orders(first: 10) {
87
+ * nodes { ...OrderFields }
88
+ * }
89
+ * }
90
+ * }
91
+ * `, [ORDER_FIELDS]);
92
+ * ```
93
+ */
26
94
  declare const gql: CustomerAccountGql;
27
95
  //#endregion
28
96
  export { AnyCustomerAccountDocument, ComposedSource, CustomerAccountDocument, FragmentSources, SourceOf, gql };
@@ -3,6 +3,53 @@ import { isObjectRecord } from "../core/utils/record.mjs";
3
3
  //#region src/customer-account/graphql.ts
4
4
  const CUSTOMER_ACCOUNT_DOCUMENT = Symbol("CustomerAccountDocument");
5
5
  const VARIABLE_DEFINITION_RE = /\$([_A-Za-z][_0-9A-Za-z]*)\s*:/g;
6
+ /**
7
+ * Creates a branded {@link CustomerAccountDocument} from a GraphQL source
8
+ * string. This is a regular function call, not a tagged template literal.
9
+ *
10
+ * The returned document is branded with a private Symbol that
11
+ * `CustomerAccountClient.graphql()` validates at runtime — passing a plain
12
+ * object with a `source` property will throw a `TypeError`.
13
+ *
14
+ * An optional second argument accepts an array of fragment documents. Fragments
15
+ * are deduplicated by source string identity (`Set` check, not by fragment
16
+ * name) and appended to the operation source.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { gql } from "@shopify/hydrogen/customer-account";
21
+ *
22
+ * const CUSTOMER_QUERY = gql(`
23
+ * query CustomerDetails {
24
+ * customer {
25
+ * firstName
26
+ * lastName
27
+ * emailAddress { emailAddress }
28
+ * }
29
+ * }
30
+ * `);
31
+ * ```
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const ORDER_FIELDS = gql(`
36
+ * fragment OrderFields on Order {
37
+ * id
38
+ * totalPrice { amount currencyCode }
39
+ * }
40
+ * `);
41
+ *
42
+ * const ORDERS_QUERY = gql(`
43
+ * query CustomerOrders {
44
+ * customer {
45
+ * orders(first: 10) {
46
+ * nodes { ...OrderFields }
47
+ * }
48
+ * }
49
+ * }
50
+ * `, [ORDER_FIELDS]);
51
+ * ```
52
+ */
6
53
  const gql = ((source, fragments) => {
7
54
  let query = source;
8
55
  if (fragments) {
@@ -1 +1 @@
1
- {"version":3,"file":"graphql.mjs","names":[],"sources":["../../src/customer-account/graphql.ts"],"sourcesContent":["import type { DocumentDecoration, initGraphQLTada as InitGraphQLTada } from \"gql.tada\";\n\nimport { isObjectRecord } from \"../core/utils/record\";\nimport type { introspection } from \"../graphql/generated/customer-account-graphql-env\";\nimport type { CustomerAccountScalars } from \"../graphql/scalars\";\nimport type { InferResult, InferVariables } from \"./type-resolver\";\n\ntype CustomerAccountTadaGql = InitGraphQLTada<{\n introspection: introspection;\n scalars: CustomerAccountScalars;\n}>;\n\nconst CUSTOMER_ACCOUNT_DOCUMENT = Symbol(\"CustomerAccountDocument\");\nconst VARIABLE_DEFINITION_RE = /\\$([_A-Za-z][_0-9A-Za-z]*)\\s*:/g;\n\nexport type CustomerAccountDocument<\n Result = unknown,\n Variables = never,\n Source extends string = string,\n> = DocumentDecoration<Result, Variables> & {\n readonly [CUSTOMER_ACCOUNT_DOCUMENT]: true;\n readonly source: Source;\n};\n\nexport type AnyCustomerAccountDocument = CustomerAccountDocument<unknown, never, string>;\n\nexport type SourceOf<Doc> = Doc extends { readonly source: infer Source extends string }\n ? Source\n : never;\n\nexport type FragmentSources<Fragments extends readonly AnyCustomerAccountDocument[]> =\n Fragments extends readonly []\n ? \"\"\n : Fragments extends readonly [infer Only extends AnyCustomerAccountDocument]\n ? SourceOf<Only>\n : Fragments extends readonly [\n infer First extends AnyCustomerAccountDocument,\n ...infer Rest extends readonly AnyCustomerAccountDocument[],\n ]\n ? `${SourceOf<First>}\\n${FragmentSources<Rest>}`\n : string;\n\nexport type ComposedSource<\n Source extends string,\n Fragments extends readonly AnyCustomerAccountDocument[],\n> = FragmentSources<Fragments> extends \"\" ? Source : `${Source}\\n${FragmentSources<Fragments>}`;\n\ntype CustomerAccountDocumentValue = AnyCustomerAccountDocument & {\n readonly variableNames: ReadonlySet<string>;\n};\n\ntype CustomerAccountGql = {\n <const Source extends string>(\n source: Source,\n ): CustomerAccountDocument<InferResult<Source>, InferVariables<Source>, Source>;\n <\n const Source extends string,\n const Fragments extends readonly AnyCustomerAccountDocument[],\n const DocumentSource extends string = ComposedSource<Source, Fragments>,\n >(\n source: Source,\n fragments: Fragments,\n ): CustomerAccountDocument<\n InferResult<DocumentSource>,\n InferVariables<DocumentSource>,\n DocumentSource\n >;\n} & CustomerAccountTadaGql;\n\n// oxlint-disable-next-line typescript-eslint/consistent-type-assertions -- gql.tada adds phantom helper properties to the function type that are not used at runtime.\nexport const gql = ((source: string, fragments?: readonly CustomerAccountDocument[]) => {\n let query = source;\n\n if (fragments) {\n const seen = new Set<string>();\n for (const fragment of fragments) {\n assertCustomerAccountDocument(fragment);\n if (!seen.has(fragment.source)) {\n seen.add(fragment.source);\n query += \"\\n\" + fragment.source;\n }\n }\n }\n\n const document = {\n [CUSTOMER_ACCOUNT_DOCUMENT]: true,\n source: query,\n variableNames: getVariableNames(query),\n } satisfies CustomerAccountDocumentValue;\n return document;\n}) as unknown as CustomerAccountGql;\n\nexport function isCustomerAccountDocument(value: unknown): value is CustomerAccountDocumentValue {\n if (!isObjectRecord(value)) return false;\n return value[CUSTOMER_ACCOUNT_DOCUMENT] === true && typeof value.source === \"string\";\n}\n\nexport function assertCustomerAccountDocument(\n value: unknown,\n): asserts value is CustomerAccountDocumentValue {\n if (!isCustomerAccountDocument(value)) {\n throw new TypeError(\"Expected a Customer Account API document returned by CAAPI.gql().\");\n }\n}\n\nfunction getVariableNames(source: string): ReadonlySet<string> {\n const names = new Set<string>();\n for (const match of source.matchAll(VARIABLE_DEFINITION_RE)) {\n names.add(match[1]);\n }\n return names;\n}\n"],"mappings":";;;AAYA,MAAM,4BAA4B,OAAO,0BAA0B;AACnE,MAAM,yBAAyB;AAyD/B,MAAa,QAAQ,QAAgB,cAAmD;CACtF,IAAI,QAAQ;AAEZ,KAAI,WAAW;EACb,MAAM,uBAAO,IAAI,KAAa;AAC9B,OAAK,MAAM,YAAY,WAAW;AAChC,iCAA8B,SAAS;AACvC,OAAI,CAAC,KAAK,IAAI,SAAS,OAAO,EAAE;AAC9B,SAAK,IAAI,SAAS,OAAO;AACzB,aAAS,OAAO,SAAS;;;;AAU/B,QAAO;GAJJ,4BAA4B;EAC7B,QAAQ;EACR,eAAe,iBAAiB,MAAM;EAEzB;;AAGjB,SAAgB,0BAA0B,OAAuD;AAC/F,KAAI,CAAC,eAAe,MAAM,CAAE,QAAO;AACnC,QAAO,MAAM,+BAA+B,QAAQ,OAAO,MAAM,WAAW;;AAG9E,SAAgB,8BACd,OAC+C;AAC/C,KAAI,CAAC,0BAA0B,MAAM,CACnC,OAAM,IAAI,UAAU,oEAAoE;;AAI5F,SAAS,iBAAiB,QAAqC;CAC7D,MAAM,wBAAQ,IAAI,KAAa;AAC/B,MAAK,MAAM,SAAS,OAAO,SAAS,uBAAuB,CACzD,OAAM,IAAI,MAAM,GAAG;AAErB,QAAO"}
1
+ {"version":3,"file":"graphql.mjs","names":[],"sources":["../../src/customer-account/graphql.ts"],"sourcesContent":["import type { DocumentDecoration, initGraphQLTada as InitGraphQLTada } from \"gql.tada\";\n\nimport { isObjectRecord } from \"../core/utils/record\";\nimport type { introspection } from \"../graphql/generated/customer-account-graphql-env\";\nimport type { CustomerAccountScalars } from \"../graphql/scalars\";\nimport type { InferResult, InferVariables } from \"./type-resolver\";\n\ntype CustomerAccountTadaGql = InitGraphQLTada<{\n introspection: introspection;\n scalars: CustomerAccountScalars;\n}>;\n\nconst CUSTOMER_ACCOUNT_DOCUMENT = Symbol(\"CustomerAccountDocument\");\nconst VARIABLE_DEFINITION_RE = /\\$([_A-Za-z][_0-9A-Za-z]*)\\s*:/g;\n\n/**\n * Branded document type returned by {@link gql}.\n *\n * Carries the GraphQL source as a type-level string literal so that\n * `InferResult` and `InferVariables` can derive typed responses. The\n * `[CUSTOMER_ACCOUNT_DOCUMENT]` brand is private and cannot be constructed\n * externally — only `gql()` produces valid instances. Passing a plain object\n * with a `source` property to `CustomerAccountClient.graphql()` will throw\n * a `TypeError` at runtime.\n */\nexport type CustomerAccountDocument<\n Result = unknown,\n Variables = never,\n Source extends string = string,\n> = DocumentDecoration<Result, Variables> & {\n readonly [CUSTOMER_ACCOUNT_DOCUMENT]: true;\n readonly source: Source;\n};\n\n/** Widened alias for {@link CustomerAccountDocument}. Use as a constraint when accepting any Customer Account document. */\nexport type AnyCustomerAccountDocument = CustomerAccountDocument<unknown, never, string>;\n\n/** Extracts the source string literal type from a {@link CustomerAccountDocument}. */\nexport type SourceOf<Doc> = Doc extends { readonly source: infer Source extends string }\n ? Source\n : never;\n\n/**\n * Recursively concatenates the source strings of an array of fragment\n * documents at the type level, joining each with a newline. An empty\n * tuple produces `\"\"`.\n */\nexport type FragmentSources<Fragments extends readonly AnyCustomerAccountDocument[]> =\n Fragments extends readonly []\n ? \"\"\n : Fragments extends readonly [infer Only extends AnyCustomerAccountDocument]\n ? SourceOf<Only>\n : Fragments extends readonly [\n infer First extends AnyCustomerAccountDocument,\n ...infer Rest extends readonly AnyCustomerAccountDocument[],\n ]\n ? `${SourceOf<First>}\\n${FragmentSources<Rest>}`\n : string;\n\n/**\n * Combines an operation source with its {@link FragmentSources} at the type\n * level. When `Fragments` is empty the result is just `Source`.\n */\nexport type ComposedSource<\n Source extends string,\n Fragments extends readonly AnyCustomerAccountDocument[],\n> = FragmentSources<Fragments> extends \"\" ? Source : `${Source}\\n${FragmentSources<Fragments>}`;\n\ntype CustomerAccountDocumentValue = AnyCustomerAccountDocument & {\n readonly variableNames: ReadonlySet<string>;\n};\n\ntype CustomerAccountGql = {\n <const Source extends string>(\n source: Source,\n ): CustomerAccountDocument<InferResult<Source>, InferVariables<Source>, Source>;\n <\n const Source extends string,\n const Fragments extends readonly AnyCustomerAccountDocument[],\n const DocumentSource extends string = ComposedSource<Source, Fragments>,\n >(\n source: Source,\n fragments: Fragments,\n ): CustomerAccountDocument<\n InferResult<DocumentSource>,\n InferVariables<DocumentSource>,\n DocumentSource\n >;\n} & CustomerAccountTadaGql;\n\n/**\n * Creates a branded {@link CustomerAccountDocument} from a GraphQL source\n * string. This is a regular function call, not a tagged template literal.\n *\n * The returned document is branded with a private Symbol that\n * `CustomerAccountClient.graphql()` validates at runtime — passing a plain\n * object with a `source` property will throw a `TypeError`.\n *\n * An optional second argument accepts an array of fragment documents. Fragments\n * are deduplicated by source string identity (`Set` check, not by fragment\n * name) and appended to the operation source.\n *\n * @example\n * ```ts\n * import { gql } from \"@shopify/hydrogen/customer-account\";\n *\n * const CUSTOMER_QUERY = gql(`\n * query CustomerDetails {\n * customer {\n * firstName\n * lastName\n * emailAddress { emailAddress }\n * }\n * }\n * `);\n * ```\n *\n * @example\n * ```ts\n * const ORDER_FIELDS = gql(`\n * fragment OrderFields on Order {\n * id\n * totalPrice { amount currencyCode }\n * }\n * `);\n *\n * const ORDERS_QUERY = gql(`\n * query CustomerOrders {\n * customer {\n * orders(first: 10) {\n * nodes { ...OrderFields }\n * }\n * }\n * }\n * `, [ORDER_FIELDS]);\n * ```\n */\n// oxlint-disable-next-line typescript-eslint/consistent-type-assertions -- gql.tada adds phantom helper properties to the function type that are not used at runtime.\nexport const gql = ((source: string, fragments?: readonly CustomerAccountDocument[]) => {\n let query = source;\n\n if (fragments) {\n const seen = new Set<string>();\n for (const fragment of fragments) {\n assertCustomerAccountDocument(fragment);\n if (!seen.has(fragment.source)) {\n seen.add(fragment.source);\n query += \"\\n\" + fragment.source;\n }\n }\n }\n\n const document = {\n [CUSTOMER_ACCOUNT_DOCUMENT]: true,\n source: query,\n variableNames: getVariableNames(query),\n } satisfies CustomerAccountDocumentValue;\n return document;\n}) as unknown as CustomerAccountGql;\n\nexport function isCustomerAccountDocument(value: unknown): value is CustomerAccountDocumentValue {\n if (!isObjectRecord(value)) return false;\n return value[CUSTOMER_ACCOUNT_DOCUMENT] === true && typeof value.source === \"string\";\n}\n\nexport function assertCustomerAccountDocument(\n value: unknown,\n): asserts value is CustomerAccountDocumentValue {\n if (!isCustomerAccountDocument(value)) {\n throw new TypeError(\"Expected a Customer Account API document returned by CAAPI.gql().\");\n }\n}\n\nfunction getVariableNames(source: string): ReadonlySet<string> {\n const names = new Set<string>();\n for (const match of source.matchAll(VARIABLE_DEFINITION_RE)) {\n names.add(match[1]);\n }\n return names;\n}\n"],"mappings":";;;AAYA,MAAM,4BAA4B,OAAO,0BAA0B;AACnE,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6H/B,MAAa,QAAQ,QAAgB,cAAmD;CACtF,IAAI,QAAQ;AAEZ,KAAI,WAAW;EACb,MAAM,uBAAO,IAAI,KAAa;AAC9B,OAAK,MAAM,YAAY,WAAW;AAChC,iCAA8B,SAAS;AACvC,OAAI,CAAC,KAAK,IAAI,SAAS,OAAO,EAAE;AAC9B,SAAK,IAAI,SAAS,OAAO;AACzB,aAAS,OAAO,SAAS;;;;AAU/B,QAAO;GAJJ,4BAA4B;EAC7B,QAAQ;EACR,eAAe,iBAAiB,MAAM;EAEzB;;AAGjB,SAAgB,0BAA0B,OAAuD;AAC/F,KAAI,CAAC,eAAe,MAAM,CAAE,QAAO;AACnC,QAAO,MAAM,+BAA+B,QAAQ,OAAO,MAAM,WAAW;;AAG9E,SAAgB,8BACd,OAC+C;AAC/C,KAAI,CAAC,0BAA0B,MAAM,CACnC,OAAM,IAAI,UAAU,oEAAoE;;AAI5F,SAAS,iBAAiB,QAAqC;CAC7D,MAAM,wBAAQ,IAAI,KAAa;AAC/B,MAAK,MAAM,SAAS,OAAO,SAAS,uBAAuB,CACzD,OAAM,IAAI,MAAM,GAAG;AAErB,QAAO"}
@@ -3,11 +3,16 @@ import { CallableRouteHandler, ShopifyRouteErrorResult, ShopifyRouteHandlerConte
3
3
  import { CartBuyerIdentitySyncSource } from "../core/cart/buyer-identity-sync.mjs";
4
4
 
5
5
  //#region src/customer-account/session.d.ts
6
+ /** OAuth callback route path: `"/account/authorize"`. */
6
7
  declare const CUSTOMER_ACCOUNT_AUTHORIZE_PATH: "/account/authorize";
8
+ /** Login route path: `"/account/login"`. */
7
9
  declare const CUSTOMER_ACCOUNT_LOGIN_PATH: "/account/login";
10
+ /** Logout route path: `"/account/logout"`. */
8
11
  declare const CUSTOMER_ACCOUNT_LOGOUT_PATH: "/account/logout";
12
+ /** Token refresh route path: `"/account/refresh"`. */
9
13
  declare const CUSTOMER_ACCOUNT_REFRESH_PATH: "/account/refresh";
10
14
  declare const CUSTOMER_SESSION_INTERNAL_BRAND: unique symbol;
15
+ /** A value that is either `T` or a `Promise<T>`, used as the return type of session manager methods. */
11
16
  type Awaitable<T> = T | Promise<T>;
12
17
  /** Read-only Customer Account session storage for UI state and strict access-token reads. */
13
18
  type ReadonlyCustomerSessionManager = {
@@ -15,50 +20,117 @@ type ReadonlyCustomerSessionManager = {
15
20
  };
16
21
  /** Writable Customer Account session storage for OAuth, refresh, and logout response boundaries. */
17
22
  type WritableCustomerSessionManager = ShopifyRouteSessionManager;
23
+ /** Options for {@link createCustomerSession}. */
18
24
  type CreateCustomerSessionOptions = {
19
- shopId: string;
25
+ /** Numeric Shopify shop ID as a string (digits only, e.g. `"12345"`). Validated against `/^\d+$/`. */shopId: string; /** Customer Account API OAuth client ID. Must be a non-empty string. */
20
26
  customerAccountApiClientId: string;
21
- customerAccountApiUrl?: string;
22
- fetch?: typeof globalThis.fetch;
27
+ /**
28
+ * Overrides the default `https://shopify.com/authentication/{shopId}` base URL
29
+ * for OAuth endpoints (authorize, token, logout). Also used as the expected
30
+ * `iss` claim when validating id_tokens — pointing this at a proxy causes
31
+ * `issuer_mismatch` unless the proxy preserves the original issuer. Must use
32
+ * HTTPS; validated at construction.
33
+ */
34
+ customerAccountApiUrl?: string; /** Custom fetch implementation. Falls back to `globalThis.fetch` when omitted. */
35
+ fetch?: typeof globalThis.fetch; /** Timeout for OAuth token requests in milliseconds. Defaults to 30 000 ms. */
23
36
  defaultTimeoutInMs?: number;
24
37
  };
38
+ /** Options for {@link CustomerSession.prepareLoginUrl}. */
25
39
  type PrepareLoginUrlOptions = {
40
+ /**
41
+ * Overrides the origin used for the OAuth redirect URI. Must be HTTPS —
42
+ * unlike `createCustomerAccountClient`, `http://localhost` is not accepted.
43
+ * Falls back to the session manager's `getSessionOrigin()`.
44
+ */
26
45
  origin?: string;
27
- returnTo?: string;
28
- locale?: string;
29
- countryCode?: string;
30
- loginHint?: string;
31
- loginHintMode?: string;
46
+ /**
47
+ * Same-origin path or URL to redirect back to after login, such as `"/account/orders"`.
48
+ * Cross-origin values and values over 2 048 bytes fall back to the default, `"/account"`.
49
+ */
50
+ returnTo?: string; /** Passed as the `locale` search param on the Shopify OAuth authorize URL. */
51
+ locale?: string; /** Passed as the `region_country` search param on the Shopify OAuth authorize URL. */
52
+ countryCode?: string; /** Email address to pre-fill on the login screen. Passed as `login_hint`. */
53
+ loginHint?: string; /** When set to `"submit"` (and `loginHint` is provided), skips the login form and sends the one-time code immediately. Passed as `login_hint_mode`. */
54
+ loginHintMode?: string; /** Passed as the `acr_values` search param for authentication context class. */
32
55
  acrValues?: string;
33
56
  };
57
+ /**
58
+ * Origin override accepted by {@link CustomerSession.getOrRefreshAccessToken}
59
+ * and {@link CustomerSession.logout}. Falls back to the session manager's
60
+ * `getSessionOrigin()`.
61
+ */
34
62
  type RequestOriginOptions = {
35
- origin?: string;
63
+ /** HTTPS origin string. */origin?: string;
36
64
  };
65
+ /** Options for {@link CustomerSession.logout}. */
37
66
  type LogoutOptions = RequestOriginOptions & {
67
+ /**
68
+ * URL to redirect to after Shopify's IdP logout completes.
69
+ * Resolved as an absolute same-origin URL. Defaults to `"/"`.
70
+ */
38
71
  postLogoutRedirectUri?: string;
39
72
  };
73
+ /**
74
+ * Core session interface returned by {@link createCustomerSession}. Server-only.
75
+ * All methods require a session manager and request context.
76
+ */
40
77
  type CustomerSession = {
41
78
  /**
42
- * Read-only signed-in check for UI state. Returns true for a usable access token
43
- * or a refresh token that can attempt to restore one later. Not an authorization
44
- * check for private Customer Account data.
79
+ * Read-only signed-in check for UI state. Returns `true` when a usable access
80
+ * token OR a refresh token exists. Does not attempt a refresh. Use for
81
+ * conditional UI rendering, not for authorizing access to Customer Account data.
82
+ */
83
+ isLoggedIn(sessionManager: ReadonlyCustomerSessionManager, requestContext: ShopifyRequestContext): Promise<boolean>;
84
+ /**
85
+ * Returns only a currently usable access token, or `undefined` when the token
86
+ * is missing or expired. Never refreshes and never writes session state.
45
87
  */
46
- isLoggedIn(sessionManager: ReadonlyCustomerSessionManager, requestContext: ShopifyRequestContext): Promise<boolean>; /** Returns only a currently usable access token. Does not refresh or mutate session state. */
47
88
  getAccessToken(sessionManager: ReadonlyCustomerSessionManager, requestContext: ShopifyRequestContext): Promise<string | undefined>;
48
89
  /**
49
- * Returns a usable access token, writing updated session state through the
50
- * supplied writable session manager when no usable access token is stored. Use
51
- * only where the eventual response commits the session manager.
90
+ * Returns a usable access token, refreshing from Shopify's token endpoint and
91
+ * writing updated tokens through the writable session manager when needed.
92
+ * Returns `undefined` when no refresh token exists (unauthenticated) or when
93
+ * the refresh fails transiently (the session is preserved for retry). A
94
+ * 400/401 from the token endpoint clears stored tokens. Never throws for
95
+ * refresh failures. Concurrent refresh requests for the same origin+token
96
+ * combination are deduplicated per `createCustomerSession` instance. Use only
97
+ * where the eventual response commits the session manager.
52
98
  */
53
99
  getOrRefreshAccessToken(sessionManager: WritableCustomerSessionManager, requestContext: ShopifyRequestContext, options?: RequestOriginOptions): Promise<string | undefined>;
100
+ /**
101
+ * Builds a Shopify OAuth authorize URL with PKCE (S256). Stores pending login
102
+ * state (state, nonce, code verifier, returnTo, origin) in the session. Pending
103
+ * login state expires after 10 minutes. Returns the full authorize URL string.
104
+ */
54
105
  prepareLoginUrl(sessionManager: WritableCustomerSessionManager, requestContext: ShopifyRequestContext, options: PrepareLoginUrlOptions): Promise<string>;
106
+ /**
107
+ * Completes the OAuth authorization code exchange. Validates `state`, exchanges
108
+ * the code for tokens, validates the `id_token` claims (nonce, issuer, audience,
109
+ * expiry), and stores tokens in the session. Returns the `returnTo` path. Clears
110
+ * pending login state on failure.
111
+ *
112
+ * @throws {CustomerAccountOAuthError} On validation failures (`missing_callback_params`,
113
+ * `state_mismatch`, `missing_pending_login`, `token_exchange_rejected`,
114
+ * `token_exchange_failed`, `invalid_token_response`, or id_token claim mismatches).
115
+ * @throws {CustomerAccountApiError} On token-request timeout.
116
+ */
55
117
  handleOAuthCallback(sessionManager: WritableCustomerSessionManager, requestContext: ShopifyRequestContext, request: Request): Promise<string>;
118
+ /**
119
+ * Clears session tokens. When an `id_token` exists, returns the Shopify IdP
120
+ * logout URL (with `id_token_hint` and `post_logout_redirect_uri`). When no
121
+ * `id_token` is stored, returns the `postLogoutRedirectUri` directly (local-only
122
+ * logout, no IdP round-trip).
123
+ */
56
124
  logout(sessionManager: WritableCustomerSessionManager, requestContext: ShopifyRequestContext, options?: LogoutOptions): Promise<string>;
57
125
  };
126
+ /**
127
+ * Route handler objects returned by {@link createCustomerAccountServerHandlers}.
128
+ * Register these with `handleShopifyRoutes` to serve the Customer Account OAuth flow.
129
+ */
58
130
  type CustomerAccountServerHandlers<TContext extends CustomerAccountRouteHandlerContext = CustomerAccountRouteHandlerContext> = {
59
- authorize: CallableRouteHandler<TContext, CustomerAccountRouteResult, typeof CUSTOMER_ACCOUNT_AUTHORIZE_PATH, "GET">;
60
- login: CallableRouteHandler<TContext, CustomerAccountRouteResult, typeof CUSTOMER_ACCOUNT_LOGIN_PATH, "GET">;
61
- logout: CallableRouteHandler<TContext, CustomerAccountRouteResult, typeof CUSTOMER_ACCOUNT_LOGOUT_PATH, "POST">;
131
+ /** GET handler for the OAuth callback path (`/account/authorize`). */authorize: CallableRouteHandler<TContext, CustomerAccountRouteResult, typeof CUSTOMER_ACCOUNT_AUTHORIZE_PATH, "GET">; /** GET handler that redirects to Shopify OAuth (`/account/login`). */
132
+ login: CallableRouteHandler<TContext, CustomerAccountRouteResult, typeof CUSTOMER_ACCOUNT_LOGIN_PATH, "GET">; /** POST handler for logout (`/account/logout`). Requires a same-origin POST for CSRF protection. */
133
+ logout: CallableRouteHandler<TContext, CustomerAccountRouteResult, typeof CUSTOMER_ACCOUNT_LOGOUT_PATH, "POST">; /** GET handler that refreshes the access token (`/account/refresh`). */
62
134
  refresh: CallableRouteHandler<TContext, CustomerAccountRouteResult, typeof CUSTOMER_ACCOUNT_REFRESH_PATH, "GET">;
63
135
  };
64
136
  /** Customer Account handlers that also synchronize cart buyer identity, so their context requires `storefrontClient`. */
@@ -67,12 +139,13 @@ type CustomerSessionWithInternals = CustomerSession & {
67
139
  readonly [CUSTOMER_SESSION_INTERNAL_BRAND]: true;
68
140
  };
69
141
  type CreateCustomerAccountServerHandlersBaseOptions<TCustomerSession extends CustomerSession = CustomerSession> = {
70
- customerSession: TCustomerSession;
71
- defaultPostLoginRedirectPathname?: string;
72
- loginFailedRedirectPath?: string;
73
- origin?: string | ((request: Request) => string);
142
+ /** The session object returned by {@link createCustomerSession}. */customerSession: TCustomerSession; /** Path to redirect to after a successful login when `return_to` is missing or cross-origin. Defaults to `"/"`. */
143
+ defaultPostLoginRedirectPathname?: string; /** Same-origin path to redirect to when the OAuth callback throws a `CustomerAccountOAuthError` (other errors propagate). Defaults to `"/account?login=failed"`. Cross-origin values fall back to `"/account"`. */
144
+ loginFailedRedirectPath?: string; /** Static origin string, or a function that resolves the origin per request for dynamic multi-origin setups. */
145
+ origin?: string | ((request: Request) => string); /** URL to redirect to after logout. Defaults to `"/"`. */
74
146
  postLogoutRedirectUri?: string;
75
147
  };
148
+ /** Options for {@link createCustomerAccountServerHandlers}. */
76
149
  type CreateCustomerAccountServerHandlersOptions = (CreateCustomerAccountServerHandlersBaseOptions & {
77
150
  cartServerHandlers?: undefined;
78
151
  }) | (CreateCustomerAccountServerHandlersBaseOptions<CustomerSessionWithInternals> & {
@@ -95,6 +168,24 @@ type CustomerAccountRouteHandlerContext = {
95
168
  type CustomerAccountServerHandlersForOptions<TOptions> = TOptions extends {
96
169
  cartServerHandlers: CartBuyerIdentitySyncSource;
97
170
  } ? CustomerAccountServerHandlersWithCartSync : CustomerAccountServerHandlers;
171
+ /**
172
+ * Creates a {@link CustomerSession} that manages the Shopify Customer Account
173
+ * OAuth flow (PKCE, token exchange, refresh, logout). Server-only: throws in a
174
+ * browser context. Validates `shopId`, `customerAccountApiClientId`,
175
+ * `customerAccountApiUrl` (HTTPS), and timeout at construction.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * import { createCustomerSession } from '@shopify/hydrogen/customer-account';
180
+ *
181
+ * const customerSession = createCustomerSession({
182
+ * shopId: env.SHOP_ID,
183
+ * customerAccountApiClientId: env.CUSTOMER_ACCOUNT_API_CLIENT_ID,
184
+ * });
185
+ * ```
186
+ *
187
+ * @throws {Error} When called in a browser context, when an option fails validation, or when no `fetch` is available.
188
+ */
98
189
  declare function createCustomerSession({
99
190
  shopId,
100
191
  customerAccountApiClientId,
@@ -1,15 +1,19 @@
1
1
  import { getLogger } from "../core/logging/logging.mjs";
2
+ import { CUSTOMER_ACCOUNT_PATHS, getSameOriginPath } from "../core/url.mjs";
2
3
  import { DEFAULT_TIMEOUT_IN_MS } from "../core/constants.mjs";
3
- import { CUSTOMER_ACCOUNT_PATHS } from "../core/url.mjs";
4
4
  import { createCallableRouteHandler } from "../core/request-routing/registered-routes.mjs";
5
5
  import { getCartBuyerIdentitySync } from "../core/cart/buyer-identity-sync.mjs";
6
6
  import { CustomerAccountApiError, CustomerAccountOAuthError } from "./errors.mjs";
7
7
 
8
8
  //#region src/customer-account/session.ts
9
9
  const log = getLogger("customer-account");
10
+ /** OAuth callback route path: `"/account/authorize"`. */
10
11
  const CUSTOMER_ACCOUNT_AUTHORIZE_PATH = CUSTOMER_ACCOUNT_PATHS.authorize;
12
+ /** Login route path: `"/account/login"`. */
11
13
  const CUSTOMER_ACCOUNT_LOGIN_PATH = CUSTOMER_ACCOUNT_PATHS.login;
14
+ /** Logout route path: `"/account/logout"`. */
12
15
  const CUSTOMER_ACCOUNT_LOGOUT_PATH = CUSTOMER_ACCOUNT_PATHS.logout;
16
+ /** Token refresh route path: `"/account/refresh"`. */
13
17
  const CUSTOMER_ACCOUNT_REFRESH_PATH = CUSTOMER_ACCOUNT_PATHS.refresh;
14
18
  const CUSTOMER_ACCOUNT_SESSION_KEY = "customerAccount";
15
19
  const DEFAULT_LOGIN_RETURN_TO_PATH = "/account";
@@ -24,7 +28,7 @@ const AUTHORIZATION_CODE_GRANT_TYPE = "authorization_code";
24
28
  const REFRESH_TOKEN_GRANT_TYPE = "refresh_token";
25
29
  const CUSTOMER_ACCOUNT_SCOPE = "openid email customer-account-api:full";
26
30
  const CODE_CHALLENGE_METHOD = "S256";
27
- const USER_AGENT = `Hydrogen 2026.10.0-preview.2`;
31
+ const USER_AGENT = `Hydrogen 2026.10.0-preview.4`;
28
32
  const EXPIRY_BUFFER_IN_SECONDS = 120;
29
33
  const PENDING_LOGIN_TTL_IN_MINUTES = 10;
30
34
  const SECONDS_PER_MINUTE = 60;
@@ -38,6 +42,24 @@ const CUSTOMER_SESSION_ACCESS_TOKEN_PERSONALIZATION_REASON = "customer-session-a
38
42
  const CUSTOMER_SESSION_MUTATION_PERSONALIZATION_REASON = "customer-session-mutation";
39
43
  const CUSTOMER_SESSION_INTERNAL_BRAND = Symbol("hydrogen.customerSessionInternal");
40
44
  const customerSessionInternals = /* @__PURE__ */ new WeakMap();
45
+ /**
46
+ * Creates a {@link CustomerSession} that manages the Shopify Customer Account
47
+ * OAuth flow (PKCE, token exchange, refresh, logout). Server-only: throws in a
48
+ * browser context. Validates `shopId`, `customerAccountApiClientId`,
49
+ * `customerAccountApiUrl` (HTTPS), and timeout at construction.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * import { createCustomerSession } from '@shopify/hydrogen/customer-account';
54
+ *
55
+ * const customerSession = createCustomerSession({
56
+ * shopId: env.SHOP_ID,
57
+ * customerAccountApiClientId: env.CUSTOMER_ACCOUNT_API_CLIENT_ID,
58
+ * });
59
+ * ```
60
+ *
61
+ * @throws {Error} When called in a browser context, when an option fails validation, or when no `fetch` is available.
62
+ */
41
63
  function createCustomerSession({ shopId, customerAccountApiClientId, customerAccountApiUrl, fetch: customFetch, defaultTimeoutInMs = DEFAULT_TIMEOUT_IN_MS }) {
42
64
  if (typeof document !== "undefined") throw new Error("Customer Account OAuth sessions cannot be used in a browser context. Use this helper from server or edge routes only.");
43
65
  validateShopId(shopId);
@@ -655,16 +677,9 @@ function setOptionalLoginParams(loginUrl, options) {
655
677
  if (options.loginHintMode) loginUrl.searchParams.set("login_hint_mode", options.loginHintMode);
656
678
  }
657
679
  function sanitizeReturnTo(returnTo, origin, fallbackReturnTo = DEFAULT_LOGIN_RETURN_TO_PATH) {
658
- if (!returnTo) return fallbackReturnTo;
659
- try {
660
- const url = new URL(returnTo, origin);
661
- if (url.origin !== origin) return fallbackReturnTo;
662
- const sanitizedReturnTo = `${url.pathname}${url.search}${url.hash}`;
663
- if (new TextEncoder().encode(sanitizedReturnTo).byteLength > MAX_RETURN_TO_LENGTH_IN_BYTES) return fallbackReturnTo;
664
- return sanitizedReturnTo;
665
- } catch {
666
- return fallbackReturnTo;
667
- }
680
+ const path = getSameOriginPath(returnTo, origin);
681
+ if (!path || new TextEncoder().encode(path).byteLength > MAX_RETURN_TO_LENGTH_IN_BYTES) return fallbackReturnTo;
682
+ return path;
668
683
  }
669
684
  function absoluteSameOriginUrl(url, origin) {
670
685
  const parsedUrl = new URL(url, origin);