@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
@@ -4,70 +4,111 @@ import { CollectionState } from "./state.mjs";
4
4
 
5
5
  //#region src/core/collection/url.d.ts
6
6
  /**
7
- * Parsed collection URL parameters — the subset of {@link CollectionState}
8
- * that is derived from the URL query string.
7
+ * The filters, sort key, and sort direction in a collection URL.
9
8
  */
10
9
  interface CollectionParams {
11
- /** Active product filters parsed from `filter.*` URL keys. */
10
+ /**
11
+ * Active product filters from the `filter.*` URL params.
12
+ *
13
+ * The filters use the Storefront Standard Events filter type. When your query variables use generated Storefront API types, cast the filters to the generated filter type where you pass them as variables.
14
+ */
12
15
  filters: ProductFilter[];
13
- /** Storefront API sort key parsed from the `sort_by` param. `undefined` when absent. */
16
+ /** Storefront API sort key from the `sort_by` param, or `undefined` when the param is missing or unrecognized. */
14
17
  sortKey: ProductCollectionSortKeys | undefined;
15
18
  /** `true` when the `sort_by` value ends with `-descending`. */
16
19
  reverse: boolean;
17
20
  }
18
21
  /**
19
- * Extracts collection state from URL search params.
22
+ * Reads collection filters and sort from URL search params. Pass the result to your Storefront API collection query.
20
23
  *
21
- * Handles Liquid-compatible filter keys (`filter.p.*`, `filter.v.*`)
22
- * and the `sort_by` param (with optional `-descending`/`-ascending` suffix).
24
+ * The function reads Liquid-compatible `filter.p.*` and `filter.v.*` keys and the `sort_by` param.
23
25
  *
24
- * @param searchParams - URL search params to parse
26
+ * A URL that sets availability to both in stock and out of stock produces no availability filter. The function recognizes the best-selling and manual sort values, plus created, price, and title with an ascending or descending suffix. For other sort values, the sort key is `undefined`. The `parseSortByValue` function also recognizes relevance, collection default, and ID.
27
+ *
28
+ * @param searchParams - URL search params to read.
29
+ * @returns The filters, sort key, and sort direction from the URL.
30
+ * @publicDocs
25
31
  */
26
32
  declare function parseCollectionParams(searchParams: URLSearchParams): CollectionParams;
27
33
  /**
28
- * Serializes collection state into URL search params using Liquid-compatible keys.
34
+ * Converts collection filters and sort to URL search params with Liquid-compatible keys. Use the result to build collection URLs.
35
+ *
36
+ * The result holds only the `filter.*` and `sort_by` keys. The function leaves out `sort_by` for the collection's default sort.
29
37
  *
30
- * Only emits store-owned keys (`filter.*`, `sort_by`).
31
- * Omits `sort_by` when `sortKey` is `undefined` (collection default).
38
+ * Category filters convert to `filter.p.category`, which `parseCollectionParams` doesn't read. A price filter with a minimum and a maximum produces two params. To build a filter checkbox, parse a filter value's `input` string and convert the parsed filter. The resulting entries give the checkbox name and value.
32
39
  *
33
- * @param state - The filter/sort slice of collection state
40
+ * @param state - The filters, sort key, and sort direction to convert.
41
+ * @returns URL search params that hold the filters and sort.
42
+ * @publicDocs
34
43
  */
35
44
  declare function serializeCollectionParams(state: Pick<CollectionState, "filters" | "sortKey" | "reverse">): URLSearchParams;
36
- /** Normalizes a router search string for comparison (strips a leading `?`). */
45
+ /**
46
+ * Removes a leading `?` from a search string. Normalize search strings before you compare them.
47
+ *
48
+ * @param search The search string from your router, with or without a leading `?`.
49
+ * @returns The search string without a leading `?`.
50
+ * @publicDocs
51
+ */
37
52
  declare function normalizeCollectionSearch(search: string): string;
38
53
  /**
39
- * Builds a URL query string with the given filter removed from the current params.
54
+ * Builds a query string from the current URL params, minus the given filter. Use the string in plain remove-filter links.
55
+ *
56
+ * The function keeps every other param and drops the `before` and `after` pagination cursors, which sends the customer to the first page of results.
40
57
  *
41
- * Clears pagination cursors so the changed filter state starts from its first page.
42
- * Returns `"?"` when removing the filter leaves no params. Useful for
43
- * rendering "remove filter" links without updating store state.
58
+ * @param currentParams The current URL search params.
59
+ * @param filter The filter to remove from the URL.
60
+ * @returns A query string that starts with `?`. The string is `?` alone when no params remain.
61
+ * @publicDocs
44
62
  */
45
63
  declare function getFilterRemovalUrl(currentParams: URLSearchParams, filter: ProductFilter): string;
46
64
  /**
47
- * Converts a Storefront API sort key and direction into the Liquid-compatible
48
- * `sort_by` param value (e.g. `"price-ascending"`, `"best-selling"`).
65
+ * Converts a Storefront API sort key and direction to a Liquid-compatible
66
+ * `sort_by` value, such as `price-ascending` or `best-selling`.
49
67
  *
50
- * Direction suffixes are only appended for sort keys that support them
51
- * (`PRICE`, `TITLE`, `CREATED`, `ID`). Others (like `BEST_SELLING`) return
52
- * the base value regardless of the `reverse` flag.
68
+ * Only the price, title, created, and ID sort keys get a direction suffix.
69
+ * Other sort keys, such as best selling, ignore the direction.
53
70
  *
54
- * @param sortKey - Storefront API `ProductCollectionSortKeys` enum value
55
- * @param reverse - `true` for descending, `false` for ascending
71
+ * @param sortKey - Storefront API product collection sort key.
72
+ * @param reverse - `true` for descending, `false` for ascending.
73
+ * @returns The value for the `sort_by` URL param.
74
+ * @publicDocs
56
75
  */
57
76
  declare function getSortByValue(sortKey: ProductCollectionSortKeys, reverse: boolean): string;
58
77
  /**
59
- * Parses a Liquid-compatible `sort_by` value back into the Storefront API
60
- * sort key and direction. Inverse of {@link getSortByValue}.
78
+ * Converts a Liquid-compatible `sort_by` value to a Storefront API
79
+ * sort key and direction. The `getSortByValue` function does the opposite conversion.
61
80
  *
62
- * @param value - A `sort_by` param value (e.g. `"price-ascending"`, `"best-selling"`)
81
+ * For unrecognized values, the sort key is `undefined`.
82
+ *
83
+ * @param value - A `sort_by` value, such as `price-ascending` or `best-selling`.
84
+ * @returns The sort key and `reverse`, which reads `true` for descending values.
85
+ * @publicDocs
63
86
  */
64
87
  declare function parseSortByValue(value: string): {
65
88
  sortKey: ProductCollectionSortKeys | undefined;
66
89
  reverse: boolean;
67
90
  };
68
- /** Compares two individual {@link ProductFilter} objects for semantic equality. */
91
+ /**
92
+ * Checks whether two product filters match.
93
+ *
94
+ * Price filters match when the minimum and maximum are equal, including ranges that set only one bound.
95
+ *
96
+ * @param a The first filter to compare.
97
+ * @param b The second filter to compare.
98
+ * @returns `true` when both filters are the same kind with the same values. `false` for different kinds or unrecognized filters.
99
+ * @publicDocs
100
+ */
69
101
  declare function filterEquals(a: ProductFilter, b: ProductFilter): boolean;
70
- /** Returns `true` when `input` (a Storefront API filter JSON string) matches an active filter. */
102
+ /**
103
+ * Checks whether a filter value's `input` JSON string from the Storefront API matches an active filter. Use the result to set the checked state of a filter control.
104
+ *
105
+ * The function compares filters the same way as `filterEquals`.
106
+ *
107
+ * @param activeFilters The active filters, such as `filters` from the collection state.
108
+ * @param input The filter value's `input` JSON string.
109
+ * @returns `true` when the parsed input matches an active filter. `false` when the input matches no active filter or isn't valid JSON.
110
+ * @publicDocs
111
+ */
71
112
  declare function isFilterInputActive(activeFilters: ProductFilter[], input: string): boolean;
72
113
  //#endregion
73
114
  export { CollectionParams, filterEquals, getFilterRemovalUrl, getSortByValue, isFilterInputActive, normalizeCollectionSearch, parseCollectionParams, parseSortByValue, serializeCollectionParams };
@@ -24,12 +24,15 @@ const STANDARD_EVENTS_COLLECTION_SORT_KEY = {
24
24
  manual: "MANUAL"
25
25
  };
26
26
  /**
27
- * Extracts collection state from URL search params.
27
+ * Reads collection filters and sort from URL search params. Pass the result to your Storefront API collection query.
28
28
  *
29
- * Handles Liquid-compatible filter keys (`filter.p.*`, `filter.v.*`)
30
- * and the `sort_by` param (with optional `-descending`/`-ascending` suffix).
29
+ * The function reads Liquid-compatible `filter.p.*` and `filter.v.*` keys and the `sort_by` param.
31
30
  *
32
- * @param searchParams - URL search params to parse
31
+ * A URL that sets availability to both in stock and out of stock produces no availability filter. The function recognizes the best-selling and manual sort values, plus created, price, and title with an ascending or descending suffix. For other sort values, the sort key is `undefined`. The `parseSortByValue` function also recognizes relevance, collection default, and ID.
32
+ *
33
+ * @param searchParams - URL search params to read.
34
+ * @returns The filters, sort key, and sort direction from the URL.
35
+ * @publicDocs
33
36
  */
