@shopify/hydrogen 2026.10.0-preview.4 → 2026.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. package/dist/cli/index.mjs +23 -12
  2. package/dist/client/client.d.mts +18 -15
  3. package/dist/client/client.mjs +8 -3
  4. package/dist/client/client.mjs.map +1 -1
  5. package/dist/client/errors.d.mts +35 -13
  6. package/dist/client/errors.mjs +23 -13
  7. package/dist/client/errors.mjs.map +1 -1
  8. package/dist/client/types.d.mts +140 -90
  9. package/dist/core/analytics/cart-tracker.d.mts +19 -10
  10. package/dist/core/analytics/cart-tracker.mjs +18 -10
  11. package/dist/core/analytics/cart-tracker.mjs.map +1 -1
  12. package/dist/core/analytics/events.d.mts +17 -11
  13. package/dist/core/analytics/events.mjs +13 -4
  14. package/dist/core/analytics/events.mjs.map +1 -1
  15. package/dist/core/analytics/types.d.mts +207 -118
  16. package/dist/core/analytics/utils/flatten-connection.d.mts +11 -9
  17. package/dist/core/analytics/utils/flatten-connection.mjs +7 -6
  18. package/dist/core/analytics/utils/flatten-connection.mjs.map +1 -1
  19. package/dist/core/cache/fetch-with-cache.d.mts +24 -4
  20. package/dist/core/cache/fetch-with-cache.mjs +17 -2
  21. package/dist/core/cache/fetch-with-cache.mjs.map +1 -1
  22. package/dist/core/cache/key.d.mts +2 -0
  23. package/dist/core/cache/key.mjs.map +1 -1
  24. package/dist/core/cache/run-with-cache.d.mts +56 -8
  25. package/dist/core/cache/run-with-cache.mjs +17 -6
  26. package/dist/core/cache/run-with-cache.mjs.map +1 -1
  27. package/dist/core/cache/store.d.mts +8 -6
  28. package/dist/core/cache/store.mjs.map +1 -1
  29. package/dist/core/cache/strategies.d.mts +27 -2
  30. package/dist/core/cache/strategies.mjs +6 -0
  31. package/dist/core/cache/strategies.mjs.map +1 -1
  32. package/dist/core/cart/actions.d.mts +37 -30
  33. package/dist/core/cart/actions.mjs +21 -9
  34. package/dist/core/cart/actions.mjs.map +1 -1
  35. package/dist/core/cart/attach-quantity-input.d.mts +8 -7
  36. package/dist/core/cart/attach-quantity-input.mjs +8 -7
  37. package/dist/core/cart/attach-quantity-input.mjs.map +1 -1
  38. package/dist/core/cart/buyer-identity-sync.d.mts +7 -6
  39. package/dist/core/cart/buyer-identity-sync.mjs.map +1 -1
  40. package/dist/core/cart/cart.d.mts +59 -46
  41. package/dist/core/cart/cart.mjs +46 -29
  42. package/dist/core/cart/cart.mjs.map +1 -1
  43. package/dist/core/cart/cookie.d.mts +8 -4
  44. package/dist/core/cart/cookie.mjs +8 -4
  45. package/dist/core/cart/cookie.mjs.map +1 -1
  46. package/dist/core/cart/form.d.mts +34 -20
  47. package/dist/core/cart/form.mjs +6 -4
  48. package/dist/core/cart/form.mjs.map +1 -1
  49. package/dist/core/cart/get-cart.d.mts +27 -15
  50. package/dist/core/cart/get-cart.mjs +13 -8
  51. package/dist/core/cart/get-cart.mjs.map +1 -1
  52. package/dist/core/cart/quantity.d.mts +16 -5
  53. package/dist/core/cart/quantity.mjs +16 -5
  54. package/dist/core/cart/quantity.mjs.map +1 -1
  55. package/dist/core/cart/queries.d.mts +25 -19
  56. package/dist/core/cart/queries.mjs +15 -1
  57. package/dist/core/cart/queries.mjs.map +1 -1
  58. package/dist/core/cart/server-handlers.d.mts +82 -45
  59. package/dist/core/cart/server-handlers.mjs +4 -0
  60. package/dist/core/cart/server-handlers.mjs.map +1 -1
  61. package/dist/core/cart/state.d.mts +110 -64
  62. package/dist/core/cart/state.mjs +24 -6
  63. package/dist/core/cart/state.mjs.map +1 -1
  64. package/dist/core/collection/collection.d.mts +52 -53
  65. package/dist/core/collection/collection.mjs +5 -3
  66. package/dist/core/collection/collection.mjs.map +1 -1
  67. package/dist/core/collection/reconciler.d.mts +23 -16
  68. package/dist/core/collection/reconciler.mjs +8 -4
  69. package/dist/core/collection/reconciler.mjs.map +1 -1
  70. package/dist/core/collection/state.d.mts +31 -26
  71. package/dist/core/collection/state.mjs +4 -3
  72. package/dist/core/collection/state.mjs.map +1 -1
  73. package/dist/core/collection/url.d.mts +70 -29
  74. package/dist/core/collection/url.mjs +63 -25
  75. package/dist/core/collection/url.mjs.map +1 -1
  76. package/dist/core/constants.d.mts +1 -0
  77. package/dist/core/constants.mjs +1 -0
  78. package/dist/core/constants.mjs.map +1 -1
  79. package/dist/core/logging/logging.d.mts +14 -8
  80. package/dist/core/logging/logging.mjs +15 -10
  81. package/dist/core/logging/logging.mjs.map +1 -1
  82. package/dist/core/logging/types.d.mts +28 -11
  83. package/dist/core/logging/types.mjs.map +1 -1
  84. package/dist/core/money/format.d.mts +6 -4
  85. package/dist/core/money/format.mjs +19 -9
  86. package/dist/core/money/format.mjs.map +1 -1
  87. package/dist/core/money/types.d.mts +41 -25
  88. package/dist/core/predictive-search/form.d.mts +37 -7
  89. package/dist/core/predictive-search/form.mjs +21 -3
  90. package/dist/core/predictive-search/form.mjs.map +1 -1
  91. package/dist/core/predictive-search/queries.d.mts +18 -30
  92. package/dist/core/predictive-search/queries.mjs +7 -3
  93. package/dist/core/predictive-search/queries.mjs.map +1 -1
  94. package/dist/core/predictive-search/search.d.mts +25 -27
  95. package/dist/core/predictive-search/search.mjs +20 -19
  96. package/dist/core/predictive-search/search.mjs.map +1 -1
  97. package/dist/core/predictive-search/server-handlers.d.mts +25 -26
  98. package/dist/core/predictive-search/server-handlers.mjs +4 -0
  99. package/dist/core/predictive-search/server-handlers.mjs.map +1 -1
  100. package/dist/core/predictive-search/store.d.mts +65 -24
  101. package/dist/core/predictive-search/store.mjs +12 -1
  102. package/dist/core/predictive-search/store.mjs.map +1 -1
  103. package/dist/core/predictive-search/url.d.mts +36 -37
  104. package/dist/core/predictive-search/url.mjs +14 -6
  105. package/dist/core/predictive-search/url.mjs.map +1 -1
  106. package/dist/core/product/form.d.mts +63 -16
  107. package/dist/core/product/form.mjs +8 -2
  108. package/dist/core/product/form.mjs.map +1 -1
  109. package/dist/core/product/options.d.mts +8 -10
  110. package/dist/core/product/options.mjs +8 -10
  111. package/dist/core/product/options.mjs.map +1 -1
  112. package/dist/core/product/product-form.d.mts +77 -29
  113. package/dist/core/product/product-form.mjs +32 -10
  114. package/dist/core/product/product-form.mjs.map +1 -1
  115. package/dist/core/product/state.d.mts +55 -32
  116. package/dist/core/product/url.d.mts +19 -13
  117. package/dist/core/product/url.mjs +10 -10
  118. package/dist/core/product/url.mjs.map +1 -1
  119. package/dist/core/request-context.d.mts +77 -19
  120. package/dist/core/request-context.mjs +5 -1
  121. package/dist/core/request-context.mjs.map +1 -1
  122. package/dist/core/request-routing/handle-shopify-redirects.d.mts +12 -4
  123. package/dist/core/request-routing/handle-shopify-redirects.mjs +9 -2
  124. package/dist/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
  125. package/dist/core/request-routing/handle-shopify-routes.d.mts +5 -5
  126. package/dist/core/request-routing/handle-shopify-routes.mjs +7 -5
  127. package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  128. package/dist/core/request-routing/interceptors/proxy.mjs +79 -43
  129. package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
  130. package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
  131. package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
  132. package/dist/core/request-routing/interceptors/url-redirects.mjs +6 -1
  133. package/dist/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
  134. package/dist/core/request-routing/interceptors/well-known.mjs +1 -1
  135. package/dist/core/request-routing/interceptors/well-known.mjs.map +1 -1
  136. package/dist/core/request-routing/registered-routes.d.mts +12 -0
  137. package/dist/core/request-routing/registered-routes.mjs +12 -0
  138. package/dist/core/request-routing/registered-routes.mjs.map +1 -1
  139. package/dist/core/request-routing/route-types.d.mts +43 -18
  140. package/dist/core/shop-pay/shop-pay.d.mts +71 -35
  141. package/dist/core/shop-pay/shop-pay.mjs +36 -10
  142. package/dist/core/shop-pay/shop-pay.mjs.map +1 -1
  143. package/dist/core/shopify-scripts/index.d.mts +15 -5
  144. package/dist/core/shopify-scripts/index.mjs +15 -5
  145. package/dist/core/shopify-scripts/index.mjs.map +1 -1
  146. package/dist/core/shopify-scripts/initialize.d.mts +12 -4
  147. package/dist/core/shopify-scripts/initialize.mjs +12 -4
  148. package/dist/core/shopify-scripts/initialize.mjs.map +1 -1
  149. package/dist/core/shopify-scripts/types.d.mts +58 -25
  150. package/dist/core/standard-routes/build.d.mts +11 -12
  151. package/dist/core/standard-routes/build.mjs +11 -12
  152. package/dist/core/standard-routes/build.mjs.map +1 -1
  153. package/dist/core/standard-routes/types.d.mts +30 -24
  154. package/dist/core/url.mjs +2 -1
  155. package/dist/core/url.mjs.map +1 -1
  156. package/dist/customer-account/client.d.mts +36 -69
  157. package/dist/customer-account/client.mjs +7 -8
  158. package/dist/customer-account/client.mjs.map +1 -1
  159. package/dist/customer-account/errors.d.mts +45 -36
  160. package/dist/customer-account/errors.mjs +21 -36
  161. package/dist/customer-account/errors.mjs.map +1 -1
  162. package/dist/customer-account/graphql.d.mts +11 -23
  163. package/dist/customer-account/graphql.mjs +3 -9
  164. package/dist/customer-account/graphql.mjs.map +1 -1
  165. package/dist/customer-account/session.d.mts +130 -100
  166. package/dist/customer-account/session.mjs +32 -10
  167. package/dist/customer-account/session.mjs.map +1 -1
  168. package/dist/customer-account/type-resolver.d.mts +7 -8
  169. package/dist/development/client/client.d.mts +18 -15
  170. package/dist/development/client/client.mjs +8 -3
  171. package/dist/development/client/client.mjs.map +1 -1
  172. package/dist/development/client/errors.d.mts +35 -13
  173. package/dist/development/client/errors.mjs +23 -13
  174. package/dist/development/client/errors.mjs.map +1 -1
  175. package/dist/development/client/types.d.mts +140 -90
  176. package/dist/development/core/analytics/cart-tracker.d.mts +19 -10
  177. package/dist/development/core/analytics/cart-tracker.mjs +18 -10
  178. package/dist/development/core/analytics/cart-tracker.mjs.map +1 -1
  179. package/dist/development/core/analytics/events.d.mts +17 -11
  180. package/dist/development/core/analytics/events.mjs +13 -4
  181. package/dist/development/core/analytics/events.mjs.map +1 -1
  182. package/dist/development/core/analytics/types.d.mts +207 -118
  183. package/dist/development/core/analytics/utils/flatten-connection.d.mts +11 -9
  184. package/dist/development/core/analytics/utils/flatten-connection.mjs +7 -6
  185. package/dist/development/core/analytics/utils/flatten-connection.mjs.map +1 -1
  186. package/dist/development/core/cache/fetch-with-cache.d.mts +24 -4
  187. package/dist/development/core/cache/fetch-with-cache.mjs +17 -2
  188. package/dist/development/core/cache/fetch-with-cache.mjs.map +1 -1
  189. package/dist/development/core/cache/key.d.mts +2 -0
  190. package/dist/development/core/cache/key.mjs.map +1 -1
  191. package/dist/development/core/cache/run-with-cache.d.mts +56 -8
  192. package/dist/development/core/cache/run-with-cache.mjs +17 -6
  193. package/dist/development/core/cache/run-with-cache.mjs.map +1 -1
  194. package/dist/development/core/cache/store.d.mts +8 -6
  195. package/dist/development/core/cache/store.mjs.map +1 -1
  196. package/dist/development/core/cache/strategies.d.mts +27 -2
  197. package/dist/development/core/cache/strategies.mjs +6 -0
  198. package/dist/development/core/cache/strategies.mjs.map +1 -1
  199. package/dist/development/core/cart/actions.d.mts +37 -30
  200. package/dist/development/core/cart/actions.mjs +21 -9
  201. package/dist/development/core/cart/actions.mjs.map +1 -1
  202. package/dist/development/core/cart/attach-quantity-input.d.mts +8 -7
  203. package/dist/development/core/cart/attach-quantity-input.mjs +8 -7
  204. package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -1
  205. package/dist/development/core/cart/buyer-identity-sync.d.mts +6 -5
  206. package/dist/development/core/cart/buyer-identity-sync.mjs.map +1 -1
  207. package/dist/development/core/cart/cart.d.mts +59 -46
  208. package/dist/development/core/cart/cart.mjs +46 -29
  209. package/dist/development/core/cart/cart.mjs.map +1 -1
  210. package/dist/development/core/cart/cookie.d.mts +8 -4
  211. package/dist/development/core/cart/cookie.mjs +8 -4
  212. package/dist/development/core/cart/cookie.mjs.map +1 -1
  213. package/dist/development/core/cart/form.d.mts +34 -20
  214. package/dist/development/core/cart/form.mjs +6 -4
  215. package/dist/development/core/cart/form.mjs.map +1 -1
  216. package/dist/development/core/cart/get-cart.d.mts +27 -15
  217. package/dist/development/core/cart/get-cart.mjs +13 -8
  218. package/dist/development/core/cart/get-cart.mjs.map +1 -1
  219. package/dist/development/core/cart/quantity.d.mts +16 -5
  220. package/dist/development/core/cart/quantity.mjs +16 -5
  221. package/dist/development/core/cart/quantity.mjs.map +1 -1
  222. package/dist/development/core/cart/queries.d.mts +25 -19
  223. package/dist/development/core/cart/queries.mjs +15 -1
  224. package/dist/development/core/cart/queries.mjs.map +1 -1
  225. package/dist/development/core/cart/server-handlers.d.mts +82 -45
  226. package/dist/development/core/cart/server-handlers.mjs +4 -0
  227. package/dist/development/core/cart/server-handlers.mjs.map +1 -1
  228. package/dist/development/core/cart/state.d.mts +110 -64
  229. package/dist/development/core/cart/state.mjs +24 -6
  230. package/dist/development/core/cart/state.mjs.map +1 -1
  231. package/dist/development/core/collection/collection.d.mts +52 -53
  232. package/dist/development/core/collection/collection.mjs +5 -3
  233. package/dist/development/core/collection/collection.mjs.map +1 -1
  234. package/dist/development/core/collection/reconciler.d.mts +23 -16
  235. package/dist/development/core/collection/reconciler.mjs +8 -4
  236. package/dist/development/core/collection/reconciler.mjs.map +1 -1
  237. package/dist/development/core/collection/state.d.mts +31 -26
  238. package/dist/development/core/collection/state.mjs +4 -3
  239. package/dist/development/core/collection/state.mjs.map +1 -1
  240. package/dist/development/core/collection/url.d.mts +70 -29
  241. package/dist/development/core/collection/url.mjs +63 -25
  242. package/dist/development/core/collection/url.mjs.map +1 -1
  243. package/dist/development/core/constants.mjs.map +1 -1
  244. package/dist/development/core/logging/logging.d.mts +14 -8
  245. package/dist/development/core/logging/logging.mjs +15 -10
  246. package/dist/development/core/logging/logging.mjs.map +1 -1
  247. package/dist/development/core/logging/types.d.mts +28 -11
  248. package/dist/development/core/logging/types.mjs.map +1 -1
  249. package/dist/development/core/money/format.d.mts +6 -4
  250. package/dist/development/core/money/format.mjs +19 -9
  251. package/dist/development/core/money/format.mjs.map +1 -1
  252. package/dist/development/core/money/types.d.mts +41 -25
  253. package/dist/development/core/predictive-search/form.d.mts +37 -7
  254. package/dist/development/core/predictive-search/form.mjs +21 -3
  255. package/dist/development/core/predictive-search/form.mjs.map +1 -1
  256. package/dist/development/core/predictive-search/queries.d.mts +18 -30
  257. package/dist/development/core/predictive-search/queries.mjs +7 -3
  258. package/dist/development/core/predictive-search/queries.mjs.map +1 -1
  259. package/dist/development/core/predictive-search/search.d.mts +25 -27
  260. package/dist/development/core/predictive-search/search.mjs +20 -19
  261. package/dist/development/core/predictive-search/search.mjs.map +1 -1
  262. package/dist/development/core/predictive-search/server-handlers.d.mts +25 -26
  263. package/dist/development/core/predictive-search/server-handlers.mjs +4 -0
  264. package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -1
  265. package/dist/development/core/predictive-search/store.d.mts +65 -24
  266. package/dist/development/core/predictive-search/store.mjs +12 -1
  267. package/dist/development/core/predictive-search/store.mjs.map +1 -1
  268. package/dist/development/core/predictive-search/url.d.mts +36 -37
  269. package/dist/development/core/predictive-search/url.mjs +14 -6
  270. package/dist/development/core/predictive-search/url.mjs.map +1 -1
  271. package/dist/development/core/product/form.d.mts +63 -16
  272. package/dist/development/core/product/form.mjs +8 -2
  273. package/dist/development/core/product/form.mjs.map +1 -1
  274. package/dist/development/core/product/options.d.mts +8 -10
  275. package/dist/development/core/product/options.mjs +8 -10
  276. package/dist/development/core/product/options.mjs.map +1 -1
  277. package/dist/development/core/product/product-form.d.mts +77 -29
  278. package/dist/development/core/product/product-form.mjs +32 -10
  279. package/dist/development/core/product/product-form.mjs.map +1 -1
  280. package/dist/development/core/product/state.d.mts +55 -32
  281. package/dist/development/core/product/url.d.mts +19 -13
  282. package/dist/development/core/product/url.mjs +10 -10
  283. package/dist/development/core/product/url.mjs.map +1 -1
  284. package/dist/development/core/request-context.d.mts +77 -19
  285. package/dist/development/core/request-context.mjs +5 -1
  286. package/dist/development/core/request-context.mjs.map +1 -1
  287. package/dist/development/core/request-routing/handle-shopify-redirects.d.mts +12 -4
  288. package/dist/development/core/request-routing/handle-shopify-redirects.mjs +9 -2
  289. package/dist/development/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
  290. package/dist/development/core/request-routing/handle-shopify-routes.development.d.mts +9 -1
  291. package/dist/development/core/request-routing/handle-shopify-routes.development.mjs +7 -0
  292. package/dist/development/core/request-routing/handle-shopify-routes.development.mjs.map +1 -1
  293. package/dist/development/core/request-routing/handle-shopify-routes.mjs +7 -5
  294. package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  295. package/dist/development/core/request-routing/interceptors/proxy.mjs +79 -43
  296. package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
  297. package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
  298. package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
  299. package/dist/development/core/request-routing/interceptors/url-redirects.mjs +6 -1
  300. package/dist/development/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
  301. package/dist/development/core/request-routing/interceptors/well-known.mjs +1 -1
  302. package/dist/development/core/request-routing/interceptors/well-known.mjs.map +1 -1
  303. package/dist/development/core/request-routing/registered-routes.d.mts +12 -0
  304. package/dist/development/core/request-routing/registered-routes.mjs +12 -0
  305. package/dist/development/core/request-routing/registered-routes.mjs.map +1 -1
  306. package/dist/development/core/request-routing/route-types.d.mts +43 -18
  307. package/dist/development/core/shop-pay/shop-pay.d.mts +71 -35
  308. package/dist/development/core/shop-pay/shop-pay.mjs +36 -10
  309. package/dist/development/core/shop-pay/shop-pay.mjs.map +1 -1
  310. package/dist/development/core/shopify-scripts/index.d.mts +15 -5
  311. package/dist/development/core/shopify-scripts/index.mjs +15 -5
  312. package/dist/development/core/shopify-scripts/index.mjs.map +1 -1
  313. package/dist/development/core/shopify-scripts/initialize.d.mts +12 -4
  314. package/dist/development/core/shopify-scripts/initialize.mjs +12 -4
  315. package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -1
  316. package/dist/development/core/shopify-scripts/types.d.mts +58 -25
  317. package/dist/development/core/standard-routes/build.d.mts +11 -12
  318. package/dist/development/core/standard-routes/build.mjs +11 -12
  319. package/dist/development/core/standard-routes/build.mjs.map +1 -1
  320. package/dist/development/core/standard-routes/types.d.mts +30 -24
  321. package/dist/development/core/url.mjs +2 -1
  322. package/dist/development/core/url.mjs.map +1 -1
  323. package/dist/development/customer-account/session.d.mts +45 -54
  324. package/dist/development/customer-account/session.mjs +20 -4
  325. package/dist/development/customer-account/session.mjs.map +1 -1
  326. package/dist/development/globals.d.mts +20 -11
  327. package/dist/development/graphql/generated/storefront-api-types.d.mts +127 -1
  328. package/dist/development/graphql/graphql.d.mts +17 -16
  329. package/dist/development/graphql/graphql.mjs +7 -8
  330. package/dist/development/graphql/graphql.mjs.map +1 -1
  331. package/dist/development/graphql/type-resolver.d.mts +15 -8
  332. package/dist/development/react/cart.d.mts +78 -32
  333. package/dist/development/react/cart.mjs +56 -29
  334. package/dist/development/react/cart.mjs.map +1 -1
  335. package/dist/development/react/collection.d.mts +40 -22
  336. package/dist/development/react/collection.mjs +27 -7
  337. package/dist/development/react/collection.mjs.map +1 -1
  338. package/dist/development/react/predictive-search.d.mts +65 -50
  339. package/dist/development/react/predictive-search.mjs +23 -21
  340. package/dist/development/react/predictive-search.mjs.map +1 -1
  341. package/dist/development/react/product.d.mts +45 -16
  342. package/dist/development/react/product.mjs +13 -11
  343. package/dist/development/react/product.mjs.map +1 -1
  344. package/dist/development/react/shop-pay.d.mts +15 -0
  345. package/dist/development/react/shop-pay.mjs +9 -0
  346. package/dist/development/react/shop-pay.mjs.map +1 -1
  347. package/dist/development/react/shopify-scripts.d.mts +23 -2
  348. package/dist/development/react/shopify-scripts.mjs +9 -0
  349. package/dist/development/react/shopify-scripts.mjs.map +1 -1
  350. package/dist/development/vue/cart.d.mts +28 -23
  351. package/dist/development/vue/cart.mjs +42 -25
  352. package/dist/development/vue/cart.mjs.map +1 -1
  353. package/dist/development/vue/collection.d.mts +20 -12
  354. package/dist/development/vue/collection.mjs +15 -7
  355. package/dist/development/vue/collection.mjs.map +1 -1
  356. package/dist/development/vue/index.d.mts +2 -2
  357. package/dist/development/vue/index.mjs +2 -2
  358. package/dist/development/vue/predictive-search.d.mts +31 -36
  359. package/dist/development/vue/predictive-search.mjs +16 -22
  360. package/dist/development/vue/predictive-search.mjs.map +1 -1
  361. package/dist/development/vue/product.d.mts +16 -15
  362. package/dist/development/vue/product.mjs +11 -11
  363. package/dist/development/vue/product.mjs.map +1 -1
  364. package/dist/development/vue/shop-pay.d.mts +14 -17
  365. package/dist/development/vue/shop-pay.mjs +8 -0
  366. package/dist/development/vue/shop-pay.mjs.map +1 -1
  367. package/dist/development/vue/shopify-scripts.d.mts +11 -0
  368. package/dist/development/vue/shopify-scripts.mjs +6 -0
  369. package/dist/development/vue/shopify-scripts.mjs.map +1 -1
  370. package/dist/globals.d.mts +20 -11
  371. package/dist/graphql/generated/storefront-api-types.d.mts +127 -1
  372. package/dist/graphql/graphql.d.mts +17 -16
  373. package/dist/graphql/graphql.mjs +7 -8
  374. package/dist/graphql/graphql.mjs.map +1 -1
  375. package/dist/graphql/type-resolver.d.mts +15 -8
  376. package/dist/react/cart.d.mts +78 -32
  377. package/dist/react/cart.mjs +56 -29
  378. package/dist/react/cart.mjs.map +1 -1
  379. package/dist/react/collection.d.mts +40 -22
  380. package/dist/react/collection.mjs +27 -7
  381. package/dist/react/collection.mjs.map +1 -1
  382. package/dist/react/predictive-search.d.mts +65 -50
  383. package/dist/react/predictive-search.mjs +23 -21
  384. package/dist/react/predictive-search.mjs.map +1 -1
  385. package/dist/react/product.d.mts +45 -16
  386. package/dist/react/product.mjs +13 -11
  387. package/dist/react/product.mjs.map +1 -1
  388. package/dist/react/shop-pay.d.mts +15 -0
  389. package/dist/react/shop-pay.mjs +9 -0
  390. package/dist/react/shop-pay.mjs.map +1 -1
  391. package/dist/react/shopify-scripts.d.mts +23 -2
  392. package/dist/react/shopify-scripts.mjs +9 -0
  393. package/dist/react/shopify-scripts.mjs.map +1 -1
  394. package/dist/ts-plugin/index.cjs +6 -0
  395. package/dist/ts-plugin/index.cjs.map +1 -1
  396. package/dist/ts-plugin/index.d.cts +6 -0
  397. package/dist/vite/local-https.d.mts +51 -17
  398. package/dist/vite/local-https.mjs +26 -5
  399. package/dist/vite/local-https.mjs.map +1 -1
  400. package/dist/vue/cart.d.mts +28 -23
  401. package/dist/vue/cart.mjs +42 -25
  402. package/dist/vue/cart.mjs.map +1 -1
  403. package/dist/vue/collection.d.mts +20 -12
  404. package/dist/vue/collection.mjs +15 -7
  405. package/dist/vue/collection.mjs.map +1 -1
  406. package/dist/vue/index.d.mts +2 -2
  407. package/dist/vue/index.mjs +2 -2
  408. package/dist/vue/predictive-search.d.mts +31 -36
  409. package/dist/vue/predictive-search.mjs +16 -22
  410. package/dist/vue/predictive-search.mjs.map +1 -1
  411. package/dist/vue/product.d.mts +16 -15
  412. package/dist/vue/product.mjs +11 -11
  413. package/dist/vue/product.mjs.map +1 -1
  414. package/dist/vue/shop-pay.d.mts +14 -17
  415. package/dist/vue/shop-pay.mjs +8 -0
  416. package/dist/vue/shop-pay.mjs.map +1 -1
  417. package/dist/vue/shopify-scripts.d.mts +11 -0
  418. package/dist/vue/shopify-scripts.mjs +6 -0
  419. package/dist/vue/shopify-scripts.mjs.map +1 -1
  420. package/package.json +3 -1
  421. package/skills/hydrogen-analytics/SKILL.md +1 -1
  422. package/skills/hydrogen-cart-ui/SKILL.md +28 -2
  423. package/skills/hydrogen-collection-browser/SKILL.md +9 -1
  424. package/skills/hydrogen-collection-browser/references/nextjs.md +1 -1
  425. package/skills/hydrogen-collection-browser/references/nuxt.md +1 -1
  426. package/skills/hydrogen-collection-browser/references/react.md +151 -32
  427. package/skills/hydrogen-customer-account/SKILL.md +2 -0
  428. package/skills/hydrogen-request-handlers/SKILL.md +21 -11
  429. package/skills/hydrogen-setup/references/analytics.md +14 -8
  430. package/skills/hydrogen-setup/steps/1-inspect-app.md +2 -2
  431. package/skills/hydrogen-setup/steps/6-product-detail-page.md +21 -11
  432. package/skills/hydrogen-storefront-client/SKILL.md +1 -1
  433. package/skills/hydrogen-storefront-client/references/caching.md +2 -0
  434. package/skills/hydrogen-variant-form/SKILL.md +25 -7
  435. package/skills/hydrogen-variant-form/references/nextjs.md +39 -4
  436. package/skills/hydrogen-variant-form/references/nuxt.md +83 -42
  437. package/skills/hydrogen-variant-form/references/react.md +45 -9
@@ -5,106 +5,121 @@ import { AnyStorefrontQueryString, SourceOf, StorefrontQueryString } from "../gr
5
5
  import { ResultOf, TadaDocumentNode, VariablesOf } from "gql.tada";
6
6
 
7
7
  //#region src/client/types.d.ts
8
+ /** A document that the client's GraphQL method accepts, from `gql` or from `gql`.tada. */
8
9
  type DocLike = TadaDocumentNode<any, any> | AnyStorefrontQueryString;
10
+ /** The typed document that Hydrogen infers from a plain query string. */
9
11
  type InferredDoc<T extends string> = StorefrontQueryString<InferResult<T>, InferVariables<T>, T>;
12
+ /** The typed document for a GraphQL call. Hydrogen infers the types when you pass a plain query string. */
10
13
  type ResolveDoc<D> = D extends DocLike ? D : D extends string ? InferredDoc<D> : never;
14
+ /** The query text of a document as a string literal type. A plain string resolves to itself. */
11
15
  type SourceText<Doc> = [SourceOf<Doc>] extends [never] ? Doc extends string ? Doc : never : SourceOf<Doc>;
16
+ /** The operation kind of a document, such as `query`. Resolves to `unknown` when the query text isn't a string literal type. */
12
17
  type OperationKindOfDoc<Doc> = [SourceText<Doc>] extends [never] ? "unknown" : string extends SourceText<Doc> ? "unknown" : InferOperationKind<SourceText<Doc>>;