34
37
  function parseCollectionParams(searchParams) {
35
38
  return {
@@ -39,12 +42,15 @@ function parseCollectionParams(searchParams) {
39
42
  };
40
43
  }
41
44
  /**
42
- * Serializes collection state into URL search params using Liquid-compatible keys.
45
+ * Converts collection filters and sort to URL search params with Liquid-compatible keys. Use the result to build collection URLs.
46
+ *
47
+ * The result holds only the `filter.*` and `sort_by` keys. The function leaves out `sort_by` for the collection's default sort.
43
48
  *
44
- * Only emits store-owned keys (`filter.*`, `sort_by`).
45
- * Omits `sort_by` when `sortKey` is `undefined` (collection default).
49
+ * Category filters convert to `filter.p.category`, which `parseCollectionParams` doesn't read. A price filter with a minimum and a maximum produces two params. To build a filter checkbox, parse a filter value's `input` string and convert the parsed filter. The resulting entries give the checkbox name and value.
46
50
  *
47
- * @param state - The filter/sort slice of collection state
51
+ * @param state - The filters, sort key, and sort direction to convert.
52
+ * @returns URL search params that hold the filters and sort.
53
+ * @publicDocs
48
54
  */
49
55
  function serializeCollectionParams(state) {
50
56
  const params = new URLSearchParams();
@@ -56,7 +62,13 @@ function serializeCollectionParams(state) {
56
62
  function isStoreOwnedParam(key) {
57
63
  return key === "sort_by" || key.startsWith("filter.");
58
64
  }
59
- /** Normalizes a router search string for comparison (strips a leading `?`). */
65
+ /**
66
+ * Removes a leading `?` from a search string. Normalize search strings before you compare them.
67
+ *
68
+ * @param search The search string from your router, with or without a leading `?`.
69
+ * @returns The search string without a leading `?`.
70
+ * @publicDocs
71
+ */
60
72
  function normalizeCollectionSearch(search) {
61
73
  return search.startsWith("?") ? search.slice(1) : search;
62
74
  }
@@ -85,11 +97,14 @@ function collectionParamsMatchState(searchParams, state) {
85
97
  return parsed.sortKey === state.sortKey && parsed.reverse === state.reverse && filtersEqual(parsed.filters, state.filters);
86
98
  }
87
99
  /**
88
- * Builds a URL query string with the given filter removed from the current params.
100
+ * Builds a query string from the current URL params, minus the given filter. Use the string in plain remove-filter links.
101
+ *
102
+ * The function keeps every other param and drops the `before` and `after` pagination cursors, which sends the customer to the first page of results.
89
103
  *
90
- * Clears pagination cursors so the changed filter state starts from its first page.
91
- * Returns `"?"` when removing the filter leaves no params. Useful for
92
- * rendering "remove filter" links without updating store state.
104
+ * @param currentParams The current URL search params.
105
+ * @param filter The filter to remove from the URL.
106
+ * @returns A query string that starts with `?`. The string is `?` alone when no params remain.
107
+ * @publicDocs
93
108
  */
94
109
  function getFilterRemovalUrl(currentParams, filter) {
95
110
  const result = new URLSearchParams(currentParams);
@@ -103,15 +118,16 @@ function isDirectionalSortKey(sortKey) {
103
118
  return sortKey === "PRICE" || sortKey === "TITLE" || sortKey === "CREATED" || sortKey === "ID";
104
119
  }
105
120
  /**
106
- * Converts a Storefront API sort key and direction into the Liquid-compatible
107
- * `sort_by` param value (e.g. `"price-ascending"`, `"best-selling"`).
121
+ * Converts a Storefront API sort key and direction to a Liquid-compatible
122
+ * `sort_by` value, such as `price-ascending` or `best-selling`.
108
123
  *
109
- * Direction suffixes are only appended for sort keys that support them
110
- * (`PRICE`, `TITLE`, `CREATED`, `ID`). Others (like `BEST_SELLING`) return
111
- * the base value regardless of the `reverse` flag.
124
+ * Only the price, title, created, and ID sort keys get a direction suffix.
125
+ * Other sort keys, such as best selling, ignore the direction.
112
126
  *
113
- * @param sortKey - Storefront API `ProductCollectionSortKeys` enum value
114
- * @param reverse - `true` for descending, `false` for ascending
127
+ * @param sortKey - Storefront API product collection sort key.
128
+ * @param reverse - `true` for descending, `false` for ascending.
129
+ * @returns The value for the `sort_by` URL param.
130
+ * @publicDocs
115
131
  */
116
132
  function getSortByValue(sortKey, reverse) {
117
133
  const base = SORT_KEY_TO_SORT_BY[sortKey] ?? sortKey.toLowerCase();
@@ -119,10 +135,14 @@ function getSortByValue(sortKey, reverse) {
119
135
  return reverse ? `${base}-descending` : `${base}-ascending`;
120
136
  }
121
137
  /**
122
- * Parses a Liquid-compatible `sort_by` value back into the Storefront API
123
- * sort key and direction. Inverse of {@link getSortByValue}.
138
+ * Converts a Liquid-compatible `sort_by` value to a Storefront API
139
+ * sort key and direction. The `getSortByValue` function does the opposite conversion.
124
140
  *
125
- * @param value - A `sort_by` param value (e.g. `"price-ascending"`, `"best-selling"`)
141
+ * For unrecognized values, the sort key is `undefined`.
142
+ *
143
+ * @param value - A `sort_by` value, such as `price-ascending` or `best-selling`.
144
+ * @returns The sort key and `reverse`, which reads `true` for descending values.
145
+ * @publicDocs
126
146
  */
127
147
  function parseSortByValue(value) {
128
148
  if (value.endsWith(SORT_BY_DESCENDING_SUFFIX)) return {
@@ -283,7 +303,16 @@ function removeFilterParams(params, filter) {
283
303
  function clearPaginationCursors(params) {
284
304
  for (const key of PAGINATION_CURSOR_PARAMS) params.delete(key);
285
305
  }
286
- /** Compares two individual {@link ProductFilter} objects for semantic equality. */
306
+ /**
307
+ * Checks whether two product filters match.
308
+ *
309
+ * Price filters match when the minimum and maximum are equal, including ranges that set only one bound.
310
+ *
311
+ * @param a The first filter to compare.
312
+ * @param b The second filter to compare.
313
+ * @returns `true` when both filters are the same kind with the same values. `false` for different kinds or unrecognized filters.
314
+ * @publicDocs
315
+ */
287
316
  function filterEquals(a, b) {
288
317
  const kindA = filterKind(a);
289
318
  if (kindA !== filterKind(b) || kindA == null) return false;
@@ -325,7 +354,16 @@ function filterEquals(a, b) {
325
354
  }
326
355
  }
327
356
  }
328
- /** Returns `true` when `input` (a Storefront API filter JSON string) matches an active filter. */
357
+ /**
358
+ * Checks whether a filter value's `input` JSON string from the Storefront API matches an active filter. Use the result to set the checked state of a filter control.
359
+ *
360
+ * The function compares filters the same way as `filterEquals`.
361
+ *
362
+ * @param activeFilters The active filters, such as `filters` from the collection state.
363
+ * @param input The filter value's `input` JSON string.
364
+ * @returns `true` when the parsed input matches an active filter. `false` when the input matches no active filter or isn't valid JSON.
365
+ * @publicDocs
366
+ */
329
367
  function isFilterInputActive(activeFilters, input) {
330
368
  let parsed;
331
369
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"url.mjs","names":[],"sources":["../../../src/core/collection/url.ts"],"sourcesContent":["import type { ProductFilter } from \"../../../vendor/standard-events\";\nimport type { ProductCollectionSortKeys } from \"../../graphql/generated/storefront-api-types\";\nimport type { CollectionState } from \"./state\";\n\n/**\n * Parsed collection URL parameters — the subset of {@link CollectionState}\n * that is derived from the URL query string.\n */\nexport interface CollectionParams {\n /** Active product filters parsed from `filter.*` URL keys. */\n filters: ProductFilter[];\n /** Storefront API sort key parsed from the `sort_by` param. `undefined` when absent. */\n sortKey: ProductCollectionSortKeys | undefined;\n /** `true` when the `sort_by` value ends with `-descending`. */\n reverse: boolean;\n}\n\nconst SORT_BY_DESCENDING_SUFFIX = \"-descending\";\nconst SORT_BY_ASCENDING_SUFFIX = \"-ascending\";\nconst PAGINATION_CURSOR_PARAMS: readonly string[] = [\"before\", \"after\"];\n\nconst SORT_KEY_TO_SORT_BY: Record<string, string> = {\n BEST_SELLING: \"best-selling\",\n CREATED: \"created\",\n PRICE: \"price\",\n TITLE: \"title\",\n MANUAL: \"manual\",\n ID: \"id\",\n RELEVANCE: \"relevance\",\n COLLECTION_DEFAULT: \"collection-default\",\n};\n\nconst SORT_BY_TO_SORT_KEY: Record<string, ProductCollectionSortKeys> = Object.fromEntries(\n Object.entries(SORT_KEY_TO_SORT_BY).map(([key, value]) => [\n value,\n key as ProductCollectionSortKeys,\n ]),\n);\n\nconst STANDARD_EVENTS_COLLECTION_SORT_KEY: Record<string, ProductCollectionSortKeys> = {\n \"best-selling\": \"BEST_SELLING\",\n \"created-ascending\": \"CREATED\",\n \"created-descending\": \"CREATED\",\n \"price-ascending\": \"PRICE\",\n \"price-descending\": \"PRICE\",\n \"title-ascending\": \"TITLE\",\n \"title-descending\": \"TITLE\",\n manual: \"MANUAL\",\n};\n\n/**\n * Extracts collection state from URL search params.\n *\n * Handles Liquid-compatible filter keys (`filter.p.*`, `filter.v.*`)\n * and the `sort_by` param (with optional `-descending`/`-ascending` suffix).\n *\n * @param searchParams - URL search params to parse\n */\nexport function parseCollectionParams(searchParams: URLSearchParams): CollectionParams {\n const filters = parseProductFilters(searchParams);\n const sortKey = parseCollectionSortKey(searchParams);\n const rawSortBy = searchParams.get(\"sort_by\");\n const reverse = rawSortBy?.endsWith(SORT_BY_DESCENDING_SUFFIX) ?? false;\n\n return { filters, sortKey, reverse };\n}\n\n/**\n * Serializes collection state into URL search params using Liquid-compatible keys.\n *\n * Only emits store-owned keys (`filter.*`, `sort_by`).\n * Omits `sort_by` when `sortKey` is `undefined` (collection default).\n *\n * @param state - The filter/sort slice of collection state\n */\nexport function serializeCollectionParams(\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): URLSearchParams {\n const params = new URLSearchParams();\n\n for (const filter of state.filters) {\n serializeFilter(params, filter);\n }\n\n if (state.sortKey) {\n params.set(\"sort_by\", getSortByValue(state.sortKey, state.reverse));\n }\n\n return params;\n}\n\n/** Returns `true` when the param key is owned by the collection store. */\nfunction isStoreOwnedParam(key: string): boolean {\n return key === \"sort_by\" || key.startsWith(\"filter.\");\n}\n\n/** Normalizes a router search string for comparison (strips a leading `?`). */\nexport function normalizeCollectionSearch(search: string): string {\n return search.startsWith(\"?\") ? search.slice(1) : search;\n}\n\n/**\n * Merges store-owned params from `state` into `existing`, preserving\n * unrelated keys (e.g. `grid`, `view`) already present in the URL. Collection\n * pagination cursors (`before`, `after`) are cleared so changed browse intent\n * starts from the first page.\n */\nexport function mergeCollectionParams(\n existing: URLSearchParams,\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): URLSearchParams {\n const merged = new URLSearchParams(existing);\n\n for (const key of Array.from(merged.keys())) {\n if (isStoreOwnedParam(key)) {\n merged.delete(key);\n }\n }\n\n clearPaginationCursors(merged);\n\n for (const [key, value] of serializeCollectionParams(state)) {\n merged.append(key, value);\n }\n\n return merged;\n}\n\n/** Returns `true` when two router search strings describe the same collection filters and sort. */\nexport function collectionSearchEqual(a: string, b: string): boolean {\n const parsedA = parseCollectionParams(new URLSearchParams(normalizeCollectionSearch(a)));\n const parsedB = parseCollectionParams(new URLSearchParams(normalizeCollectionSearch(b)));\n\n return (\n parsedA.sortKey === parsedB.sortKey &&\n parsedA.reverse === parsedB.reverse &&\n filtersEqual(parsedA.filters, parsedB.filters)\n );\n}\n\n/** Returns `true` when URL params and collection browse state describe the same filters and sort. */\nexport function collectionParamsMatchState(\n searchParams: URLSearchParams,\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): boolean {\n const parsed = parseCollectionParams(searchParams);\n\n return (\n parsed.sortKey === state.sortKey &&\n parsed.reverse === state.reverse &&\n filtersEqual(parsed.filters, state.filters)\n );\n}\n\n/**\n * Builds a URL query string with the given filter removed from the current params.\n *\n * Clears pagination cursors so the changed filter state starts from its first page.\n * Returns `\"?\"` when removing the filter leaves no params. Useful for\n * rendering \"remove filter\" links without updating store state.\n */\nexport function getFilterRemovalUrl(currentParams: URLSearchParams, filter: ProductFilter): string {\n const result = new URLSearchParams(currentParams);\n removeFilterParams(result, filter);\n clearPaginationCursors(result);\n const serialized = result.toString();\n return serialized ? `?${serialized}` : \"?\";\n}\n\n/** Returns `true` when the sort key supports ascending/descending direction suffixes. */\nexport function isDirectionalSortKey(sortKey: ProductCollectionSortKeys): boolean {\n return sortKey === \"PRICE\" || sortKey === \"TITLE\" || sortKey === \"CREATED\" || sortKey === \"ID\";\n}\n\n/**\n * Converts a Storefront API sort key and direction into the Liquid-compatible\n * `sort_by` param value (e.g. `\"price-ascending\"`, `\"best-selling\"`).\n *\n * Direction suffixes are only appended for sort keys that support them\n * (`PRICE`, `TITLE`, `CREATED`, `ID`). Others (like `BEST_SELLING`) return\n * the base value regardless of the `reverse` flag.\n *\n * @param sortKey - Storefront API `ProductCollectionSortKeys` enum value\n * @param reverse - `true` for descending, `false` for ascending\n */\nexport function getSortByValue(sortKey: ProductCollectionSortKeys, reverse: boolean): string {\n const base = SORT_KEY_TO_SORT_BY[sortKey] ?? sortKey.toLowerCase();\n\n if (!isDirectionalSortKey(sortKey)) return base;\n\n return reverse ? `${base}-descending` : `${base}-ascending`;\n}\n\n/**\n * Parses a Liquid-compatible `sort_by` value back into the Storefront API\n * sort key and direction. Inverse of {@link getSortByValue}.\n *\n * @param value - A `sort_by` param value (e.g. `\"price-ascending\"`, `\"best-selling\"`)\n */\nexport function parseSortByValue(value: string): {\n sortKey: ProductCollectionSortKeys | undefined;\n reverse: boolean;\n} {\n if (value.endsWith(SORT_BY_DESCENDING_SUFFIX)) {\n const base = value.slice(0, -SORT_BY_DESCENDING_SUFFIX.length);\n return { sortKey: SORT_BY_TO_SORT_KEY[base], reverse: true };\n }\n\n if (value.endsWith(SORT_BY_ASCENDING_SUFFIX)) {\n const base = value.slice(0, -SORT_BY_ASCENDING_SUFFIX.length);\n return { sortKey: SORT_BY_TO_SORT_KEY[base], reverse: false };\n }\n\n return { sortKey: SORT_BY_TO_SORT_KEY[value], reverse: false };\n}\n\n/**\n * Mirrors `parseProductFilters` from storefront-standard-events.\n *\n * Keep this local because Hydrogen only vendors Standard Events\n * declarations, so importing CollectionUpdateEvent would require runtime JS.\n */\nfunction parseProductFilters(searchParams: URLSearchParams): ProductFilter[] {\n const filters: ProductFilter[] = [];\n const price: ProductFilter[\"price\"] = {};\n const minPrice = searchParams.get(\"filter.v.price.gte\");\n const maxPrice = searchParams.get(\"filter.v.price.lte\");\n\n if (minPrice) price.min = parseLocalizedNumber(minPrice);\n if (maxPrice) price.max = parseLocalizedNumber(maxPrice);\n if (price.min !== undefined || price.max !== undefined) filters.push({ price });\n\n const availability = searchParams.getAll(\"filter.v.availability\");\n const inStock = availability.includes(\"1\");\n const outOfStock = availability.includes(\"0\");\n if (inStock && !outOfStock) filters.push({ available: true });\n if (outOfStock && !inStock) filters.push({ available: false });\n\n for (const productType of searchParams.getAll(\"filter.p.product_type\")) {\n filters.push({ productType });\n }\n\n for (const productVendor of searchParams.getAll(\"filter.p.vendor\")) {\n filters.push({ productVendor });\n }\n\n for (const tag of searchParams.getAll(\"filter.p.tag\")) {\n filters.push({ tag });\n }\n\n for (const [key, value] of searchParams) {\n let match: RegExpMatchArray | null;\n\n if ((match = key.match(/^filter\\.v\\.option\\.(.+)$/))) {\n filters.push({ variantOption: { name: decodeFilterKeyPart(match[1]), value } });\n continue;\n }\n\n if ((match = key.match(/^filter\\.p\\.m\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n productMetafield: {\n namespace: decodeFilterKeyPart(match[1]),\n key: decodeFilterKeyPart(match[2]),\n value,\n },\n });\n continue;\n }\n\n if ((match = key.match(/^filter\\.v\\.m\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n variantMetafield: {\n namespace: decodeFilterKeyPart(match[1]),\n key: decodeFilterKeyPart(match[2]),\n value,\n },\n });\n continue;\n }\n\n if ((match = key.match(/^filter\\.v\\.t\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n taxonomyMetafield: {\n key: `${decodeFilterKeyPart(match[1])}.${decodeFilterKeyPart(match[2])}`,\n value,\n },\n });\n }\n }\n\n return filters;\n}\n\n/** Mirrors `getCollectionSortKey` from storefront-standard-events. */\nfunction parseCollectionSortKey(\n searchParams: URLSearchParams,\n): ProductCollectionSortKeys | undefined {\n const rawSortBy = searchParams.get(\"sort_by\");\n return rawSortBy ? STANDARD_EVENTS_COLLECTION_SORT_KEY[rawSortBy] : undefined;\n}\n\nfunction parseLocalizedNumber(value: string): number {\n try {\n const locale = typeof window !== \"undefined\" ? window.Shopify?.locale : undefined;\n const parts = new Intl.NumberFormat(locale ?? \"en\").formatToParts(1234.5);\n const group = parts.find((part) => part.type === \"group\")?.value ?? \",\";\n const decimal = parts.find((part) => part.type === \"decimal\")?.value ?? \".\";\n const normalized = value.split(group).join(\"\").replace(decimal, \".\");\n const number = Number(normalized);\n if (!Number.isNaN(number)) return number;\n } catch {}\n\n return Number(value.replace(/[^\\d.]/g, \"\")) || 0;\n}\n\nfunction decodeFilterKeyPart(value: string): string {\n return decodeURIComponent(value.replace(/\\+/g, \" \"));\n}\n\nfunction getFilterParamEntries(filter: ProductFilter): Array<{ key: string; value: string }> {\n const entries: Array<{ key: string; value: string }> = [];\n\n if (filter.available != null) {\n entries.push({ key: \"filter.v.availability\", value: filter.available ? \"1\" : \"0\" });\n }\n\n if (filter.price) {\n if (filter.price.min != null) {\n entries.push({ key: \"filter.v.price.gte\", value: String(filter.price.min) });\n }\n if (filter.price.max != null) {\n entries.push({ key: \"filter.v.price.lte\", value: String(filter.price.max) });\n }\n }\n\n if (filter.productType != null) {\n entries.push({ key: \"filter.p.product_type\", value: filter.productType });\n }\n\n if (filter.productVendor != null) {\n entries.push({ key: \"filter.p.vendor\", value: filter.productVendor });\n }\n\n if (filter.tag != null) {\n entries.push({ key: \"filter.p.tag\", value: filter.tag });\n }\n\n if (filter.variantOption) {\n entries.push({\n key: `filter.v.option.${encodeURIComponent(filter.variantOption.name)}`,\n value: filter.variantOption.value ?? \"\",\n });\n }\n\n if (filter.productMetafield) {\n const { namespace, key, value } = filter.productMetafield;\n entries.push({\n key: `filter.p.m.${encodeURIComponent(namespace)}.${encodeURIComponent(key)}`,\n value: value ?? \"\",\n });\n }\n\n if (filter.variantMetafield) {\n const { namespace, key, value } = filter.variantMetafield;\n entries.push({\n key: `filter.v.m.${encodeURIComponent(namespace)}.${encodeURIComponent(key)}`,\n value: value ?? \"\",\n });\n }\n\n if (filter.taxonomyMetafield) {\n const fullKey = filter.taxonomyMetafield.key;\n const dotIdx = fullKey.indexOf(\".\");\n const ns = dotIdx >= 0 ? fullKey.slice(0, dotIdx) : fullKey;\n const k = dotIdx >= 0 ? fullKey.slice(dotIdx + 1) : \"\";\n entries.push({\n key: `filter.v.t.${encodeURIComponent(ns)}.${encodeURIComponent(k)}`,\n value: filter.taxonomyMetafield.value,\n });\n }\n\n if (filter.category) {\n entries.push({ key: \"filter.p.category\", value: filter.category.id });\n }\n\n return entries;\n}\n\nfunction serializeFilter(params: URLSearchParams, filter: ProductFilter): void {\n for (const { key, value } of getFilterParamEntries(filter)) {\n params.append(key, value);\n }\n}\n\nfunction removeFilterParams(params: URLSearchParams, filter: ProductFilter): void {\n for (const { key, value } of getFilterParamEntries(filter)) {\n removeParamValue(params, key, value);\n }\n}\n\nfunction clearPaginationCursors(params: URLSearchParams): void {\n for (const key of PAGINATION_CURSOR_PARAMS) {\n params.delete(key);\n }\n}\n\n/** Compares two individual {@link ProductFilter} objects for semantic equality. */\nexport function filterEquals(a: ProductFilter, b: ProductFilter): boolean {\n const kindA = filterKind(a);\n const kindB = filterKind(b);\n if (kindA !== kindB || kindA == null) return false;\n\n switch (kindA) {\n case \"tag\":\n return a.tag === b.tag;\n case \"available\":\n return a.available === b.available;\n case \"price\":\n return priceRangeEquals(a.price, b.price);\n case \"productType\":\n return a.productType === b.productType;\n case \"productVendor\":\n return a.productVendor === b.productVendor;\n case \"variantOption\": {\n const aOpt = a.variantOption;\n const bOpt = b.variantOption;\n if (aOpt == null || bOpt == null) return false;\n return aOpt.name === bOpt.name && (aOpt.value ?? \"\") === (bOpt.value ?? \"\");\n }\n case \"productMetafield\": {\n const aMeta = a.productMetafield;\n const bMeta = b.productMetafield;\n if (aMeta == null || bMeta == null) return false;\n return metafieldEquals(aMeta, bMeta);\n }\n case \"variantMetafield\": {\n const aMeta = a.variantMetafield;\n const bMeta = b.variantMetafield;\n if (aMeta == null || bMeta == null) return false;\n return metafieldEquals(aMeta, bMeta);\n }\n case \"taxonomyMetafield\": {\n const aMeta = a.taxonomyMetafield;\n const bMeta = b.taxonomyMetafield;\n if (aMeta == null || bMeta == null) return false;\n return aMeta.key === bMeta.key && aMeta.value === bMeta.value;\n }\n case \"category\": {\n const aCat = a.category;\n const bCat = b.category;\n if (aCat == null || bCat == null) return false;\n return aCat.id === bCat.id;\n }\n }\n}\n\n/** Returns `true` when `input` (a Storefront API filter JSON string) matches an active filter. */\nexport function isFilterInputActive(activeFilters: ProductFilter[], input: string): boolean {\n let parsed: ProductFilter;\n try {\n parsed = JSON.parse(input) as ProductFilter;\n } catch {\n return false;\n }\n return activeFilters.some((filter) => filterEquals(filter, parsed));\n}\n\nfunction filterKind(\n filter: ProductFilter,\n):\n | \"tag\"\n | \"available\"\n | \"price\"\n | \"productType\"\n | \"productVendor\"\n | \"variantOption\"\n | \"productMetafield\"\n | \"variantMetafield\"\n | \"taxonomyMetafield\"\n | \"category\"\n | null {\n if (filter.tag != null) return \"tag\";\n if (filter.available != null) return \"available\";\n if (filter.price != null) return \"price\";\n if (filter.productType != null) return \"productType\";\n if (filter.productVendor != null) return \"productVendor\";\n if (filter.variantOption != null) return \"variantOption\";\n if (filter.productMetafield != null) return \"productMetafield\";\n if (filter.variantMetafield != null) return \"variantMetafield\";\n if (filter.taxonomyMetafield != null) return \"taxonomyMetafield\";\n if (filter.category != null) return \"category\";\n return null;\n}\n\nfunction priceRangeEquals(a: ProductFilter[\"price\"], b: ProductFilter[\"price\"]): boolean {\n if (a == null || b == null) return a === b;\n return a.min === b.min && a.max === b.max;\n}\n\nfunction metafieldEquals(\n a: { namespace: string; key: string; value?: string },\n b: { namespace: string; key: string; value?: string },\n): boolean {\n return a.namespace === b.namespace && a.key === b.key && (a.value ?? \"\") === (b.value ?? \"\");\n}\n\nfunction filtersEqual(a: ProductFilter[], b: ProductFilter[]): boolean {\n if (a.length !== b.length) return false;\n\n const remaining = [...b];\n for (const filter of a) {\n const index = remaining.findIndex((candidate) => filterEquals(filter, candidate));\n if (index === -1) return false;\n remaining.splice(index, 1);\n }\n\n return true;\n}\n\nfunction removeParamValue(params: URLSearchParams, name: string, value: string): void {\n const remaining = params.getAll(name).filter((v) => v !== value);\n params.delete(name);\n for (const v of remaining) {\n params.append(name, v);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"url.mjs","names":[],"sources":["../../../src/core/collection/url.ts"],"sourcesContent":["import type { ProductFilter } from \"../../../vendor/standard-events\";\nimport type { ProductCollectionSortKeys } from \"../../graphql/generated/storefront-api-types\";\nimport type { CollectionState } from \"./state\";\n\n/**\n * The filters, sort key, and sort direction in a collection URL.\n */\nexport interface CollectionParams {\n /**\n * Active product filters from the `filter.*` URL params.\n *\n * The filters use the Storefront Standard Events filter type. When your query variables use generated Storefront API types, cast the filters to the generated filter type where you pass them as variables.\n */\n filters: ProductFilter[];\n /** Storefront API sort key from the `sort_by` param, or `undefined` when the param is missing or unrecognized. */\n sortKey: ProductCollectionSortKeys | undefined;\n /** `true` when the `sort_by` value ends with `-descending`. */\n reverse: boolean;\n}\n\nconst SORT_BY_DESCENDING_SUFFIX = \"-descending\";\nconst SORT_BY_ASCENDING_SUFFIX = \"-ascending\";\nconst PAGINATION_CURSOR_PARAMS: readonly string[] = [\"before\", \"after\"];\n\nconst SORT_KEY_TO_SORT_BY: Record<string, string> = {\n BEST_SELLING: \"best-selling\",\n CREATED: \"created\",\n PRICE: \"price\",\n TITLE: \"title\",\n MANUAL: \"manual\",\n ID: \"id\",\n RELEVANCE: \"relevance\",\n COLLECTION_DEFAULT: \"collection-default\",\n};\n\nconst SORT_BY_TO_SORT_KEY: Record<string, ProductCollectionSortKeys> = Object.fromEntries(\n Object.entries(SORT_KEY_TO_SORT_BY).map(([key, value]) => [\n value,\n key as ProductCollectionSortKeys,\n ]),\n);\n\nconst STANDARD_EVENTS_COLLECTION_SORT_KEY: Record<string, ProductCollectionSortKeys> = {\n \"best-selling\": \"BEST_SELLING\",\n \"created-ascending\": \"CREATED\",\n \"created-descending\": \"CREATED\",\n \"price-ascending\": \"PRICE\",\n \"price-descending\": \"PRICE\",\n \"title-ascending\": \"TITLE\",\n \"title-descending\": \"TITLE\",\n manual: \"MANUAL\",\n};\n\n/**\n * Reads collection filters and sort from URL search params. Pass the result to your Storefront API collection query.\n *\n * The function reads Liquid-compatible `filter.p.*` and `filter.v.*` keys and the `sort_by` param.\n *\n * A URL that sets availability to both in stock and out of stock produces no availability filter. The function recognizes the best-selling and manual sort values, plus created, price, and title with an ascending or descending suffix. For other sort values, the sort key is `undefined`. The `parseSortByValue` function also recognizes relevance, collection default, and ID.\n *\n * @param searchParams - URL search params to read.\n * @returns The filters, sort key, and sort direction from the URL.\n * @publicDocs\n */\nexport function parseCollectionParams(searchParams: URLSearchParams): CollectionParams {\n const filters = parseProductFilters(searchParams);\n const sortKey = parseCollectionSortKey(searchParams);\n const rawSortBy = searchParams.get(\"sort_by\");\n const reverse = rawSortBy?.endsWith(SORT_BY_DESCENDING_SUFFIX) ?? false;\n\n return { filters, sortKey, reverse };\n}\n\n/**\n * Converts collection filters and sort to URL search params with Liquid-compatible keys. Use the result to build collection URLs.\n *\n * The result holds only the `filter.*` and `sort_by` keys. The function leaves out `sort_by` for the collection's default sort.\n *\n * Category filters convert to `filter.p.category`, which `parseCollectionParams` doesn't read. A price filter with a minimum and a maximum produces two params. To build a filter checkbox, parse a filter value's `input` string and convert the parsed filter. The resulting entries give the checkbox name and value.\n *\n * @param state - The filters, sort key, and sort direction to convert.\n * @returns URL search params that hold the filters and sort.\n * @publicDocs\n */\nexport function serializeCollectionParams(\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): URLSearchParams {\n const params = new URLSearchParams();\n\n for (const filter of state.filters) {\n serializeFilter(params, filter);\n }\n\n if (state.sortKey) {\n params.set(\"sort_by\", getSortByValue(state.sortKey, state.reverse));\n }\n\n return params;\n}\n\n/** Returns `true` when the param key is owned by the collection store. */\nfunction isStoreOwnedParam(key: string): boolean {\n return key === \"sort_by\" || key.startsWith(\"filter.\");\n}\n\n/**\n * Removes a leading `?` from a search string. Normalize search strings before you compare them.\n *\n * @param search The search string from your router, with or without a leading `?`.\n * @returns The search string without a leading `?`.\n * @publicDocs\n */\nexport function normalizeCollectionSearch(search: string): string {\n return search.startsWith(\"?\") ? search.slice(1) : search;\n}\n\n/**\n * Merges store-owned params from `state` into `existing`, preserving\n * unrelated keys (e.g. `grid`, `view`) already present in the URL. Collection\n * pagination cursors (`before`, `after`) are cleared so changed browse intent\n * starts from the first page.\n */\nexport function mergeCollectionParams(\n existing: URLSearchParams,\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): URLSearchParams {\n const merged = new URLSearchParams(existing);\n\n for (const key of Array.from(merged.keys())) {\n if (isStoreOwnedParam(key)) {\n merged.delete(key);\n }\n }\n\n clearPaginationCursors(merged);\n\n for (const [key, value] of serializeCollectionParams(state)) {\n merged.append(key, value);\n }\n\n return merged;\n}\n\n/** Returns `true` when two router search strings describe the same collection filters and sort. */\nexport function collectionSearchEqual(a: string, b: string): boolean {\n const parsedA = parseCollectionParams(new URLSearchParams(normalizeCollectionSearch(a)));\n const parsedB = parseCollectionParams(new URLSearchParams(normalizeCollectionSearch(b)));\n\n return (\n parsedA.sortKey === parsedB.sortKey &&\n parsedA.reverse === parsedB.reverse &&\n filtersEqual(parsedA.filters, parsedB.filters)\n );\n}\n\n/** Returns `true` when URL params and collection browse state describe the same filters and sort. */\nexport function collectionParamsMatchState(\n searchParams: URLSearchParams,\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): boolean {\n const parsed = parseCollectionParams(searchParams);\n\n return (\n parsed.sortKey === state.sortKey &&\n parsed.reverse === state.reverse &&\n filtersEqual(parsed.filters, state.filters)\n );\n}\n\n/**\n * Builds a query string from the current URL params, minus the given filter. Use the string in plain remove-filter links.\n *\n * The function keeps every other param and drops the `before` and `after` pagination cursors, which sends the customer to the first page of results.\n *\n * @param currentParams The current URL search params.\n * @param filter The filter to remove from the URL.\n * @returns A query string that starts with `?`. The string is `?` alone when no params remain.\n * @publicDocs\n */\nexport function getFilterRemovalUrl(currentParams: URLSearchParams, filter: ProductFilter): string {\n const result = new URLSearchParams(currentParams);\n removeFilterParams(result, filter);\n clearPaginationCursors(result);\n const serialized = result.toString();\n return serialized ? `?${serialized}` : \"?\";\n}\n\n/** Returns `true` when the sort key supports ascending/descending direction suffixes. */\nexport function isDirectionalSortKey(sortKey: ProductCollectionSortKeys): boolean {\n return sortKey === \"PRICE\" || sortKey === \"TITLE\" || sortKey === \"CREATED\" || sortKey === \"ID\";\n}\n\n/**\n * Converts a Storefront API sort key and direction to a Liquid-compatible\n * `sort_by` value, such as `price-ascending` or `best-selling`.\n *\n * Only the price, title, created, and ID sort keys get a direction suffix.\n * Other sort keys, such as best selling, ignore the direction.\n *\n * @param sortKey - Storefront API product collection sort key.\n * @param reverse - `true` for descending, `false` for ascending.\n * @returns The value for the `sort_by` URL param.\n * @publicDocs\n */\nexport function getSortByValue(sortKey: ProductCollectionSortKeys, reverse: boolean): string {\n const base = SORT_KEY_TO_SORT_BY[sortKey] ?? sortKey.toLowerCase();\n\n if (!isDirectionalSortKey(sortKey)) return base;\n\n return reverse ? `${base}-descending` : `${base}-ascending`;\n}\n\n/**\n * Converts a Liquid-compatible `sort_by` value to a Storefront API\n * sort key and direction. The `getSortByValue` function does the opposite conversion.\n *\n * For unrecognized values, the sort key is `undefined`.\n *\n * @param value - A `sort_by` value, such as `price-ascending` or `best-selling`.\n * @returns The sort key and `reverse`, which reads `true` for descending values.\n * @publicDocs\n */\nexport function parseSortByValue(value: string): {\n sortKey: ProductCollectionSortKeys | undefined;\n reverse: boolean;\n} {\n if (value.endsWith(SORT_BY_DESCENDING_SUFFIX)) {\n const base = value.slice(0, -SORT_BY_DESCENDING_SUFFIX.length);\n return { sortKey: SORT_BY_TO_SORT_KEY[base], reverse: true };\n }\n\n if (value.endsWith(SORT_BY_ASCENDING_SUFFIX)) {\n const base = value.slice(0, -SORT_BY_ASCENDING_SUFFIX.length);\n return { sortKey: SORT_BY_TO_SORT_KEY[base], reverse: false };\n }\n\n return { sortKey: SORT_BY_TO_SORT_KEY[value], reverse: false };\n}\n\n/**\n * Mirrors `parseProductFilters` from storefront-standard-events.\n *\n * Keep this local because Hydrogen only vendors Standard Events\n * declarations, so importing CollectionUpdateEvent would require runtime JS.\n */\nfunction parseProductFilters(searchParams: URLSearchParams): ProductFilter[] {\n const filters: ProductFilter[] = [];\n const price: ProductFilter[\"price\"] = {};\n const minPrice = searchParams.get(\"filter.v.price.gte\");\n const maxPrice = searchParams.get(\"filter.v.price.lte\");\n\n if (minPrice) price.min = parseLocalizedNumber(minPrice);\n if (maxPrice) price.max = parseLocalizedNumber(maxPrice);\n if (price.min !== undefined || price.max !== undefined) filters.push({ price });\n\n const availability = searchParams.getAll(\"filter.v.availability\");\n const inStock = availability.includes(\"1\");\n const outOfStock = availability.includes(\"0\");\n if (inStock && !outOfStock) filters.push({ available: true });\n if (outOfStock && !inStock) filters.push({ available: false });\n\n for (const productType of searchParams.getAll(\"filter.p.product_type\")) {\n filters.push({ productType });\n }\n\n for (const productVendor of searchParams.getAll(\"filter.p.vendor\")) {\n filters.push({ productVendor });\n }\n\n for (const tag of searchParams.getAll(\"filter.p.tag\")) {\n filters.push({ tag });\n }\n\n for (const [key, value] of searchParams) {\n let match: RegExpMatchArray | null;\n\n if ((match = key.match(/^filter\\.v\\.option\\.(.+)$/))) {\n filters.push({ variantOption: { name: decodeFilterKeyPart(match[1]), value } });\n continue;\n }\n\n if ((match = key.match(/^filter\\.p\\.m\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n productMetafield: {\n namespace: decodeFilterKeyPart(match[1]),\n key: decodeFilterKeyPart(match[2]),\n value,\n },\n });\n continue;\n }\n\n if ((match = key.match(/^filter\\.v\\.m\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n variantMetafield: {\n namespace: decodeFilterKeyPart(match[1]),\n key: decodeFilterKeyPart(match[2]),\n value,\n },\n });\n continue;\n }\n\n if ((match = key.match(/^filter\\.v\\.t\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n taxonomyMetafield: {\n key: `${decodeFilterKeyPart(match[1])}.${decodeFilterKeyPart(match[2])}`,\n value,\n },\n });\n }\n }\n\n return filters;\n}\n\n/** Mirrors `getCollectionSortKey` from storefront-standard-events. */\nfunction parseCollectionSortKey(\n searchParams: URLSearchParams,\n): ProductCollectionSortKeys | undefined {\n const rawSortBy = searchParams.get(\"sort_by\");\n return rawSortBy ? STANDARD_EVENTS_COLLECTION_SORT_KEY[rawSortBy] : undefined;\n}\n\nfunction parseLocalizedNumber(value: string): number {\n try {\n const locale = typeof window !== \"undefined\" ? window.Shopify?.locale : undefined;\n const parts = new Intl.NumberFormat(locale ?? \"en\").formatToParts(1234.5);\n const group = parts.find((part) => part.type === \"group\")?.value ?? \",\";\n const decimal = parts.find((part) => part.type === \"decimal\")?.value ?? \".\";\n const normalized = value.split(group).join(\"\").replace(decimal, \".\");\n const number = Number(normalized);\n if (!Number.isNaN(number)) return number;\n } catch {}\n\n return Number(value.replace(/[^\\d.]/g, \"\")) || 0;\n}\n\nfunction decodeFilterKeyPart(value: string): string {\n return decodeURIComponent(value.replace(/\\+/g, \" \"));\n}\n\nfunction getFilterParamEntries(filter: ProductFilter): Array<{ key: string; value: string }> {\n const entries: Array<{ key: string; value: string }> = [];\n\n if (filter.available != null) {\n entries.push({ key: \"filter.v.availability\", value: filter.available ? \"1\" : \"0\" });\n }\n\n if (filter.price) {\n if (filter.price.min != null) {\n entries.push({ key: \"filter.v.price.gte\", value: String(filter.price.min) });\n }\n if (filter.price.max != null) {\n entries.push({ key: \"filter.v.price.lte\", value: String(filter.price.max) });\n }\n }\n\n if (filter.productType != null) {\n entries.push({ key: \"filter.p.product_type\", value: filter.productType });\n }\n\n if (filter.productVendor != null) {\n entries.push({ key: \"filter.p.vendor\", value: filter.productVendor });\n }\n\n if (filter.tag != null) {\n entries.push({ key: \"filter.p.tag\", value: filter.tag });\n }\n\n if (filter.variantOption) {\n entries.push({\n key: `filter.v.option.${encodeURIComponent(filter.variantOption.name)}`,\n value: filter.variantOption.value ?? \"\",\n });\n }\n\n if (filter.productMetafield) {\n const { namespace, key, value } = filter.productMetafield;\n entries.push({\n key: `filter.p.m.${encodeURIComponent(namespace)}.${encodeURIComponent(key)}`,\n value: value ?? \"\",\n });\n }\n\n if (filter.variantMetafield) {\n const { namespace, key, value } = filter.variantMetafield;\n entries.push({\n key: `filter.v.m.${encodeURIComponent(namespace)}.${encodeURIComponent(key)}`,\n value: value ?? \"\",\n });\n }\n\n if (filter.taxonomyMetafield) {\n const fullKey = filter.taxonomyMetafield.key;\n const dotIdx = fullKey.indexOf(\".\");\n const ns = dotIdx >= 0 ? fullKey.slice(0, dotIdx) : fullKey;\n const k = dotIdx >= 0 ? fullKey.slice(dotIdx + 1) : \"\";\n entries.push({\n key: `filter.v.t.${encodeURIComponent(ns)}.${encodeURIComponent(k)}`,\n value: filter.taxonomyMetafield.value,\n });\n }\n\n if (filter.category) {\n entries.push({ key: \"filter.p.category\", value: filter.category.id });\n }\n\n return entries;\n}\n\nfunction serializeFilter(params: URLSearchParams, filter: ProductFilter): void {\n for (const { key, value } of getFilterParamEntries(filter)) {\n params.append(key, value);\n }\n}\n\nfunction removeFilterParams(params: URLSearchParams, filter: ProductFilter): void {\n for (const { key, value } of getFilterParamEntries(filter)) {\n removeParamValue(params, key, value);\n }\n}\n\nfunction clearPaginationCursors(params: URLSearchParams): void {\n for (const key of PAGINATION_CURSOR_PARAMS) {\n params.delete(key);\n }\n}\n\n/**\n * Checks whether two product filters match.\n *\n * Price filters match when the minimum and maximum are equal, including ranges that set only one bound.\n *\n * @param a The first filter to compare.\n * @param b The second filter to compare.\n * @returns `true` when both filters are the same kind with the same values. `false` for different kinds or unrecognized filters.\n * @publicDocs\n */\nexport function filterEquals(a: ProductFilter, b: ProductFilter): boolean {\n const kindA = filterKind(a);\n const kindB = filterKind(b);\n if (kindA !== kindB || kindA == null) return false;\n\n switch (kindA) {\n case \"tag\":\n return a.tag === b.tag;\n case \"available\":\n return a.available === b.available;\n case \"price\":\n return priceRangeEquals(a.price, b.price);\n case \"productType\":\n return a.productType === b.productType;\n case \"productVendor\":\n return a.productVendor === b.productVendor;\n case \"variantOption\": {\n const aOpt = a.variantOption;\n const bOpt = b.variantOption;\n if (aOpt == null || bOpt == null) return false;\n return aOpt.name === bOpt.name && (aOpt.value ?? \"\") === (bOpt.value ?? \"\");\n }\n case \"productMetafield\": {\n const aMeta = a.productMetafield;\n const bMeta = b.productMetafield;\n if (aMeta == null || bMeta == null) return false;\n return metafieldEquals(aMeta, bMeta);\n }\n case \"variantMetafield\": {\n const aMeta = a.variantMetafield;\n const bMeta = b.variantMetafield;\n if (aMeta == null || bMeta == null) return false;\n return metafieldEquals(aMeta, bMeta);\n }\n case \"taxonomyMetafield\": {\n const aMeta = a.taxonomyMetafield;\n const bMeta = b.taxonomyMetafield;\n if (aMeta == null || bMeta == null) return false;\n return aMeta.key === bMeta.key && aMeta.value === bMeta.value;\n }\n case \"category\": {\n const aCat = a.category;\n const bCat = b.category;\n if (aCat == null || bCat == null) return false;\n return aCat.id === bCat.id;\n }\n }\n}\n\n/**\n * Checks whether a filter value's `input` JSON string from the Storefront API matches an active filter. Use the result to set the checked state of a filter control.\n *\n * The function compares filters the same way as `filterEquals`.\n *\n * @param activeFilters The active filters, such as `filters` from the collection state.\n * @param input The filter value's `input` JSON string.\n * @returns `true` when the parsed input matches an active filter. `false` when the input matches no active filter or isn't valid JSON.\n * @publicDocs\n */\nexport function isFilterInputActive(activeFilters: ProductFilter[], input: string): boolean {\n let parsed: ProductFilter;\n try {\n parsed = JSON.parse(input) as ProductFilter;\n } catch {\n return false;\n }\n return activeFilters.some((filter) => filterEquals(filter, parsed));\n}\n\nfunction filterKind(\n filter: ProductFilter,\n):\n | \"tag\"\n | \"available\"\n | \"price\"\n | \"productType\"\n | \"productVendor\"\n | \"variantOption\"\n | \"productMetafield\"\n | \"variantMetafield\"\n | \"taxonomyMetafield\"\n | \"category\"\n | null {\n if (filter.tag != null) return \"tag\";\n if (filter.available != null) return \"available\";\n if (filter.price != null) return \"price\";\n if (filter.productType != null) return \"productType\";\n if (filter.productVendor != null) return \"productVendor\";\n if (filter.variantOption != null) return \"variantOption\";\n if (filter.productMetafield != null) return \"productMetafield\";\n if (filter.variantMetafield != null) return \"variantMetafield\";\n if (filter.taxonomyMetafield != null) return \"taxonomyMetafield\";\n if (filter.category != null) return \"category\";\n return null;\n}\n\nfunction priceRangeEquals(a: ProductFilter[\"price\"], b: ProductFilter[\"price\"]): boolean {\n if (a == null || b == null) return a === b;\n return a.min === b.min && a.max === b.max;\n}\n\nfunction metafieldEquals(\n a: { namespace: string; key: string; value?: string },\n b: { namespace: string; key: string; value?: string },\n): boolean {\n return a.namespace === b.namespace && a.key === b.key && (a.value ?? \"\") === (b.value ?? \"\");\n}\n\nfunction filtersEqual(a: ProductFilter[], b: ProductFilter[]): boolean {\n if (a.length !== b.length) return false;\n\n const remaining = [...b];\n for (const filter of a) {\n const index = remaining.findIndex((candidate) => filterEquals(filter, candidate));\n if (index === -1) return false;\n remaining.splice(index, 1);\n }\n\n return true;\n}\n\nfunction removeParamValue(params: URLSearchParams, name: string, value: string): void {\n const remaining = params.getAll(name).filter((v) => v !== value);\n params.delete(name);\n for (const v of remaining) {\n params.append(name, v);\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  //#region src/core/constants.d.ts
2
+ /** @publicDocs */
2
3
  declare const CUSTOMER_ACCOUNT_API_VERSION = "2026-10";
3
4
  //#endregion
4
5
  export { CUSTOMER_ACCOUNT_API_VERSION };
@@ -1,5 +1,6 @@
1
1
  //#region src/core/constants.ts
2
2
  const STOREFRONT_API_VERSION = "2026-10";
3
+ /** @publicDocs */
3
4
  const CUSTOMER_ACCOUNT_API_VERSION = "2026-10";
4
5
  const DEFAULT_TIMEOUT_IN_MS = 3e4;
5
6
 
@@ -1 +1 @@
1
- {"version":3,"file":"constants.mjs","names":[],"sources":["../../src/core/constants.ts"],"sourcesContent":["export const STOREFRONT_API_VERSION = \"2026-10\";\nexport const CUSTOMER_ACCOUNT_API_VERSION = \"2026-10\";\nexport const DEFAULT_TIMEOUT_IN_MS = 30_000;\n"],"mappings":";AAAA,MAAa,yBAAyB;AACtC,MAAa,+BAA+B;AAC5C,MAAa,wBAAwB"}
1
+ {"version":3,"file":"constants.mjs","names":[],"sources":["../../src/core/constants.ts"],"sourcesContent":["export const STOREFRONT_API_VERSION = \"2026-10\";\n/** @publicDocs */\nexport const CUSTOMER_ACCOUNT_API_VERSION = \"2026-10\";\nexport const DEFAULT_TIMEOUT_IN_MS = 30_000;\n"],"mappings":";AAAA,MAAa,yBAAyB;;AAEtC,MAAa,+BAA+B;AAC5C,MAAa,wBAAwB"}
@@ -1,19 +1,25 @@
1
1
  import { HydrogenLogger, LogLevel } from "./types.mjs";
2
2
 
3
3
  //#region src/core/logging/logging.d.ts
4
+ /** The logger and minimum severity for Hydrogen log entries. */
4
5
  type ConfigureLoggingOptions = {
5
- /** Receives all entries at or above `level`. Defaults to the built-in console logger. */logger?: HydrogenLogger; /** Minimum severity forwarded to the logger. Defaults to `"info"`. */
6
+ /** Receives all entries at or above `level`. Defaults to the built-in console logger. */logger?: HydrogenLogger; /** The minimum severity that Hydrogen forwards to the logger. Defaults to `info`. */
6
7
  level?: LogLevel;
7
8
  };
8
9
  /**
9
- * Configures logging for every Hydrogen helper in this JavaScript context.
10
- * Call it once at startup (app entry on the browser, module init on the
11
- * server) before Hydrogen helpers run.
10
+ * Sends Hydrogen's log entries to your logger at the minimum level you choose. Use the function to
11
+ * forward Hydrogen errors to a monitoring service or to reduce console output.
12
12
  *
13
- * Reconfiguring with different options applies the new options (last call
14
- * wins). Inline bootstrap scripts that Hydrogen serializes into HTML
15
- * (analytics, consent) run outside the app bundle and always write to the
16
- * console with the standard prefix; they cannot receive a custom logger.
13
+ * Call the function once at startup. In the browser, call the function at app entry. On the
14
+ * server, call the function during module initialization. The configuration applies to every
15
+ * Hydrogen helper in the current JavaScript context, including helpers that loaded before the call.
16
+ * Each call replaces the previous configuration, and options that the call omits reset to their
17
+ * defaults. Hydrogen's inline analytics and consent scripts run outside your app bundle, and they
18
+ * always write to the console.
19
+ *
20
+ * @param options The logger that receives entries, and the minimum severity to send to the logger.
21
+ * @returns Nothing.
22
+ * @publicDocs
17
23
  */
18
24
  declare function configureLogging(options: ConfigureLoggingOptions): void;
19
25
  //#endregion
@@ -20,9 +20,9 @@ function writeToConsole(level, message, context) {
20
20
  console[CONSOLE_METHODS[level]](...args);
21
21
  }
22
22
  /**
23
- * Built-in sink: writes `[hydrogen:<level>:<scope>] <message>` to a
24
- * level-specific `console` method, followed by `context.error` and any extra
25
- * context fields. `trace` uses `console.debug`; `fatal` uses `console.error`.
23
+ * The built-in logger. Writes each entry to the matching console method, with a
24
+ * `[hydrogen:<level>:<scope>]` prefix when the entry has a scope, followed by the error and any
25
+ * extra context fields. Trace entries use `console.debug`, and fatal entries use `console.error`.
26
26
  */
27
27
  const consoleLogger = {
28
28
  trace: (message, context) => writeToConsole("trace", message, context),
@@ -37,14 +37,19 @@ const state = {
37
37
  level: DEFAULT_LOG_LEVEL
38
38
  };
39
39
  /**
40
- * Configures logging for every Hydrogen helper in this JavaScript context.
41
- * Call it once at startup (app entry on the browser, module init on the
42
- * server) before Hydrogen helpers run.
40
+ * Sends Hydrogen's log entries to your logger at the minimum level you choose. Use the function to
41
+ * forward Hydrogen errors to a monitoring service or to reduce console output.
43
42
  *
44
- * Reconfiguring with different options applies the new options (last call
45
- * wins). Inline bootstrap scripts that Hydrogen serializes into HTML
46
- * (analytics, consent) run outside the app bundle and always write to the
47
- * console with the standard prefix; they cannot receive a custom logger.
43
+ * Call the function once at startup. In the browser, call the function at app entry. On the
44
+ * server, call the function during module initialization. The configuration applies to every
45
+ * Hydrogen helper in the current JavaScript context, including helpers that loaded before the call.
46
+ * Each call replaces the previous configuration, and options that the call omits reset to their
47
+ * defaults. Hydrogen's inline analytics and consent scripts run outside your app bundle, and they
48
+ * always write to the console.
49
+ *
50
+ * @param options The logger that receives entries, and the minimum severity to send to the logger.
51
+ * @returns Nothing.
52
+ * @publicDocs
48
53
  */
49
54
  function configureLogging(options) {
50
55
  const logger = options.logger ?? consoleLogger;
@@ -1 +1 @@
1
- {"version":3,"file":"logging.mjs","names":[],"sources":["../../../src/core/logging/logging.ts"],"sourcesContent":["import {\n DEFAULT_LOG_LEVEL,\n isLevelEnabled,\n type HydrogenLogger,\n type LogContext,\n type LogLevel,\n type LogSeverity,\n} from \"./types\";\n\nconst CONSOLE_METHODS: Record<LogSeverity, \"debug\" | \"info\" | \"warn\" | \"error\"> = {\n // `console.trace` prints a stack trace for every call, which is too noisy\n // for level-gated trace logs; route it to `console.debug` instead.\n trace: \"debug\",\n debug: \"debug\",\n info: \"info\",\n warn: \"warn\",\n error: \"error\",\n fatal: \"error\",\n};\n\nfunction formatLogPrefix(level: LogSeverity, scope: string): string {\n return `[hydrogen:${level}:${scope}]`;\n}\n\nfunction writeToConsole(level: LogSeverity, message: string, context?: LogContext): void {\n const { scope, error, ...rest } = context ?? {};\n const prefixed = scope ? `${formatLogPrefix(level, scope)} ${message}` : message;\n const args: unknown[] = [prefixed];\n if (error !== undefined) args.push(error);\n if (Object.keys(rest).length > 0) args.push(rest);\n\n // oxlint-disable-next-line no-console -- The built-in sink is the one sanctioned console call site.\n console[CONSOLE_METHODS[level]](...args);\n}\n\n/**\n * Built-in sink: writes `[hydrogen:<level>:<scope>] <message>` to a\n * level-specific `console` method, followed by `context.error` and any extra\n * context fields. `trace` uses `console.debug`; `fatal` uses `console.error`.\n */\nexport const consoleLogger: HydrogenLogger = {\n trace: (message, context) => writeToConsole(\"trace\", message, context),\n debug: (message, context) => writeToConsole(\"debug\", message, context),\n info: (message, context) => writeToConsole(\"info\", message, context),\n warn: (message, context) => writeToConsole(\"warn\", message, context),\n error: (message, context) => writeToConsole(\"error\", message, context),\n fatal: (message, context) => writeToConsole(\"fatal\", message, context),\n};\n\nexport type ConfigureLoggingOptions = {\n /** Receives all entries at or above `level`. Defaults to the built-in console logger. */\n logger?: HydrogenLogger;\n /** Minimum severity forwarded to the logger. Defaults to `\"info\"`. */\n level?: LogLevel;\n};\n\ntype LoggingState = {\n logger: HydrogenLogger;\n level: LogLevel;\n};\n\nconst state: LoggingState = {\n logger: consoleLogger,\n level: DEFAULT_LOG_LEVEL,\n};\n\n/**\n * Configures logging for every Hydrogen helper in this JavaScript context.\n * Call it once at startup (app entry on the browser, module init on the\n * server) before Hydrogen helpers run.\n *\n * Reconfiguring with different options applies the new options (last call\n * wins). Inline bootstrap scripts that Hydrogen serializes into HTML\n * (analytics, consent) run outside the app bundle and always write to the\n * console with the standard prefix; they cannot receive a custom logger.\n */\nexport function configureLogging(options: ConfigureLoggingOptions): void {\n const logger = options.logger ?? consoleLogger;\n const level = options.level ?? DEFAULT_LOG_LEVEL;\n\n state.logger = logger;\n state.level = level;\n}\n\n/**\n * Scoped logger used by Hydrogen internals. Entries are level-gated and\n * tagged with `scope`, then forwarded to the configured sink. Resolution is\n * lazy: `configureLogging` affects loggers obtained before or after the call.\n */\ntype ScopedLogContext = Omit<LogContext, \"scope\"> & { scope?: never };\n\ntype ScopedLogger = Record<LogSeverity, (message: string, context?: ScopedLogContext) => void>;\n\nfunction emit(scope: string, level: LogSeverity, message: string, context?: LogContext): void {\n if (!isLevelEnabled(level, state.level)) return;\n\n try {\n state.logger[level](message, { ...context, scope });\n } catch (error) {\n reportLoggerFailure(error, { level, scope, message, context });\n }\n}\n\ntype FailedLogEntry = {\n level: LogSeverity;\n scope: string;\n message: string;\n context?: LogContext;\n};\n\nfunction reportLoggerFailure(error: unknown, failedEntry: FailedLogEntry): void {\n if (state.logger === consoleLogger) return;\n\n const fallbackContext: LogContext = {\n scope: \"logging\",\n error,\n originalLevel: failedEntry.level,\n originalScope: failedEntry.scope,\n originalMessage: failedEntry.message,\n };\n if (failedEntry.context !== undefined) fallbackContext.originalContext = failedEntry.context;\n\n try {\n consoleLogger.error(\"configured logger failed\", fallbackContext);\n } catch {\n // Logging failures must never change the runtime path that triggered the log.\n }\n}\n\n/** @internal Returns the lazily-resolved scoped logger for a Hydrogen subsystem. */\nexport function getLogger(scope: string): ScopedLogger {\n return {\n trace: (message, context) => emit(scope, \"trace\", message, context),\n debug: (message, context) => emit(scope, \"debug\", message, context),\n info: (message, context) => emit(scope, \"info\", message, context),\n warn: (message, context) => emit(scope, \"warn\", message, context),\n error: (message, context) => emit(scope, \"error\", message, context),\n fatal: (message, context) => emit(scope, \"fatal\", message, context),\n };\n}\n"],"mappings":";;;AASA,MAAM,kBAA4E;CAGhF,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACR;AAED,SAAS,gBAAgB,OAAoB,OAAuB;AAClE,QAAO,aAAa,MAAM,GAAG,MAAM;;AAGrC,SAAS,eAAe,OAAoB,SAAiB,SAA4B;CACvF,MAAM,EAAE,OAAO,OAAO,GAAG,SAAS,WAAW,EAAE;CAE/C,MAAM,OAAkB,CADP,QAAQ,GAAG,gBAAgB,OAAO,MAAM,CAAC,GAAG,YAAY,QACvC;AAClC,KAAI,UAAU,OAAW,MAAK,KAAK,MAAM;AACzC,KAAI,OAAO,KAAK,KAAK,CAAC,SAAS,EAAG,MAAK,KAAK,KAAK;AAGjD,SAAQ,gBAAgB,QAAQ,GAAG,KAAK;;;;;;;AAQ1C,MAAa,gBAAgC;CAC3C,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACtE,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACtE,OAAO,SAAS,YAAY,eAAe,QAAQ,SAAS,QAAQ;CACpE,OAAO,SAAS,YAAY,eAAe,QAAQ,SAAS,QAAQ;CACpE,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACtE,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACvE;AAcD,MAAM,QAAsB;CAC1B,QAAQ;CACR,OAAO;CACR;;;;;;;;;;;AAYD,SAAgB,iBAAiB,SAAwC;CACvE,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,QAAQ,QAAQ;AAEtB,OAAM,SAAS;AACf,OAAM,QAAQ;;AAYhB,SAAS,KAAK,OAAe,OAAoB,SAAiB,SAA4B;AAC5F,KAAI,CAAC,eAAe,OAAO,MAAM,MAAM,CAAE;AAEzC,KAAI;AACF,QAAM,OAAO,OAAO,SAAS;GAAE,GAAG;GAAS;GAAO,CAAC;UAC5C,OAAO;AACd,sBAAoB,OAAO;GAAE;GAAO;GAAO;GAAS;GAAS,CAAC;;;AAWlE,SAAS,oBAAoB,OAAgB,aAAmC;AAC9E,KAAI,MAAM,WAAW,cAAe;CAEpC,MAAM,kBAA8B;EAClC,OAAO;EACP;EACA,eAAe,YAAY;EAC3B,eAAe,YAAY;EAC3B,iBAAiB,YAAY;EAC9B;AACD,KAAI,YAAY,YAAY,OAAW,iBAAgB,kBAAkB,YAAY;AAErF,KAAI;AACF,gBAAc,MAAM,4BAA4B,gBAAgB;SAC1D;;;AAMV,SAAgB,UAAU,OAA6B;AACrD,QAAO;EACL,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACnE,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACnE,OAAO,SAAS,YAAY,KAAK,OAAO,QAAQ,SAAS,QAAQ;EACjE,OAAO,SAAS,YAAY,KAAK,OAAO,QAAQ,SAAS,QAAQ;EACjE,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACnE,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACpE"}
1
+ {"version":3,"file":"logging.mjs","names":[],"sources":["../../../src/core/logging/logging.ts"],"sourcesContent":["import {\n DEFAULT_LOG_LEVEL,\n isLevelEnabled,\n type HydrogenLogger,\n type LogContext,\n type LogLevel,\n type LogSeverity,\n} from \"./types\";\n\nconst CONSOLE_METHODS: Record<LogSeverity, \"debug\" | \"info\" | \"warn\" | \"error\"> = {\n // `console.trace` prints a stack trace for every call, which is too noisy\n // for level-gated trace logs; route it to `console.debug` instead.\n trace: \"debug\",\n debug: \"debug\",\n info: \"info\",\n warn: \"warn\",\n error: \"error\",\n fatal: \"error\",\n};\n\nfunction formatLogPrefix(level: LogSeverity, scope: string): string {\n return `[hydrogen:${level}:${scope}]`;\n}\n\nfunction writeToConsole(level: LogSeverity, message: string, context?: LogContext): void {\n const { scope, error, ...rest } = context ?? {};\n const prefixed = scope ? `${formatLogPrefix(level, scope)} ${message}` : message;\n const args: unknown[] = [prefixed];\n if (error !== undefined) args.push(error);\n if (Object.keys(rest).length > 0) args.push(rest);\n\n // oxlint-disable-next-line no-console -- The built-in sink is the one sanctioned console call site.\n console[CONSOLE_METHODS[level]](...args);\n}\n\n/**\n * The built-in logger. Writes each entry to the matching console method, with a\n * `[hydrogen:<level>:<scope>]` prefix when the entry has a scope, followed by the error and any\n * extra context fields. Trace entries use `console.debug`, and fatal entries use `console.error`.\n */\nexport const consoleLogger: HydrogenLogger = {\n trace: (message, context) => writeToConsole(\"trace\", message, context),\n debug: (message, context) => writeToConsole(\"debug\", message, context),\n info: (message, context) => writeToConsole(\"info\", message, context),\n warn: (message, context) => writeToConsole(\"warn\", message, context),\n error: (message, context) => writeToConsole(\"error\", message, context),\n fatal: (message, context) => writeToConsole(\"fatal\", message, context),\n};\n\n/** The logger and minimum severity for Hydrogen log entries. */\nexport type ConfigureLoggingOptions = {\n /** Receives all entries at or above `level`. Defaults to the built-in console logger. */\n logger?: HydrogenLogger;\n /** The minimum severity that Hydrogen forwards to the logger. Defaults to `info`. */\n level?: LogLevel;\n};\n\ntype LoggingState = {\n logger: HydrogenLogger;\n level: LogLevel;\n};\n\nconst state: LoggingState = {\n logger: consoleLogger,\n level: DEFAULT_LOG_LEVEL,\n};\n\n/**\n * Sends Hydrogen's log entries to your logger at the minimum level you choose. Use the function to\n * forward Hydrogen errors to a monitoring service or to reduce console output.\n *\n * Call the function once at startup. In the browser, call the function at app entry. On the\n * server, call the function during module initialization. The configuration applies to every\n * Hydrogen helper in the current JavaScript context, including helpers that loaded before the call.\n * Each call replaces the previous configuration, and options that the call omits reset to their\n * defaults. Hydrogen's inline analytics and consent scripts run outside your app bundle, and they\n * always write to the console.\n *\n * @param options The logger that receives entries, and the minimum severity to send to the logger.\n * @returns Nothing.\n * @publicDocs\n */\nexport function configureLogging(options: ConfigureLoggingOptions): void {\n const logger = options.logger ?? consoleLogger;\n const level = options.level ?? DEFAULT_LOG_LEVEL;\n\n state.logger = logger;\n state.level = level;\n}\n\n/**\n * Scoped logger used by Hydrogen internals. Entries are level-gated and\n * tagged with `scope`, then forwarded to the configured sink. Resolution is\n * lazy: `configureLogging` affects loggers obtained before or after the call.\n */\ntype ScopedLogContext = Omit<LogContext, \"scope\"> & { scope?: never };\n\ntype ScopedLogger = Record<LogSeverity, (message: string, context?: ScopedLogContext) => void>;\n\nfunction emit(scope: string, level: LogSeverity, message: string, context?: LogContext): void {\n if (!isLevelEnabled(level, state.level)) return;\n\n try {\n state.logger[level](message, { ...context, scope });\n } catch (error) {\n reportLoggerFailure(error, { level, scope, message, context });\n }\n}\n\ntype FailedLogEntry = {\n level: LogSeverity;\n scope: string;\n message: string;\n context?: LogContext;\n};\n\nfunction reportLoggerFailure(error: unknown, failedEntry: FailedLogEntry): void {\n if (state.logger === consoleLogger) return;\n\n const fallbackContext: LogContext = {\n scope: \"logging\",\n error,\n originalLevel: failedEntry.level,\n originalScope: failedEntry.scope,\n originalMessage: failedEntry.message,\n };\n if (failedEntry.context !== undefined) fallbackContext.originalContext = failedEntry.context;\n\n try {\n consoleLogger.error(\"configured logger failed\", fallbackContext);\n } catch {\n // Logging failures must never change the runtime path that triggered the log.\n }\n}\n\n/** @internal Returns the lazily-resolved scoped logger for a Hydrogen subsystem. */\nexport function getLogger(scope: string): ScopedLogger {\n return {\n trace: (message, context) => emit(scope, \"trace\", message, context),\n debug: (message, context) => emit(scope, \"debug\", message, context),\n info: (message, context) => emit(scope, \"info\", message, context),\n warn: (message, context) => emit(scope, \"warn\", message, context),\n error: (message, context) => emit(scope, \"error\", message, context),\n fatal: (message, context) => emit(scope, \"fatal\", message, context),\n };\n}\n"],"mappings":";;;AASA,MAAM,kBAA4E;CAGhF,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACR;AAED,SAAS,gBAAgB,OAAoB,OAAuB;AAClE,QAAO,aAAa,MAAM,GAAG,MAAM;;AAGrC,SAAS,eAAe,OAAoB,SAAiB,SAA4B;CACvF,MAAM,EAAE,OAAO,OAAO,GAAG,SAAS,WAAW,EAAE;CAE/C,MAAM,OAAkB,CADP,QAAQ,GAAG,gBAAgB,OAAO,MAAM,CAAC,GAAG,YAAY,QACvC;AAClC,KAAI,UAAU,OAAW,MAAK,KAAK,MAAM;AACzC,KAAI,OAAO,KAAK,KAAK,CAAC,SAAS,EAAG,MAAK,KAAK,KAAK;AAGjD,SAAQ,gBAAgB,QAAQ,GAAG,KAAK;;;;;;;AAQ1C,MAAa,gBAAgC;CAC3C,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACtE,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACtE,OAAO,SAAS,YAAY,eAAe,QAAQ,SAAS,QAAQ;CACpE,OAAO,SAAS,YAAY,eAAe,QAAQ,SAAS,QAAQ;CACpE,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACtE,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACvE;AAeD,MAAM,QAAsB;CAC1B,QAAQ;CACR,OAAO;CACR;;;;;;;;;;;;;;;;AAiBD,SAAgB,iBAAiB,SAAwC;CACvE,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,QAAQ,QAAQ;AAEtB,OAAM,SAAS;AACf,OAAM,QAAQ;;AAYhB,SAAS,KAAK,OAAe,OAAoB,SAAiB,SAA4B;AAC5F,KAAI,CAAC,eAAe,OAAO,MAAM,MAAM,CAAE;AAEzC,KAAI;AACF,QAAM,OAAO,OAAO,SAAS;GAAE,GAAG;GAAS;GAAO,CAAC;UAC5C,OAAO;AACd,sBAAoB,OAAO;GAAE;GAAO;GAAO;GAAS;GAAS,CAAC;;;AAWlE,SAAS,oBAAoB,OAAgB,aAAmC;AAC9E,KAAI,MAAM,WAAW,cAAe;CAEpC,MAAM,kBAA8B;EAClC,OAAO;EACP;EACA,eAAe,YAAY;EAC3B,eAAe,YAAY;EAC3B,iBAAiB,YAAY;EAC9B;AACD,KAAI,YAAY,YAAY,OAAW,iBAAgB,kBAAkB,YAAY;AAErF,KAAI;AACF,gBAAc,MAAM,4BAA4B,gBAAgB;SAC1D;;;AAMV,SAAgB,UAAU,OAA6B;AACrD,QAAO;EACL,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACnE,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACnE,OAAO,SAAS,YAAY,KAAK,OAAO,QAAQ,SAAS,QAAQ;EACjE,OAAO,SAAS,YAAY,KAAK,OAAO,QAAQ,SAAS,QAAQ;EACjE,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACnE,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACpE"}