18
+ /** Offers the `cache` option only for documents that TypeScript recognizes as queries. */
13
19
  type GraphqlExtraOptionsForDoc<Doc, Extra extends Record<string, unknown>> = OperationKindOfDoc<Doc> extends "query" ? Extra : Omit<Extra, "cache">;
20
+ /** The result type of a `gql` document or a `gql`.tada document node. */
14
21
  type ResultOfDoc<Doc> = Doc extends StorefrontQueryString<infer Result, infer _Variables, string> ? Result : Doc extends TadaDocumentNode<any, any> ? ResultOf<Doc> : never;
15
22
  type VariablesOfDoc<Doc> = Doc extends StorefrontQueryString<infer _Result, infer Variables, string> ? Variables : Doc extends TadaDocumentNode<any, any> ? VariablesOf<Doc> : never;
16
23
  type StorefrontApiResultOf<Doc extends DocLike> = ResultOf<Doc>;
17
24
  type StorefrontApiVariablesOf<Doc extends DocLike> = VariablesOf<Doc>;
18
25
  /**
19
- * Minimal shape matching the GraphQL spec's error format, defined locally to
20
- * avoid a runtime dependency on the `graphql` package.
21
- *
22
- * Present on the `errors` arm of {@link StorefrontGraphqlResult}.
26
+ * A GraphQL error from the Storefront API. The result of a GraphQL call lists GraphQL errors
27
+ * in its `errors` field.
23
28
  */
24
29
  interface GraphQLFormattedError {
25
- /** Human-readable error description. */
30
+ /** The error message. */
26
31
  readonly message: string;
27
- /** Source locations in the query where the error originated. */
32
+ /** The lines and columns in the query where the error occurs. */
28
33
  readonly locations?: ReadonlyArray<{
29
34
  line: number;
30
35
  column: number;
31
36
  }>;
32
- /** Response path to the field that triggered the error. */
37
+ /** The path to the response field that failed. */
33
38
  readonly path?: ReadonlyArray<string | number>;
34
- /** Vendor extensions (e.g. Shopify error codes). */
39
+ /** Extra details that the Storefront API adds to the error. */
35
40
  readonly extensions?: Record<string, unknown>;
36
41
  }
37
42
  type CommonOptions = {
38
- /** Shopify store domain — e.g. `"my-store.myshopify.com"`. Normalized internally. */storeDomain: string;
39
43
  /**
40
- * Identifies the Hydrogen storefront making Storefront API requests so Shopify can attribute
41
- * cart analytics to the correct storefront.
44
+ * Your store's domain, such as `my-store.myshopify.com`. Hydrogen adds `https://` when the
45
+ * domain has no protocol, and removes trailing slashes. `createStorefrontClient` throws an error
46
+ * for an empty domain.
47
+ */
48
+ storeDomain: string;
49
+ /**
50
+ * Your Hydrogen storefront's ID. The client sends the ID in the `Shopify-Storefront-Id` header
51
+ * of every request.
42
52
  */
43
53
  storefrontId?: string;
44
54
  /**
45
- * Storefront API version used in the GraphQL endpoint path (`/api/<version>/graphql.json`).
46
- * Defaults to the version baked into the package. To send some queries to a different endpoint
47
- * version (e.g. `unstable`), create a second `createStorefrontClient` instance with that
48
- * `apiVersion` so version routing stays explicit. Hydrogen's bundled gql.tada schema still
49
- * controls type inference, so queries against fields outside that schema need their own typing.
55
+ * The Storefront API version that the client queries. Defaults to the version that Hydrogen
56
+ * bundles. To send some queries to another version, such as `unstable`, create a second client
57
+ * with that version. Type inference always uses Hydrogen's bundled schema. Write your own types
58
+ * for fields outside the bundled schema.
59
+ */
60
+ apiVersion?: string;
61
+ /**
62
+ * How long a request can run before the client throws a `StorefrontTimeoutError`, in
63
+ * milliseconds. Defaults to `30000`. Set `0` to turn off the timeout. Stale-while-revalidate
64
+ * refreshes use the same timeout, or 30,000 when the timeout is `0`. `createStorefrontClient`
65
+ * throws an error for a negative value.
50
66
  */
51
- apiVersion?: string; /** Per-request timeout in milliseconds. `0` disables the timeout. Defaults to 30,000 ms. */
52
- defaultTimeoutInMs?: number; /** Shared cache instance that enables per-query caching via the `cache` option on `graphql()`. */
53
- cache?: CacheInstance; /** Worker-style `waitUntil` to extend the request lifetime for background cache writes. */
67
+ defaultTimeoutInMs?: number;
68
+ /**
69
+ * The cache that stores query results. The client caches only queries that pass a `cache`
70
+ * strategy. The client never caches mutations or results with GraphQL errors.
71
+ */
72
+ cache?: CacheInstance; /** Keeps the runtime alive while the client writes to the cache in the background, such as a worker's `waitUntil` function. Without the function, each cached query waits for its cache write before it resolves. */
54
73
  waitUntil?: WaitUntil;
55
74
  };
75
+ /** A fetch function, or any function with a compatible signature that resolves to a `Response`. */
56
76
  type AnyFetch = typeof globalThis.fetch | ((...args: never[]) => Promise<Response>);
57
77
  /**
58
- * Public client — uses a public Storefront Access Token, or tokenless access
59
- * when `publicStorefrontToken` is omitted.
78
+ * Options for a public client, which runs in the browser or a mobile app. Pass a public access
79
+ * token, or omit the token for tokenless access.
60
80
  *
61
- * Best for: browser or mobile requests where the token is safe to expose.
62
- * Token-based access is required for some Storefront API fields, including
63
- * product tags, metaobjects, metafields, menus, and customers.
81
+ * Tokenless access can't read some Storefront API fields, including product tags, metaobjects,
82
+ * metafields, menus, and customers.
64
83
  */
65
84
  interface PublicClientOptions<Fetch extends AnyFetch | undefined = typeof globalThis.fetch> extends CommonOptions {
66
- /** Custom fetch implementation. Falls back to `globalThis.fetch`. */
85
+ /** The fetch function that the client calls. Defaults to `globalThis.fetch`. `createStorefrontClient` throws an error when neither exists. */
67
86
  fetch?: Fetch;
68
- /** Public Storefront Access Token. Omit for tokenless access (limited fields). */
87
+ /** Your public Storefront API access token. Omit the token for tokenless access. `createStorefrontClient` throws an error for an empty string. */
69
88
  publicStorefrontToken?: string | undefined;
70
89
  }
71
90
  /**
72
- * Private client — uses a private Storefront Access Token.
73
- *
74
- * `buyerIp` must be resolved on the request context before creating the client.
75
- *
76
- * Best for: SSR/server-side requests where you control the fetch
77
- * layer and can forward trusted buyer context.
78
- * Token-based access is required for some Storefront API fields, including
79
- * product tags, metaobjects, metafields, menus, and customers.
91
+ * Options for a private client, which runs during server-side rendering and forwards the
92
+ * customer's IP address to the Storefront API. Set `buyerIp` on the request context before you
93
+ * create the client.
80
94
  */
81
95
  interface PrivateClientOptions<Fetch extends AnyFetch | undefined = typeof globalThis.fetch> extends CommonOptions {
82
- /** Custom fetch implementation. Falls back to `globalThis.fetch`. */
96
+ /** The fetch function that the client calls. Defaults to `globalThis.fetch`. `createStorefrontClient` throws an error when neither exists. */
83
97
  fetch?: Fetch;
84
- /** Private Storefront Access Token. Must never be exposed to browsers. */
98
+ /** Your private Storefront API access token. Keep the token out of browser code. `createStorefrontClient` throws an error when the token is missing. */
85
99
  privateStorefrontToken: string;
86
100
  }
87
101
  /**
88
- * Private client without buyer context — uses a private Storefront Access Token
89
- * but does NOT forward per-buyer identity.
90
- *
91
- * Best for: background jobs, webhooks, or server contexts where no
92
- * shopper identity is available.
102
+ * Options for a private client without buyer context, for background jobs, webhooks, and other
103
+ * server code with no customer. The client sends a private access token and no customer IP address.
93
104
  */
94
105
  interface PrivateNoBuyerContextClientOptions<Fetch extends AnyFetch | undefined = typeof globalThis.fetch> extends CommonOptions {
95
- /** Custom fetch implementation. Falls back to `globalThis.fetch`. */
106
+ /** The fetch function that the client calls. Defaults to `globalThis.fetch`. `createStorefrontClient` throws an error when neither exists. */
96
107
  fetch?: Fetch;
97
- /** Private Storefront Access Token. Must never be exposed to browsers. */
108
+ /** Your private Storefront API access token. Keep the token out of browser code. `createStorefrontClient` throws an error when the token is missing. */
98
109
  privateStorefrontToken: string;
99
110
  }
100
- /** Union of all config shapes passed as the `config` field of {@link CreateStorefrontClientArgs}. */
111
+ /**
112
+ * The store settings that you pass as `config` when you create a client. The options depend on the client type.
113
+ *
114
+ * @publicDocs
115
+ */
101
116
  type StorefrontClientOptions = PublicClientOptions | PrivateClientOptions | PrivateNoBuyerContextClientOptions;
102
117
  /**
103
- * Discriminated union accepted by `createStorefrontClient`.
118
+ * The arguments for createStorefrontClient. Set `type` to choose the client, then pass a request
119
+ * context and the matching store settings.
104
120
  *
105
- * Discriminated on `type`: `"public"` requires a public (or no) token,
106
- * `"private"` requires a private token plus a request context with `buyerIp`,
107
- * and `"private_no_buyer_context"` requires a private token; `buyerIp` is not forwarded.
121
+ * A public client takes a public token or none. A private client takes a private token and a
122
+ * request context with `buyerIp`. A private client without buyer context takes a private token.
108
123
  */
109
124
  type CreateStorefrontClientArgs<RequestContext extends ShopifyRequestContext = ShopifyRequestContext, Type extends ClientType = ClientType, CacheConfig extends CacheInstance | undefined = CacheInstance | undefined> = {
110
125
  type: "public" & Type;
@@ -126,39 +141,41 @@ type CreateStorefrontClientArgs<RequestContext extends ShopifyRequestContext = S
126
141
  };
127
142
  };
128
143
  type AutoAddedVariableNames = "country" | "language";
144
+ /** The variables that you pass for a document, without `country` and `language`. The client fills both from the request context. */
129
145
  type UserVariables<Doc> = Omit<VariablesOfDoc<Doc>, AutoAddedVariableNames>;
146
+ /** Resolves to `true` when an object type has no required keys. */
130
147
  type HasNoRequiredKeys<T> = Record<string, never> extends T ? true : false;
131
- /** Per-call options common to every `storefront.graphql()` invocation. */
148
+ /**
149
+ * Options that every Storefront API GraphQL call accepts.
150
+ *
151
+ * @publicDocs
152
+ */
132
153
  type StorefrontGraphqlOptions = {
133
- /** Abort signal forwarded to the underlying fetch. Combined with the request-context signal via `AbortSignal.any`. */signal?: AbortSignal;
154
+ /**
155
+ * Cancels the request when the signal aborts. The request context's signal and the client timeout
156
+ * also cancel the request. The signal doesn't cancel a stale-while-revalidate refresh.
157
+ */
158
+ signal?: AbortSignal;
134
159
  };
160
+ /** The options for a GraphQL call. The call needs `variables` only when the document declares a required variable. */
135
161
  type MergedOptions<Doc extends DocLike, Extra extends Record<string, unknown>> = Extra & StorefrontGraphqlOptions & (HasNoRequiredKeys<UserVariables<Doc>> extends true ? {
136
162
  variables?: UserVariables<Doc>;
137
163
  } : {
138
164
  variables: UserVariables<Doc>;
139
165
  });
140
166
  /**
141
- * Rest-parameter tuple for `storefront.graphql()`.
142
- *
143
- * Resolves to `[options]` (required) when the document declares required
144
- * variables, or `[options?]` (optional) when all variables are optional or
145
- * auto-injected (e.g. `$country`, `$language`).
167
+ * The options argument of a GraphQL call. Pass the options argument when the document declares a
168
+ * required variable. You can leave the options argument out when every variable is optional, or
169
+ * when the only required variables are `$country` and `$language`, which the client fills.
146
170
  */
147
171
  type GqlRestParam<Doc extends DocLike, Extra extends Record<string, unknown> = {}> = HasNoRequiredKeys<MergedOptions<Doc, GraphqlExtraOptionsForDoc<Doc, Extra>>> extends true ? [options?: MergedOptions<Doc, GraphqlExtraOptionsForDoc<Doc, Extra>>] : [options: MergedOptions<Doc, GraphqlExtraOptionsForDoc<Doc, Extra>>];
148
172
  /**
149
- * Discriminated union returned by `storefront.graphql()`.
150
- *
151
- * Discriminated on `errors`:
173
+ * The result of a GraphQL call, with the data, any GraphQL errors, and the response headers. Check
174
+ * `errors` before you read `data`.
152
175
  *
153
- * - **No `errors`** — `data` is non-null and fully typed by the document.
154
- * - **`errors` present** — `data` may be `null` (request-level error, or a non-null
155
- * field error that propagated to the root) or
156
- * partial (field-level error absorbed by a nullable ancestor). Always
157
- * inspect `errors` first.
158
- *
159
- * `Partial` is deliberately not used: a non-null `data` never has absent
160
- * top-level keys — failures surface as `null` values already covered by the
161
- * schema types.
176
+ * Without errors, `data` holds the full result, typed from your document. With errors, `data` is
177
+ * `null` after a request error or after a failed non-null field at the root. Otherwise, `data`
178
+ * holds a partial result, and `null` replaces each failed field or its nearest nullable parent.
162
179
  */
163
180
  type StorefrontGraphqlResult<Doc extends DocLike> = {
164
181
  data: ResultOfDoc<Doc>;
@@ -169,41 +186,73 @@ type StorefrontGraphqlResult<Doc extends DocLike> = {
169
186
  errors: GraphQLFormattedError[];
170
187
  headers: Headers;
171
188
  };
172
- /** Base callable signature of `StorefrontClient.graphql` without per-client extra options. Accepts a `gql()` document or a plain string. */
173
- type StorefrontGraphql = <const Doc extends DocLike | string>(doc: Doc, ...options: GqlRestParam<ResolveDoc<Doc>>) => Promise<StorefrontGraphqlResult<ResolveDoc<Doc>>>;
174
- type ClientType = /** Public access token or tokenless access. Best for: browser requests. */"public" /** Private token with trusted buyer context. Best for: SSR. */ | "private" /** Private token without buyer context. Best for: background jobs, webhooks. */ | "private_no_buyer_context";
175
189
  /**
176
- * A type-safe Storefront API client returned by `createStorefrontClient`.
190
+ * Runs a Storefront API query or mutation. Pass a `gql` document or a plain query string.
191
+ *
192
+ * @publicDocs
193
+ */
194
+ type StorefrontGraphql =
195
+ /**
196
+ * @param doc - The `gql` document or query string to run.
197
+ * @param options - The variables and abort signal for the call. Pass the options when the document declares a required variable.
198
+ * @returns The response data, any GraphQL errors, and the response headers.
199
+ */
200
+ <const Doc extends DocLike | string>(doc: Doc, ...options: GqlRestParam<ResolveDoc<Doc>>) => Promise<StorefrontGraphqlResult<ResolveDoc<Doc>>>;
201
+ /**
202
+ * Chooses the kind of Storefront API client to create. Use `public` in the browser, with a public access token or none. Use `private` during server-side rendering, with a private token and the customer's IP address. Use `private_no_buyer_context` in background jobs and webhooks, with a private token and no customer.
177
203
  *
178
- * Call `client.graphql(doc, options?)` to execute a query or mutation against
179
- * the Storefront API. The result is typed by the document you pass in.
204
+ * @publicDocs
205
+ */
206
+ type ClientType = /** Runs in the browser with a public access token or none. */"public" /** Runs during server-side rendering with a private token and the customer's IP address. */ | "private" /** Runs in background jobs and webhooks with a private token and no customer. */ | "private_no_buyer_context";
207
+ /**
208
+ * A Storefront API client. Call `graphql()` with a document to run a query or mutation and get a
209
+ * typed result.
180
210
  */
181
211
  type StorefrontClient<Extra extends Record<string, unknown> = {}, Type extends ClientType = ClientType, RequestContext extends ShopifyRequestContext = ShopifyRequestContext> = {
182
- /** Which client variant was created (`"public"`, `"private"`, or `"private_no_buyer_context"`). */type: Type; /** Resolved i18n config (country + language) used for automatic variable injection. */
212
+ /** The client type that you passed to `createStorefrontClient`. */type: Type; /** The country and language that the client sends for `$country` and `$language` variables. */
183
213
  i18n: RequestContext["i18n"];
184
214
  /**
185
- * Execute a Storefront API GraphQL operation.
215
+ * Runs a Storefront API query or mutation and returns a typed result. Pass a `gql` document or a
216
+ * plain query string. When the operation declares `$country` or `$language`, the client sets the
217
+ * variable from the request context and overrides any value you pass.
186
218
  *
187
- * Accepts a `gql()` document or a plain query string. Variables
188
- * declared as `$country` / `$language` are auto-injected from the client's
189
- * i18n config.
219
+ * The method throws an error when a query passes a `cache` strategy and the client has no cache,
220
+ * or when the strategy uses `private` mode. A catch block for `StorefrontApiError` doesn't catch
221
+ * this configuration error. To cache customer-specific data, use `createRunWithCache` with a key
222
+ * unique to the customer.
190
223
  */
191
- graphql: <const Doc extends DocLike | string>(doc: Doc, ...options: GqlRestParam<ResolveDoc<Doc>, Extra>) => Promise<StorefrontGraphqlResult<ResolveDoc<Doc>>>; /** Full Storefront API GraphQL endpoint URL. */
192
- apiUrl: string; /** Normalized store URL (e.g. `"https://my-store.myshopify.com"`). */
193
- storeUrl: string; /** Storefront identifier for cart analytics attribution, when provided. */
194
- storefrontId?: string; /** The request context used for request-scoped headers, abort signals, and i18n. */
224
+ graphql: <const Doc extends DocLike | string>(doc: Doc, ...options: GqlRestParam<ResolveDoc<Doc>, Extra>) => Promise<StorefrontGraphqlResult<ResolveDoc<Doc>>>; /** The Storefront API GraphQL endpoint that the client calls. */
225
+ apiUrl: string; /** Your store's URL with its protocol, such as `https://my-store.myshopify.com`. */
226
+ storeUrl: string; /** The storefront ID from the client options. */
227
+ storefrontId?: string; /** The request context that you passed to `createStorefrontClient`. */
195
228
  requestContext: RequestContext;
196
229
  };
197
- /** A {@link StorefrontClient} narrowed to `type: "public"`. */
230
+ /**
231
+ * The type of a client that you create with `type: "public"`.
232
+ *
233
+ * @publicDocs
234
+ */
198
235
  type PublicStorefrontClient<Extra extends Record<string, unknown> = {}, RequestContext extends ShopifyRequestContext = ShopifyRequestContext> = StorefrontClient<Extra, "public", RequestContext>;
199
- /** A {@link StorefrontClient} narrowed to `type: "private"`. Requires a request context with `buyerIp`. */
236
+ /**
237
+ * The type of a client that you create with `type: "private"`. The client's request context includes `buyerIp`.
238
+ *
239
+ * @publicDocs
240
+ */
200
241
  type PrivateStorefrontClient<Extra extends Record<string, unknown> = {}, RequestContext extends ShopifyRequestContextWithBuyerIp = ShopifyRequestContextWithBuyerIp> = StorefrontClient<Extra, "private", RequestContext>;
201
- /** Convenience alias: a private client whose request context is always the concrete `ShopifyRequestContextWithBuyerIp`. */
242
+ /**
243
+ * The type of a private client with the default request context. Use the type to annotate a private client without naming your request context type.
244
+ *
245
+ * @publicDocs
246
+ */
202
247
  type RequestScopedPrivateStorefrontClient<Extra extends Record<string, unknown> = {}> = PrivateStorefrontClient<Extra, ShopifyRequestContextWithBuyerIp>;
203
- /** A {@link StorefrontClient} narrowed to `type: "private_no_buyer_context"`. Best for background jobs and webhooks. */
248
+ /**
249
+ * The type of a client that you create with `type: "private_no_buyer_context"`, for background jobs and webhooks.
250
+ *
251
+ * @publicDocs
252
+ */
204
253
  type PrivateNoBuyerContextStorefrontClient<Extra extends Record<string, unknown> = {}, RequestContext extends ShopifyRequestContext = ShopifyRequestContext> = StorefrontClient<Extra, "private_no_buyer_context", RequestContext>;
205
254
  /**
206
- * Namespace for extracting result and variable types from Storefront API documents.
255
+ * Type helpers that read the result and variables types of a Storefront API document.
207
256
  *
208
257
  * @example
209
258
  * ```ts
@@ -214,13 +263,14 @@ type PrivateNoBuyerContextStorefrontClient<Extra extends Record<string, unknown>
214
263
  * type Product = StorefrontApi.ResultOf<typeof PRODUCT_QUERY>;
215
264
  * type Vars = StorefrontApi.VariablesOf<typeof PRODUCT_QUERY>;
216
265
  * ```
266
+ * @publicDocs
217
267
  */
218
268
  declare namespace StorefrontApi {
219
- /** Extracts the typed result shape from a Storefront API document. */
269
+ /** The result type of a document. */
220
270
  type ResultOf<Doc extends DocLike> = StorefrontApiResultOf<Doc>;
221
- /** Extracts the typed variables shape from a Storefront API document. */
271
+ /** The variables type of a document. */
222
272
  type VariablesOf<Doc extends DocLike> = StorefrontApiVariablesOf<Doc>;
223
- /** A typed Storefront API document — a `gql()` document or a `TadaDocumentNode`. */
273
+ /** A typed Storefront API document from `gql`, or a gql.tada document node. */
224
274
  type DocumentNode = DocLike;
225
275
  }
226
276
  //#endregion
@@ -1,20 +1,29 @@
1
1
  import { CartStore } from "../cart/cart.mjs";
2
2
 
3
3
  //#region src/core/analytics/cart-tracker.d.ts
4
+ /** A cart store, or any object with the cart store's `getState()` and `subscribe()` methods. */
4
5
  type CartAnalyticsStore = Pick<CartStore, "getState" | "subscribe">;
5
6
  /**
6
- * Subscribes to a cart store and publishes analytics events when the cart changes.
7
+ * Publishes cart analytics events when the server confirms a change to the customer's cart. In React, call `useCartAnalytics`.
7
8
  *
8
- * Emits `cart_updated` whenever a settled cart's `updatedAt` changes, plus
9
- * `product_added_to_cart` / `product_removed_from_cart` for each line that was
10
- * added, removed, or changed quantity. Deduplicates on `updatedAt` with an
11
- * in-memory cursor (within this subscription) and `localStorage` (across full
12
- * page loads and tabs). A store filled from your own cart query must select
13
- * `updatedAt`, or deduplication can't work.
9
+ * The function publishes `cart_updated` when the cart's update time changes, plus
10
+ * `product_added_to_cart` or `product_removed_from_cart` for each added line, removed
11
+ * line, and quantity change. The function waits until no cart change or reload is in flight.
12
+ * Analytics payloads leave out cart lines without merchandise.
14
13
  *
15
- * @throws {Error} If `window.Shopify.analytics` is not set (including on the server).
16
- * Render `ShopifyScripts` (or the `getShopifyScriptTags()` output) first.
17
- * @returns An unsubscribe function that stops tracking.
14
+ * The function stores the last published cart update in local storage under `cartLastUpdatedAt`
15
+ * to avoid repeat events across page loads and tabs. A store that you fill from your own cart query
16
+ * must select `updatedAt`. Without the update time, the function publishes on every confirmed cart state.
17
+ * The function also sets `window.Shopify.currency.active` to the cart's currency.
18
+ *
19
+ * The function throws when the Shopify analytics bus isn't available, including during server rendering.
20
+ * Render `ShopifyScripts`, then start tracking in a client-only effect, such as `useEffect` or `onMounted`.
21
+ *
22
+ * @param store The cart store to track.
23
+ * @throws {Error} If `window.Shopify.analytics` isn't set, including on the server.
24
+ * Render Shopify's script tags first.
25
+ * @returns A function that stops tracking.
26
+ * @publicDocs
18
27
  */
19
28
  declare function trackCartAnalytics(store: CartAnalyticsStore): () => void;
20
29
  //#endregion
@@ -3,18 +3,26 @@ import { flattenConnection } from "./utils/flatten-connection.mjs";
3
3
 
4
4
  //#region src/core/analytics/cart-tracker.ts
5
5
  /**
6
- * Subscribes to a cart store and publishes analytics events when the cart changes.
6
+ * Publishes cart analytics events when the server confirms a change to the customer's cart. In React, call `useCartAnalytics`.
7
7
  *
8
- * Emits `cart_updated` whenever a settled cart's `updatedAt` changes, plus
9
- * `product_added_to_cart` / `product_removed_from_cart` for each line that was
10
- * added, removed, or changed quantity. Deduplicates on `updatedAt` with an
11
- * in-memory cursor (within this subscription) and `localStorage` (across full
12
- * page loads and tabs). A store filled from your own cart query must select
13
- * `updatedAt`, or deduplication can't work.
8
+ * The function publishes `cart_updated` when the cart's update time changes, plus
9
+ * `product_added_to_cart` or `product_removed_from_cart` for each added line, removed
10
+ * line, and quantity change. The function waits until no cart change or reload is in flight.
11
+ * Analytics payloads leave out cart lines without merchandise.
14
12
  *
15
- * @throws {Error} If `window.Shopify.analytics` is not set (including on the server).
16
- * Render `ShopifyScripts` (or the `getShopifyScriptTags()` output) first.
17
- * @returns An unsubscribe function that stops tracking.
13
+ * The function stores the last published cart update in local storage under `cartLastUpdatedAt`
14
+ * to avoid repeat events across page loads and tabs. A store that you fill from your own cart query
15
+ * must select `updatedAt`. Without the update time, the function publishes on every confirmed cart state.
16
+ * The function also sets `window.Shopify.currency.active` to the cart's currency.
17
+ *
18
+ * The function throws when the Shopify analytics bus isn't available, including during server rendering.
19
+ * Render `ShopifyScripts`, then start tracking in a client-only effect, such as `useEffect` or `onMounted`.
20
+ *
21
+ * @param store The cart store to track.
22
+ * @throws {Error} If `window.Shopify.analytics` isn't set, including on the server.
23
+ * Render Shopify's script tags first.
24
+ * @returns A function that stops tracking.
25
+ * @publicDocs
18
26
  */
19
27
  function trackCartAnalytics(store) {
20
28
  const analytics = getGlobalAnalytics();
@@ -1 +1 @@
1
- {"version":3,"file":"cart-tracker.mjs","names":[],"sources":["../../../src/core/analytics/cart-tracker.ts"],"sourcesContent":["import type { CartStore } from \"../cart/cart\";\nimport type { CartData, CartState } from \"../cart/state\";\nimport { AnalyticsEvent } from \"./events\";\nimport type {\n AnalyticsCart,\n AnalyticsCartLine,\n CartUpdatePayload,\n StorefrontAnalytics,\n} from \"./types\";\nimport { flattenConnection } from \"./utils/flatten-connection\";\n\ntype CartTrackerAnalytics = Pick<StorefrontAnalytics, \"publish\" | \"getConfig\">;\ntype CartAnalyticsStore = Pick<CartStore, \"getState\" | \"subscribe\">;\n\ntype CartStorage = {\n updatedAt: string;\n id: string;\n};\n\ntype CartTrackerState = {\n prevCart: AnalyticsCart | null;\n lastEventId: string | null;\n};\n\n/**\n * Subscribes to a cart store and publishes analytics events when the cart changes.\n *\n * Emits `cart_updated` whenever a settled cart's `updatedAt` changes, plus\n * `product_added_to_cart` / `product_removed_from_cart` for each line that was\n * added, removed, or changed quantity. Deduplicates on `updatedAt` with an\n * in-memory cursor (within this subscription) and `localStorage` (across full\n * page loads and tabs). A store filled from your own cart query must select\n * `updatedAt`, or deduplication can't work.\n *\n * @throws {Error} If `window.Shopify.analytics` is not set (including on the server).\n * Render `ShopifyScripts` (or the `getShopifyScriptTags()` output) first.\n * @returns An unsubscribe function that stops tracking.\n */\nexport function trackCartAnalytics(store: CartAnalyticsStore): () => void {\n const analytics = getGlobalAnalytics();\n const state: CartTrackerState = {\n prevCart: toAnalyticsCart(store.getState()),\n lastEventId: null,\n };\n\n if (state.prevCart) syncShopifyCurrency(state.prevCart);\n\n return store.subscribe((cartState) => {\n const cart = toAnalyticsCart(cartState);\n if (!cart) return;\n\n trackCartChange(analytics, state, cart);\n });\n}\n\nfunction trackCartChange(\n analytics: CartTrackerAnalytics,\n state: CartTrackerState,\n cart: AnalyticsCart,\n): void {\n if (!cart || !cart.updatedAt) return;\n\n syncShopifyCurrency(cart);\n\n if (cart.updatedAt === state.prevCart?.updatedAt) return;\n\n let cartLastUpdatedAt: CartStorage | null;\n try {\n cartLastUpdatedAt = JSON.parse(localStorage.getItem(\"cartLastUpdatedAt\") || \"\");\n } catch {\n cartLastUpdatedAt = null;\n }\n\n if (cart.id === cartLastUpdatedAt?.id && cart.updatedAt === cartLastUpdatedAt?.updatedAt) {\n state.prevCart = cart;\n return;\n }\n\n if (cart.updatedAt === state.lastEventId) return;\n state.lastEventId = cart.updatedAt;\n\n const config = analytics.getConfig();\n const payload: CartUpdatePayload = {\n eventTimestamp: Date.now(),\n cart,\n prevCart: state.prevCart,\n shop: config.shop,\n customData: config.customData,\n };\n\n analytics.publish(AnalyticsEvent.CART_UPDATED, payload);\n\n try {\n localStorage.setItem(\n \"cartLastUpdatedAt\",\n JSON.stringify({ id: cart.id, updatedAt: cart.updatedAt }),\n );\n } catch {\n // Safari private browsing or storage quota exceeded; analytics\n // deduplication will rely on in-memory prevCart only for this session.\n }\n\n const previousCartLines = flattenConnection<AnalyticsCartLine>(state.prevCart?.lines);\n const currentCartLines = flattenConnection<AnalyticsCartLine>(cart.lines);\n\n previousCartLines.forEach((prevLine) => {\n const matchedLineId = currentCartLines.filter((line) => prevLine.id === line.id);\n if (matchedLineId.length === 1) {\n const matchedLine = matchedLineId[0];\n if (prevLine.quantity < matchedLine.quantity) {\n analytics.publish(AnalyticsEvent.PRODUCT_ADD_TO_CART, {\n ...payload,\n prevLine,\n currentLine: matchedLine,\n });\n } else if (prevLine.quantity > matchedLine.quantity) {\n analytics.publish(AnalyticsEvent.PRODUCT_REMOVED_FROM_CART, {\n ...payload,\n prevLine,\n currentLine: matchedLine,\n });\n }\n } else {\n analytics.publish(AnalyticsEvent.PRODUCT_REMOVED_FROM_CART, {\n ...payload,\n prevLine,\n });\n }\n });\n\n currentCartLines.forEach((line) => {\n const matchedLineId = previousCartLines.filter((previousLine) => line.id === previousLine.id);\n if (!matchedLineId || matchedLineId.length === 0) {\n analytics.publish(AnalyticsEvent.PRODUCT_ADD_TO_CART, {\n ...payload,\n currentLine: line,\n });\n }\n });\n\n state.prevCart = cart;\n}\n\nfunction getGlobalAnalytics(): CartTrackerAnalytics {\n const analytics = typeof window !== \"undefined\" ? window.Shopify?.analytics : undefined;\n if (!analytics) {\n throw new Error(\n \"Shopify analytics bus is not available. Render ShopifyScripts before calling trackCartAnalytics().\",\n );\n }\n\n return analytics;\n}\n\nfunction toAnalyticsCart(state: CartState): AnalyticsCart | null {\n const cart = state.data;\n if (!cart.id || hasPendingCartWork(state)) return null;\n\n return {\n id: cart.id,\n updatedAt: getCartUpdatedAt(cart),\n cost: cart.cost,\n lines: {\n nodes: cart.lines.nodes.flatMap((line): AnalyticsCartLine[] => {\n const merchandise = line.merchandise;\n if (!merchandise) return [];\n\n const product = merchandise.product;\n const productId = typeof product.id === \"string\" ? product.id : \"\";\n const vendor = typeof product.vendor === \"string\" ? product.vendor : \"\";\n const productType =\n typeof product.productType === \"string\" ? product.productType : undefined;\n\n return [\n {\n id: line.id,\n quantity: line.quantity,\n merchandise: {\n id: merchandise.id,\n title: merchandise.title ?? product.title,\n price: line.cost.amountPerQuantity,\n sku: typeof merchandise.sku === \"string\" ? merchandise.sku : null,\n product: {\n id: productId,\n title: product.title,\n vendor,\n productType,\n handle: product.handle,\n },\n },\n },\n ];\n }),\n },\n };\n}\n\nfunction hasPendingCartWork({ pending, revalidating }: CartState): boolean {\n const hasPendingCost = pending.cost ?? (pending.lines.size > 0 || pending.discountCodes.size > 0);\n return revalidating === true || hasPendingCost || pending.note || pending.attributes;\n}\n\nfunction getCartUpdatedAt(cart: CartData): string {\n return cart.updatedAt ?? new Date().toISOString();\n}\n\nfunction syncShopifyCurrency(cart: AnalyticsCart): void {\n if (typeof window === \"undefined\") return;\n\n const currencyCode = getCartCurrencyCode(cart);\n if (!currencyCode) return;\n\n const normalizedCurrencyCode = currencyCode.toUpperCase();\n const shopifyWindow: { Shopify?: { currency?: { active: string } } } = window;\n const shopify = (shopifyWindow.Shopify ??= {});\n if (shopify.currency?.active === normalizedCurrencyCode) return;\n\n shopify.currency = { active: normalizedCurrencyCode };\n}\n\nfunction getCartCurrencyCode(cart: AnalyticsCart): string | undefined {\n return (\n nonEmptyCurrencyCode(cart.cost?.totalAmount?.currencyCode) ??\n nonEmptyCurrencyCode(cart.cost?.subtotalAmount?.currencyCode) ??\n flattenConnection<AnalyticsCartLine>(cart.lines).find(\n (line) => line.merchandise.price.currencyCode,\n )?.merchandise.price.currencyCode\n );\n}\n\nfunction nonEmptyCurrencyCode(currencyCode: string | undefined): string | undefined {\n return currencyCode || undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cart-tracker.mjs","names":[],"sources":["../../../src/core/analytics/cart-tracker.ts"],"sourcesContent":["import type { CartStore } from \"../cart/cart\";\nimport type { CartData, CartState } from \"../cart/state\";\nimport { AnalyticsEvent } from \"./events\";\nimport type {\n AnalyticsCart,\n AnalyticsCartLine,\n CartUpdatePayload,\n StorefrontAnalytics,\n} from \"./types\";\nimport { flattenConnection } from \"./utils/flatten-connection\";\n\ntype CartTrackerAnalytics = Pick<StorefrontAnalytics, \"publish\" | \"getConfig\">;\n/** A cart store, or any object with the cart store's `getState()` and `subscribe()` methods. */\ntype CartAnalyticsStore = Pick<CartStore, \"getState\" | \"subscribe\">;\n\ntype CartStorage = {\n updatedAt: string;\n id: string;\n};\n\ntype CartTrackerState = {\n prevCart: AnalyticsCart | null;\n lastEventId: string | null;\n};\n\n/**\n * Publishes cart analytics events when the server confirms a change to the customer's cart. In React, call `useCartAnalytics`.\n *\n * The function publishes `cart_updated` when the cart's update time changes, plus\n * `product_added_to_cart` or `product_removed_from_cart` for each added line, removed\n * line, and quantity change. The function waits until no cart change or reload is in flight.\n * Analytics payloads leave out cart lines without merchandise.\n *\n * The function stores the last published cart update in local storage under `cartLastUpdatedAt`\n * to avoid repeat events across page loads and tabs. A store that you fill from your own cart query\n * must select `updatedAt`. Without the update time, the function publishes on every confirmed cart state.\n * The function also sets `window.Shopify.currency.active` to the cart's currency.\n *\n * The function throws when the Shopify analytics bus isn't available, including during server rendering.\n * Render `ShopifyScripts`, then start tracking in a client-only effect, such as `useEffect` or `onMounted`.\n *\n * @param store The cart store to track.\n * @throws {Error} If `window.Shopify.analytics` isn't set, including on the server.\n * Render Shopify's script tags first.\n * @returns A function that stops tracking.\n * @publicDocs\n */\nexport function trackCartAnalytics(store: CartAnalyticsStore): () => void {\n const analytics = getGlobalAnalytics();\n const state: CartTrackerState = {\n prevCart: toAnalyticsCart(store.getState()),\n lastEventId: null,\n };\n\n if (state.prevCart) syncShopifyCurrency(state.prevCart);\n\n return store.subscribe((cartState) => {\n const cart = toAnalyticsCart(cartState);\n if (!cart) return;\n\n trackCartChange(analytics, state, cart);\n });\n}\n\nfunction trackCartChange(\n analytics: CartTrackerAnalytics,\n state: CartTrackerState,\n cart: AnalyticsCart,\n): void {\n if (!cart || !cart.updatedAt) return;\n\n syncShopifyCurrency(cart);\n\n if (cart.updatedAt === state.prevCart?.updatedAt) return;\n\n let cartLastUpdatedAt: CartStorage | null;\n try {\n cartLastUpdatedAt = JSON.parse(localStorage.getItem(\"cartLastUpdatedAt\") || \"\");\n } catch {\n cartLastUpdatedAt = null;\n }\n\n if (cart.id === cartLastUpdatedAt?.id && cart.updatedAt === cartLastUpdatedAt?.updatedAt) {\n state.prevCart = cart;\n return;\n }\n\n if (cart.updatedAt === state.lastEventId) return;\n state.lastEventId = cart.updatedAt;\n\n const config = analytics.getConfig();\n const payload: CartUpdatePayload = {\n eventTimestamp: Date.now(),\n cart,\n prevCart: state.prevCart,\n shop: config.shop,\n customData: config.customData,\n };\n\n analytics.publish(AnalyticsEvent.CART_UPDATED, payload);\n\n try {\n localStorage.setItem(\n \"cartLastUpdatedAt\",\n JSON.stringify({ id: cart.id, updatedAt: cart.updatedAt }),\n );\n } catch {\n // Safari private browsing or storage quota exceeded; analytics\n // deduplication will rely on in-memory prevCart only for this session.\n }\n\n const previousCartLines = flattenConnection<AnalyticsCartLine>(state.prevCart?.lines);\n const currentCartLines = flattenConnection<AnalyticsCartLine>(cart.lines);\n\n previousCartLines.forEach((prevLine) => {\n const matchedLineId = currentCartLines.filter((line) => prevLine.id === line.id);\n if (matchedLineId.length === 1) {\n const matchedLine = matchedLineId[0];\n if (prevLine.quantity < matchedLine.quantity) {\n analytics.publish(AnalyticsEvent.PRODUCT_ADD_TO_CART, {\n ...payload,\n prevLine,\n currentLine: matchedLine,\n });\n } else if (prevLine.quantity > matchedLine.quantity) {\n analytics.publish(AnalyticsEvent.PRODUCT_REMOVED_FROM_CART, {\n ...payload,\n prevLine,\n currentLine: matchedLine,\n });\n }\n } else {\n analytics.publish(AnalyticsEvent.PRODUCT_REMOVED_FROM_CART, {\n ...payload,\n prevLine,\n });\n }\n });\n\n currentCartLines.forEach((line) => {\n const matchedLineId = previousCartLines.filter((previousLine) => line.id === previousLine.id);\n if (!matchedLineId || matchedLineId.length === 0) {\n analytics.publish(AnalyticsEvent.PRODUCT_ADD_TO_CART, {\n ...payload,\n currentLine: line,\n });\n }\n });\n\n state.prevCart = cart;\n}\n\nfunction getGlobalAnalytics(): CartTrackerAnalytics {\n const analytics = typeof window !== \"undefined\" ? window.Shopify?.analytics : undefined;\n if (!analytics) {\n throw new Error(\n \"Shopify analytics bus is not available. Render ShopifyScripts before calling trackCartAnalytics().\",\n );\n }\n\n return analytics;\n}\n\nfunction toAnalyticsCart(state: CartState): AnalyticsCart | null {\n const cart = state.data;\n if (!cart.id || hasPendingCartWork(state)) return null;\n\n return {\n id: cart.id,\n updatedAt: getCartUpdatedAt(cart),\n cost: cart.cost,\n lines: {\n nodes: cart.lines.nodes.flatMap((line): AnalyticsCartLine[] => {\n const merchandise = line.merchandise;\n if (!merchandise) return [];\n\n const product = merchandise.product;\n const productId = typeof product.id === \"string\" ? product.id : \"\";\n const vendor = typeof product.vendor === \"string\" ? product.vendor : \"\";\n const productType =\n typeof product.productType === \"string\" ? product.productType : undefined;\n\n return [\n {\n id: line.id,\n quantity: line.quantity,\n merchandise: {\n id: merchandise.id,\n title: merchandise.title ?? product.title,\n price: line.cost.amountPerQuantity,\n sku: typeof merchandise.sku === \"string\" ? merchandise.sku : null,\n product: {\n id: productId,\n title: product.title,\n vendor,\n productType,\n handle: product.handle,\n },\n },\n },\n ];\n }),\n },\n };\n}\n\nfunction hasPendingCartWork({ pending, revalidating }: CartState): boolean {\n const hasPendingCost = pending.cost ?? (pending.lines.size > 0 || pending.discountCodes.size > 0);\n return revalidating === true || hasPendingCost || pending.note || pending.attributes;\n}\n\nfunction getCartUpdatedAt(cart: CartData): string {\n return cart.updatedAt ?? new Date().toISOString();\n}\n\nfunction syncShopifyCurrency(cart: AnalyticsCart): void {\n if (typeof window === \"undefined\") return;\n\n const currencyCode = getCartCurrencyCode(cart);\n if (!currencyCode) return;\n\n const normalizedCurrencyCode = currencyCode.toUpperCase();\n const shopifyWindow: { Shopify?: { currency?: { active: string } } } = window;\n const shopify = (shopifyWindow.Shopify ??= {});\n if (shopify.currency?.active === normalizedCurrencyCode) return;\n\n shopify.currency = { active: normalizedCurrencyCode };\n}\n\nfunction getCartCurrencyCode(cart: AnalyticsCart): string | undefined {\n return (\n nonEmptyCurrencyCode(cart.cost?.totalAmount?.currencyCode) ??\n nonEmptyCurrencyCode(cart.cost?.subtotalAmount?.currencyCode) ??\n flattenConnection<AnalyticsCartLine>(cart.lines).find(\n (line) => line.merchandise.price.currencyCode,\n )?.merchandise.price.currencyCode\n );\n}\n\nfunction nonEmptyCurrencyCode(currencyCode: string | undefined): string | undefined {\n return currencyCode || undefined;\n}\n"],"mappings":"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"}
@@ -1,21 +1,27 @@
1
1
  //#region src/core/analytics/events.d.ts
2
2
  /**
3
- * Canonical event names for the Hydrogen analytics bus.
3
+ * The names of the analytics events that Hydrogen supports.
4
4
  *
5
- * Use these as the `event` argument to `publish()` and a destination's `subscribe()`. Raw string
6
- * literals are type-checked and narrow the payload the same way; the constants add
7
- * autocomplete and a single place to rename.
5
+ * Pass a constant as the event name when you publish an event or subscribe a destination.
6
+ * TypeScript narrows the payload to match the event name.
7
+ *
8
+ * @publicDocs
8
9
  */
9
10
  declare const AnalyticsEvent: {
10
- PAGE_VIEWED: "page_viewed";
11
- PRODUCT_VIEWED: "product_viewed";
12
- COLLECTION_VIEWED: "collection_viewed";
13
- CART_VIEWED: "cart_viewed";
14
- SEARCH_VIEWED: "search_viewed";
15
- CART_UPDATED: "cart_updated";
16
- PRODUCT_ADD_TO_CART: "product_added_to_cart";
11
+ /** Publish when a customer views a page. Hydrogen fills in the current URL when the payload has none. */PAGE_VIEWED: "page_viewed"; /** Publish when a customer views a product. The payload lists the viewed products. */
12
+ PRODUCT_VIEWED: "product_viewed"; /** Publish when a customer views a collection. The payload identifies the collection by ID and handle. */
13
+ COLLECTION_VIEWED: "collection_viewed"; /** Publish when a customer views the cart. The payload carries the current cart. */
14
+ CART_VIEWED: "cart_viewed"; /** Publish when a customer views search results. The payload carries the search term. */
15
+ SEARCH_VIEWED: "search_viewed"; /** Cart tracking publishes this event when the cart changes. */
16
+ CART_UPDATED: "cart_updated"; /** Cart tracking publishes this event when the customer adds a line or raises a line's quantity. */
17
+ PRODUCT_ADD_TO_CART: "product_added_to_cart"; /** Cart tracking publishes this event when the customer removes a line or lowers a line's quantity. */
17
18
  PRODUCT_REMOVED_FROM_CART: "product_removed_from_cart";
18
19
  };
20
+ /**
21
+ * The name of a supported analytics event, such as `"page_viewed"`.
22
+ *
23
+ * @publicDocs
24
+ */
19
25
  type AnalyticsEventName = (typeof AnalyticsEvent)[keyof typeof AnalyticsEvent];
20
26
  //#endregion
21
27
  export { AnalyticsEvent, AnalyticsEventName };
@@ -1,19 +1,28 @@
1
1
  //#region src/core/analytics/events.ts
2
2
  /**
3
- * Canonical event names for the Hydrogen analytics bus.
3
+ * The names of the analytics events that Hydrogen supports.
4
4
  *
5
- * Use these as the `event` argument to `publish()` and a destination's `subscribe()`. Raw string
6
- * literals are type-checked and narrow the payload the same way; the constants add
7
- * autocomplete and a single place to rename.
5
+ * Pass a constant as the event name when you publish an event or subscribe a destination.
6
+ * TypeScript narrows the payload to match the event name.
7
+ *
8
+ * @publicDocs
8
9
  */
9
10
  const AnalyticsEvent = {
11
+ /** Publish when a customer views a page. Hydrogen fills in the current URL when the payload has none. */
10
12
  PAGE_VIEWED: "page_viewed",
13
+ /** Publish when a customer views a product. The payload lists the viewed products. */
11
14
  PRODUCT_VIEWED: "product_viewed",
15
+ /** Publish when a customer views a collection. The payload identifies the collection by ID and handle. */
12
16
  COLLECTION_VIEWED: "collection_viewed",
17
+ /** Publish when a customer views the cart. The payload carries the current cart. */
13
18
  CART_VIEWED: "cart_viewed",
19
+ /** Publish when a customer views search results. The payload carries the search term. */
14
20
  SEARCH_VIEWED: "search_viewed",
21
+ /** Cart tracking publishes this event when the cart changes. */
15
22
  CART_UPDATED: "cart_updated",
23
+ /** Cart tracking publishes this event when the customer adds a line or raises a line's quantity. */
16
24
  PRODUCT_ADD_TO_CART: "product_added_to_cart",
25
+ /** Cart tracking publishes this event when the customer removes a line or lowers a line's quantity. */
17
26
  PRODUCT_REMOVED_FROM_CART: "product_removed_from_cart"
18
27
  };
19
28
 
@@ -1 +1 @@
1
- {"version":3,"file":"events.mjs","names":[],"sources":["../../../src/core/analytics/events.ts"],"sourcesContent":["/**\n * Canonical event names for the Hydrogen analytics bus.\n *\n * Use these as the `event` argument to `publish()` and a destination's `subscribe()`. Raw string\n * literals are type-checked and narrow the payload the same way; the constants add\n * autocomplete and a single place to rename.\n */\nexport const AnalyticsEvent = {\n PAGE_VIEWED: \"page_viewed\" as const,\n PRODUCT_VIEWED: \"product_viewed\" as const,\n COLLECTION_VIEWED: \"collection_viewed\" as const,\n CART_VIEWED: \"cart_viewed\" as const,\n SEARCH_VIEWED: \"search_viewed\" as const,\n\n CART_UPDATED: \"cart_updated\" as const,\n PRODUCT_ADD_TO_CART: \"product_added_to_cart\" as const,\n PRODUCT_REMOVED_FROM_CART: \"product_removed_from_cart\" as const,\n};\n\nexport type AnalyticsEventName = (typeof AnalyticsEvent)[keyof typeof AnalyticsEvent];\n"],"mappings":";;;;;;;;AAOA,MAAa,iBAAiB;CAC5B,aAAa;CACb,gBAAgB;CAChB,mBAAmB;CACnB,aAAa;CACb,eAAe;CAEf,cAAc;CACd,qBAAqB;CACrB,2BAA2B;CAC5B"}
1
+ {"version":3,"file":"events.mjs","names":[],"sources":["../../../src/core/analytics/events.ts"],"sourcesContent":["/**\n * The names of the analytics events that Hydrogen supports.\n *\n * Pass a constant as the event name when you publish an event or subscribe a destination.\n * TypeScript narrows the payload to match the event name.\n *\n * @publicDocs\n */\nexport const AnalyticsEvent = {\n /** Publish when a customer views a page. Hydrogen fills in the current URL when the payload has none. */\n PAGE_VIEWED: \"page_viewed\" as const,\n /** Publish when a customer views a product. The payload lists the viewed products. */\n PRODUCT_VIEWED: \"product_viewed\" as const,\n /** Publish when a customer views a collection. The payload identifies the collection by ID and handle. */\n COLLECTION_VIEWED: \"collection_viewed\" as const,\n /** Publish when a customer views the cart. The payload carries the current cart. */\n CART_VIEWED: \"cart_viewed\" as const,\n /** Publish when a customer views search results. The payload carries the search term. */\n SEARCH_VIEWED: \"search_viewed\" as const,\n\n /** Cart tracking publishes this event when the cart changes. */\n CART_UPDATED: \"cart_updated\" as const,\n /** Cart tracking publishes this event when the customer adds a line or raises a line's quantity. */\n PRODUCT_ADD_TO_CART: \"product_added_to_cart\" as const,\n /** Cart tracking publishes this event when the customer removes a line or lowers a line's quantity. */\n PRODUCT_REMOVED_FROM_CART: \"product_removed_from_cart\" as const,\n};\n\n/** The analytics event constants, keyed by constant name. */\ntype AnalyticsEventMap = typeof AnalyticsEvent;\n\n/**\n * The names of the analytics events that Hydrogen supports. Pass a value as the event name when you publish an event or subscribe a destination. TypeScript narrows the payload to match the event name.\n *\n * @publicDocs\n */\nexport interface AnalyticsEventForDocs extends AnalyticsEventMap {}\n\n/**\n * The name of a supported analytics event, such as `\"page_viewed\"`.\n *\n * @publicDocs\n */\nexport type AnalyticsEventName = (typeof AnalyticsEvent)[keyof typeof AnalyticsEvent];\n"],"mappings":";;;;;;;;;AAQA,MAAa,iBAAiB;;CAE5B,aAAa;;CAEb,gBAAgB;;CAEhB,mBAAmB;;CAEnB,aAAa;;CAEb,eAAe;;CAGf,cAAc;;CAEd,qBAAqB;;CAErB,2BAA2B;CAC5B"}