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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) hide show
  1. package/README.md +34 -3
  2. package/dist/cli/index.mjs +578 -85
  3. package/dist/client/client.mjs +2 -2
  4. package/dist/client/errors.d.mts +19 -19
  5. package/dist/client/errors.mjs +19 -16
  6. package/dist/client/errors.mjs.map +1 -1
  7. package/dist/client/types.d.mts +91 -10
  8. package/dist/core/analytics/cart-tracker.d.mts +14 -0
  9. package/dist/core/analytics/cart-tracker.mjs +15 -1
  10. package/dist/core/analytics/cart-tracker.mjs.map +1 -1
  11. package/dist/core/analytics/custom-consent.mjs +12 -0
  12. package/dist/core/analytics/custom-consent.mjs.map +1 -0
  13. package/dist/core/analytics/events.d.mts +7 -0
  14. package/dist/core/analytics/events.mjs +7 -0
  15. package/dist/core/analytics/events.mjs.map +1 -1
  16. package/dist/core/analytics/index.d.mts +1 -1
  17. package/dist/core/analytics/types.d.mts +152 -16
  18. package/dist/core/cart/actions.d.mts +47 -3
  19. package/dist/core/cart/actions.mjs +29 -0
  20. package/dist/core/cart/actions.mjs.map +1 -1
  21. package/dist/core/cart/attach-quantity-input.d.mts +26 -0
  22. package/dist/core/cart/attach-quantity-input.mjs +26 -0
  23. package/dist/core/cart/attach-quantity-input.mjs.map +1 -1
  24. package/dist/core/cart/cart.d.mts +106 -6
  25. package/dist/core/cart/cart.mjs +57 -6
  26. package/dist/core/cart/cart.mjs.map +1 -1
  27. package/dist/core/cart/cookie.d.mts +15 -0
  28. package/dist/core/cart/cookie.mjs +15 -0
  29. package/dist/core/cart/cookie.mjs.map +1 -1
  30. package/dist/core/cart/form.d.mts +61 -0
  31. package/dist/core/cart/form.mjs +18 -0
  32. package/dist/core/cart/form.mjs.map +1 -1
  33. package/dist/core/cart/get-cart.d.mts +43 -2
  34. package/dist/core/cart/get-cart.mjs +33 -0
  35. package/dist/core/cart/get-cart.mjs.map +1 -1
  36. package/dist/core/cart/index.d.mts +1 -1
  37. package/dist/core/cart/quantity.d.mts +17 -0
  38. package/dist/core/cart/quantity.mjs +17 -0
  39. package/dist/core/cart/quantity.mjs.map +1 -1
  40. package/dist/core/cart/queries.d.mts +402 -369
  41. package/dist/core/cart/queries.mjs +2 -1
  42. package/dist/core/cart/queries.mjs.map +1 -1
  43. package/dist/core/cart/server-handlers.d.mts +69 -0
  44. package/dist/core/cart/server-handlers.mjs +2 -10
  45. package/dist/core/cart/server-handlers.mjs.map +1 -1
  46. package/dist/core/cart/state.d.mts +133 -0
  47. package/dist/core/cart/state.mjs +8 -0
  48. package/dist/core/cart/state.mjs.map +1 -1
  49. package/dist/core/collection/collection.d.mts +18 -2
  50. package/dist/core/collection/collection.mjs +11 -0
  51. package/dist/core/collection/collection.mjs.map +1 -1
  52. package/dist/core/collection/index.d.mts +1 -1
  53. package/dist/core/collection/reconciler.d.mts +13 -4
  54. package/dist/core/collection/reconciler.mjs.map +1 -1
  55. package/dist/core/collection/state.d.mts +7 -2
  56. package/dist/core/collection/state.mjs.map +1 -1
  57. package/dist/core/collection/url.d.mts +3 -0
  58. package/dist/core/collection/url.mjs.map +1 -1
  59. package/dist/core/constants.d.mts +1 -1
  60. package/dist/core/constants.mjs +2 -2
  61. package/dist/core/constants.mjs.map +1 -1
  62. package/dist/core/headers.mjs +2 -2
  63. package/dist/core/headers.mjs.map +1 -1
  64. package/dist/core/index.d.mts +5 -5
  65. package/dist/core/logging/logging.mjs.map +1 -1
  66. package/dist/core/money/format.mjs +6 -3
  67. package/dist/core/money/format.mjs.map +1 -1
  68. package/dist/core/observable.mjs +0 -3
  69. package/dist/core/observable.mjs.map +1 -1
  70. package/dist/core/predictive-search/form.d.mts +1 -0
  71. package/dist/core/predictive-search/form.mjs +1 -0
  72. package/dist/core/predictive-search/form.mjs.map +1 -1
  73. package/dist/core/predictive-search/index.d.mts +1 -1
  74. package/dist/core/predictive-search/queries.d.mts +40 -4
  75. package/dist/core/predictive-search/queries.mjs +7 -1
  76. package/dist/core/predictive-search/queries.mjs.map +1 -1
  77. package/dist/core/predictive-search/search.d.mts +43 -10
  78. package/dist/core/predictive-search/search.mjs +34 -0
  79. package/dist/core/predictive-search/search.mjs.map +1 -1
  80. package/dist/core/predictive-search/server-handlers.d.mts +31 -5
  81. package/dist/core/predictive-search/server-handlers.mjs +5 -3
  82. package/dist/core/predictive-search/server-handlers.mjs.map +1 -1
  83. package/dist/core/predictive-search/store.d.mts +3 -1
  84. package/dist/core/predictive-search/store.mjs.map +1 -1
  85. package/dist/core/predictive-search/url.d.mts +50 -8
  86. package/dist/core/predictive-search/url.mjs +9 -0
  87. package/dist/core/predictive-search/url.mjs.map +1 -1
  88. package/dist/core/product/form.d.mts +21 -2
  89. package/dist/core/product/form.mjs +13 -0
  90. package/dist/core/product/form.mjs.map +1 -1
  91. package/dist/core/product/index.d.mts +1 -1
  92. package/dist/core/product/options.d.mts +2 -2
  93. package/dist/core/product/options.mjs +2 -2
  94. package/dist/core/product/options.mjs.map +1 -1
  95. package/dist/core/product/product-form.d.mts +33 -1
  96. package/dist/core/product/product-form.mjs +30 -0
  97. package/dist/core/product/product-form.mjs.map +1 -1
  98. package/dist/core/product/state.d.mts +41 -0
  99. package/dist/core/product/url.d.mts +6 -0
  100. package/dist/core/product/url.mjs.map +1 -1
  101. package/dist/core/request-context.d.mts +8 -5
  102. package/dist/core/request-context.mjs +17 -39
  103. package/dist/core/request-context.mjs.map +1 -1
  104. package/dist/core/request-routing/handle-shopify-routes.mjs +4 -1
  105. package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  106. package/dist/core/request-routing/interceptors/ajax-api.mjs +1 -1
  107. package/dist/core/request-routing/interceptors/api-proxy.mjs +1 -1
  108. package/dist/core/request-routing/interceptors/checkout.mjs +22 -6
  109. package/dist/core/request-routing/interceptors/checkout.mjs.map +1 -1
  110. package/dist/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
  111. package/dist/core/request-routing/interceptors/proxy.mjs +36 -10
  112. package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
  113. package/dist/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
  114. package/dist/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
  115. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
  116. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
  117. package/dist/core/request-routing/interceptors/ucp-mcp-proxy.mjs +35 -0
  118. package/dist/core/request-routing/interceptors/ucp-mcp-proxy.mjs.map +1 -0
  119. package/dist/core/request-routing/interceptors/well-known.mjs +1 -1
  120. package/dist/core/shopify-scripts/analytics.mjs +2 -2
  121. package/dist/core/shopify-scripts/analytics.mjs.map +1 -1
  122. package/dist/core/shopify-scripts/constants.mjs +4 -2
  123. package/dist/core/shopify-scripts/constants.mjs.map +1 -1
  124. package/dist/core/shopify-scripts/global.mjs +9 -4
  125. package/dist/core/shopify-scripts/global.mjs.map +1 -1
  126. package/dist/core/shopify-scripts/index.d.mts +1 -0
  127. package/dist/core/shopify-scripts/index.mjs +14 -12
  128. package/dist/core/shopify-scripts/index.mjs.map +1 -1
  129. package/dist/core/shopify-scripts/initialize.d.mts +1 -0
  130. package/dist/core/shopify-scripts/initialize.mjs +10 -3
  131. package/dist/core/shopify-scripts/initialize.mjs.map +1 -1
  132. package/dist/core/shopify-scripts/types.d.mts +6 -0
  133. package/dist/core/url.mjs +21 -2
  134. package/dist/core/url.mjs.map +1 -1
  135. package/dist/customer-account/client.d.mts +128 -4
  136. package/dist/customer-account/client.mjs +27 -1
  137. package/dist/customer-account/client.mjs.map +1 -1
  138. package/dist/customer-account/errors.d.mts +46 -0
  139. package/dist/customer-account/errors.mjs +46 -0
  140. package/dist/customer-account/errors.mjs.map +1 -1
  141. package/dist/customer-account/graphql.d.mts +68 -0
  142. package/dist/customer-account/graphql.mjs +47 -0
  143. package/dist/customer-account/graphql.mjs.map +1 -1
  144. package/dist/customer-account/session.d.mts +114 -23
  145. package/dist/customer-account/session.mjs +27 -12
  146. package/dist/customer-account/session.mjs.map +1 -1
  147. package/dist/customer-account/type-resolver.d.mts +23 -0
  148. package/dist/customer-account-api-types.d.ts +570 -374
  149. package/dist/customer-account.schema.json +2981 -1527
  150. package/dist/development/client/client.mjs +2 -2
  151. package/dist/development/client/errors.d.mts +19 -19
  152. package/dist/development/client/errors.mjs +20 -20
  153. package/dist/development/client/errors.mjs.map +1 -1
  154. package/dist/development/client/types.d.mts +91 -10
  155. package/dist/development/core/analytics/cart-tracker.d.mts +14 -0
  156. package/dist/development/core/analytics/cart-tracker.mjs +15 -1
  157. package/dist/development/core/analytics/cart-tracker.mjs.map +1 -1
  158. package/dist/development/core/analytics/custom-consent.mjs +12 -0
  159. package/dist/development/core/analytics/custom-consent.mjs.map +1 -0
  160. package/dist/development/core/analytics/events.d.mts +7 -0
  161. package/dist/development/core/analytics/events.mjs +7 -0
  162. package/dist/development/core/analytics/events.mjs.map +1 -1
  163. package/dist/development/core/analytics/index.d.mts +1 -1
  164. package/dist/development/core/analytics/types.d.mts +152 -16
  165. package/dist/development/core/cart/actions.d.mts +47 -3
  166. package/dist/development/core/cart/actions.mjs +29 -0
  167. package/dist/development/core/cart/actions.mjs.map +1 -1
  168. package/dist/development/core/cart/attach-quantity-input.d.mts +26 -0
  169. package/dist/development/core/cart/attach-quantity-input.mjs +26 -0
  170. package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -1
  171. package/dist/development/core/cart/cart.d.mts +106 -6
  172. package/dist/development/core/cart/cart.mjs +57 -6
  173. package/dist/development/core/cart/cart.mjs.map +1 -1
  174. package/dist/development/core/cart/cookie.d.mts +15 -0
  175. package/dist/development/core/cart/cookie.mjs +15 -0
  176. package/dist/development/core/cart/cookie.mjs.map +1 -1
  177. package/dist/development/core/cart/form.d.mts +61 -0
  178. package/dist/development/core/cart/form.mjs +18 -0
  179. package/dist/development/core/cart/form.mjs.map +1 -1
  180. package/dist/development/core/cart/get-cart.d.mts +43 -2
  181. package/dist/development/core/cart/get-cart.mjs +33 -0
  182. package/dist/development/core/cart/get-cart.mjs.map +1 -1
  183. package/dist/development/core/cart/index.d.mts +1 -1
  184. package/dist/development/core/cart/quantity.d.mts +17 -0
  185. package/dist/development/core/cart/quantity.mjs +17 -0
  186. package/dist/development/core/cart/quantity.mjs.map +1 -1
  187. package/dist/development/core/cart/queries.d.mts +345 -312
  188. package/dist/development/core/cart/queries.mjs +2 -1
  189. package/dist/development/core/cart/queries.mjs.map +1 -1
  190. package/dist/development/core/cart/server-handlers.d.mts +69 -0
  191. package/dist/development/core/cart/server-handlers.mjs +2 -10
  192. package/dist/development/core/cart/server-handlers.mjs.map +1 -1
  193. package/dist/development/core/cart/state.d.mts +133 -0
  194. package/dist/development/core/cart/state.mjs +8 -0
  195. package/dist/development/core/cart/state.mjs.map +1 -1
  196. package/dist/development/core/collection/collection.d.mts +18 -2
  197. package/dist/development/core/collection/collection.mjs +11 -0
  198. package/dist/development/core/collection/collection.mjs.map +1 -1
  199. package/dist/development/core/collection/index.d.mts +1 -1
  200. package/dist/development/core/collection/reconciler.d.mts +13 -4
  201. package/dist/development/core/collection/reconciler.mjs.map +1 -1
  202. package/dist/development/core/collection/state.d.mts +7 -2
  203. package/dist/development/core/collection/state.mjs.map +1 -1
  204. package/dist/development/core/collection/url.d.mts +3 -0
  205. package/dist/development/core/collection/url.mjs.map +1 -1
  206. package/dist/development/core/constants.mjs +1 -1
  207. package/dist/development/core/constants.mjs.map +1 -1
  208. package/dist/development/core/development.d.mts +5 -5
  209. package/dist/development/core/headers.mjs +2 -2
  210. package/dist/development/core/headers.mjs.map +1 -1
  211. package/dist/development/core/index.d.mts +4 -4
  212. package/dist/development/core/logging/logging.mjs.map +1 -1
  213. package/dist/development/core/money/format.mjs +6 -3
  214. package/dist/development/core/money/format.mjs.map +1 -1
  215. package/dist/development/core/observable.mjs +0 -3
  216. package/dist/development/core/observable.mjs.map +1 -1
  217. package/dist/development/core/predictive-search/form.d.mts +1 -0
  218. package/dist/development/core/predictive-search/form.mjs +1 -0
  219. package/dist/development/core/predictive-search/form.mjs.map +1 -1
  220. package/dist/development/core/predictive-search/index.d.mts +1 -1
  221. package/dist/development/core/predictive-search/queries.d.mts +38 -2
  222. package/dist/development/core/predictive-search/queries.mjs +7 -1
  223. package/dist/development/core/predictive-search/queries.mjs.map +1 -1
  224. package/dist/development/core/predictive-search/search.d.mts +43 -10
  225. package/dist/development/core/predictive-search/search.mjs +34 -0
  226. package/dist/development/core/predictive-search/search.mjs.map +1 -1
  227. package/dist/development/core/predictive-search/server-handlers.d.mts +31 -5
  228. package/dist/development/core/predictive-search/server-handlers.mjs +5 -3
  229. package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -1
  230. package/dist/development/core/predictive-search/store.d.mts +3 -1
  231. package/dist/development/core/predictive-search/store.mjs.map +1 -1
  232. package/dist/development/core/predictive-search/url.d.mts +50 -8
  233. package/dist/development/core/predictive-search/url.mjs +9 -0
  234. package/dist/development/core/predictive-search/url.mjs.map +1 -1
  235. package/dist/development/core/product/form.d.mts +21 -2
  236. package/dist/development/core/product/form.mjs +13 -0
  237. package/dist/development/core/product/form.mjs.map +1 -1
  238. package/dist/development/core/product/index.d.mts +1 -1
  239. package/dist/development/core/product/options.d.mts +2 -2
  240. package/dist/development/core/product/options.mjs +2 -2
  241. package/dist/development/core/product/options.mjs.map +1 -1
  242. package/dist/development/core/product/product-form.d.mts +33 -1
  243. package/dist/development/core/product/product-form.mjs +30 -0
  244. package/dist/development/core/product/product-form.mjs.map +1 -1
  245. package/dist/development/core/product/state.d.mts +41 -0
  246. package/dist/development/core/product/url.d.mts +6 -0
  247. package/dist/development/core/product/url.mjs.map +1 -1
  248. package/dist/development/core/request-context.d.mts +8 -5
  249. package/dist/development/core/request-context.mjs +17 -39
  250. package/dist/development/core/request-context.mjs.map +1 -1
  251. package/dist/development/core/request-routing/handle-shopify-routes.mjs +4 -1
  252. package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  253. package/dist/development/core/request-routing/interceptors/ajax-api.mjs +1 -1
  254. package/dist/development/core/request-routing/interceptors/api-proxy.mjs +1 -1
  255. package/dist/development/core/request-routing/interceptors/checkout.mjs +22 -6
  256. package/dist/development/core/request-routing/interceptors/checkout.mjs.map +1 -1
  257. package/dist/development/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
  258. package/dist/development/core/request-routing/interceptors/proxy.mjs +36 -10
  259. package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
  260. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
  261. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
  262. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
  263. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
  264. package/dist/development/core/request-routing/interceptors/ucp-mcp-proxy.mjs +35 -0
  265. package/dist/development/core/request-routing/interceptors/ucp-mcp-proxy.mjs.map +1 -0
  266. package/dist/development/core/request-routing/interceptors/well-known.mjs +1 -1
  267. package/dist/development/core/shopify-scripts/analytics.mjs +2 -2
  268. package/dist/development/core/shopify-scripts/analytics.mjs.map +1 -1
  269. package/dist/development/core/shopify-scripts/constants.mjs +4 -2
  270. package/dist/development/core/shopify-scripts/constants.mjs.map +1 -1
  271. package/dist/development/core/shopify-scripts/global.mjs +9 -4
  272. package/dist/development/core/shopify-scripts/global.mjs.map +1 -1
  273. package/dist/development/core/shopify-scripts/index.d.mts +1 -0
  274. package/dist/development/core/shopify-scripts/index.mjs +14 -12
  275. package/dist/development/core/shopify-scripts/index.mjs.map +1 -1
  276. package/dist/development/core/shopify-scripts/initialize.d.mts +1 -0
  277. package/dist/development/core/shopify-scripts/initialize.mjs +10 -3
  278. package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -1
  279. package/dist/development/core/shopify-scripts/types.d.mts +6 -0
  280. package/dist/development/core/url.mjs +21 -2
  281. package/dist/development/core/url.mjs.map +1 -1
  282. package/dist/development/customer-account/session.d.mts +69 -13
  283. package/dist/development/customer-account/session.mjs +4 -0
  284. package/dist/development/customer-account/session.mjs.map +1 -1
  285. package/dist/development/globals.d.mts +6 -5
  286. package/dist/development/graphql/generated/graphql-env.d.mts +459 -16
  287. package/dist/development/graphql/generated/storefront-api-types.d.mts +1 -1
  288. package/dist/development/graphql/graphql.d.mts +33 -0
  289. package/dist/development/graphql/graphql.mjs +30 -0
  290. package/dist/development/graphql/graphql.mjs.map +1 -1
  291. package/dist/development/graphql/type-resolver.d.mts +14 -0
  292. package/dist/development/react/cart.d.mts +85 -14
  293. package/dist/development/react/cart.mjs +123 -3
  294. package/dist/development/react/cart.mjs.map +1 -1
  295. package/dist/development/react/collection.d.mts +2 -4
  296. package/dist/development/react/collection.mjs.map +1 -1
  297. package/dist/development/react/index.d.mts +8 -6
  298. package/dist/development/react/index.mjs +2 -2
  299. package/dist/development/react/predictive-search.d.mts +68 -7
  300. package/dist/development/react/predictive-search.mjs +54 -12
  301. package/dist/development/react/predictive-search.mjs.map +1 -1
  302. package/dist/development/react/product.d.mts +1 -0
  303. package/dist/development/react/product.mjs +5 -2
  304. package/dist/development/react/product.mjs.map +1 -1
  305. package/dist/development/react/shopify-scripts.mjs +1 -0
  306. package/dist/development/react/shopify-scripts.mjs.map +1 -1
  307. package/dist/development/vue/cart.d.mts +85 -6
  308. package/dist/development/vue/cart.mjs +95 -0
  309. package/dist/development/vue/cart.mjs.map +1 -1
  310. package/dist/development/vue/collection.d.mts +83 -5
  311. package/dist/development/vue/collection.mjs +52 -0
  312. package/dist/development/vue/collection.mjs.map +1 -1
  313. package/dist/development/vue/index.d.mts +6 -4
  314. package/dist/development/vue/predictive-search.d.mts +53 -6
  315. package/dist/development/vue/predictive-search.mjs +33 -0
  316. package/dist/development/vue/predictive-search.mjs.map +1 -1
  317. package/dist/development/vue/product.d.mts +6 -2
  318. package/dist/development/vue/product.mjs.map +1 -1
  319. package/dist/development/vue/shopify-scripts.d.mts +30 -3
  320. package/dist/development/vue/shopify-scripts.mjs +6 -0
  321. package/dist/development/vue/shopify-scripts.mjs.map +1 -1
  322. package/dist/globals.d.mts +6 -5
  323. package/dist/graphql/generated/customer-account-graphql-env.d.mts +1717 -1090
  324. package/dist/graphql/generated/graphql-env.d.mts +459 -16
  325. package/dist/graphql/generated/storefront-api-types.d.mts +1 -1
  326. package/dist/graphql/graphql.d.mts +33 -0
  327. package/dist/graphql/graphql.mjs +30 -0
  328. package/dist/graphql/graphql.mjs.map +1 -1
  329. package/dist/graphql/type-resolver.d.mts +14 -0
  330. package/dist/react/cart.d.mts +85 -14
  331. package/dist/react/cart.mjs +123 -3
  332. package/dist/react/cart.mjs.map +1 -1
  333. package/dist/react/collection.d.mts +2 -4
  334. package/dist/react/collection.mjs.map +1 -1
  335. package/dist/react/index.d.mts +8 -6
  336. package/dist/react/index.mjs +2 -2
  337. package/dist/react/predictive-search.d.mts +68 -7
  338. package/dist/react/predictive-search.mjs +54 -12
  339. package/dist/react/predictive-search.mjs.map +1 -1
  340. package/dist/react/product.d.mts +1 -0
  341. package/dist/react/product.mjs +5 -2
  342. package/dist/react/product.mjs.map +1 -1
  343. package/dist/react/shopify-scripts.mjs +1 -0
  344. package/dist/react/shopify-scripts.mjs.map +1 -1
  345. package/dist/storefront-api-types.d.ts +165 -20
  346. package/dist/storefront.schema.json +782 -57
  347. package/dist/vite/customer-account.mjs +191 -0
  348. package/dist/vite/customer-account.mjs.map +1 -0
  349. package/dist/vite/local-https.mjs +13 -29
  350. package/dist/vite/local-https.mjs.map +1 -1
  351. package/dist/vite/mkcert.mjs +1 -1
  352. package/dist/vue/cart.d.mts +85 -6
  353. package/dist/vue/cart.mjs +95 -0
  354. package/dist/vue/cart.mjs.map +1 -1
  355. package/dist/vue/collection.d.mts +83 -5
  356. package/dist/vue/collection.mjs +52 -0
  357. package/dist/vue/collection.mjs.map +1 -1
  358. package/dist/vue/index.d.mts +6 -4
  359. package/dist/vue/predictive-search.d.mts +53 -6
  360. package/dist/vue/predictive-search.mjs +33 -0
  361. package/dist/vue/predictive-search.mjs.map +1 -1
  362. package/dist/vue/product.d.mts +6 -2
  363. package/dist/vue/product.mjs.map +1 -1
  364. package/dist/vue/shopify-scripts.d.mts +31 -4
  365. package/dist/vue/shopify-scripts.mjs +6 -0
  366. package/dist/vue/shopify-scripts.mjs.map +1 -1
  367. package/package.json +5 -2
  368. package/skills/hydrogen-analytics/SKILL.md +6 -5
  369. package/skills/hydrogen-analytics/references/custom-consent.md +52 -0
  370. package/skills/hydrogen-analytics/references/react.md +1 -1
  371. package/skills/hydrogen-cart-metafields/SKILL.md +133 -0
  372. package/skills/hydrogen-cart-ui/SKILL.md +2 -1
  373. package/skills/hydrogen-customer-account/SKILL.md +3 -3
  374. package/skills/hydrogen-local-https/SKILL.md +22 -7
  375. package/skills/hydrogen-request-handlers/SKILL.md +5 -5
  376. package/skills/hydrogen-request-handlers/references/nuxt.md +0 -6
  377. package/skills/hydrogen-setup/references/analytics.md +24 -23
  378. package/skills/hydrogen-setup/steps/1-inspect-app.md +1 -1
  379. package/skills/hydrogen-setup/steps/11-verify.md +1 -0
  380. package/skills/hydrogen-setup/steps/2-scaffold.md +22 -1
  381. package/skills/hydrogen-smoke-test/SKILL.md +2 -2
  382. package/skills/hydrogen-storefront-client/SKILL.md +4 -2
  383. package/skills/hydrogen-variant-form/SKILL.md +10 -4
  384. package/ts-plugin/package.json +5 -0
  385. package/dist/core/shopify-scripts/consent.mjs +0 -17
  386. package/dist/core/shopify-scripts/consent.mjs.map +0 -1
  387. package/dist/core/shopify-scripts/deprecated-cookies.mjs +0 -97
  388. package/dist/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
  389. package/dist/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
  390. package/dist/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
  391. package/dist/core/shopify-scripts/utils/tracking-values.mjs +0 -65
  392. package/dist/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
  393. package/dist/core/shopify-scripts/utils/uuid.mjs +0 -17
  394. package/dist/core/shopify-scripts/utils/uuid.mjs.map +0 -1
  395. package/dist/development/core/shopify-scripts/consent.mjs +0 -17
  396. package/dist/development/core/shopify-scripts/consent.mjs.map +0 -1
  397. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs +0 -97
  398. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
  399. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
  400. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
  401. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs +0 -65
  402. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
  403. package/dist/development/core/shopify-scripts/utils/uuid.mjs +0 -17
  404. package/dist/development/core/shopify-scripts/utils/uuid.mjs.map +0 -1
@@ -1 +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 filters: ProductFilter[];\n sortKey: ProductCollectionSortKeys | undefined;\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. */\nexport function 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 * 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,5 +1,5 @@
1
1
  //#region src/core/constants.ts
2
- const STOREFRONT_API_VERSION = "2026-04";
2
+ const STOREFRONT_API_VERSION = "2026-10";
3
3
  const DEFAULT_TIMEOUT_IN_MS = 3e4;
4
4
 
5
5
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"constants.mjs","names":[],"sources":["../../../src/core/constants.ts"],"sourcesContent":["export const STOREFRONT_API_VERSION = \"2026-04\";\nexport const CUSTOMER_ACCOUNT_API_VERSION = \"2026-04\";\nexport const DEFAULT_TIMEOUT_IN_MS = 30_000;\n"],"mappings":";AAAA,MAAa,yBAAyB;AAEtC,MAAa,wBAAwB"}
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;AAEtC,MAAa,wBAAwB"}
@@ -15,12 +15,12 @@ import { createFetchWithCache } from "./cache/fetch-with-cache.mjs";
15
15
  import { StorefrontApiError, StorefrontTimeoutError } from "../client/errors.mjs";
16
16
  import { ProductFilter } from "../vendor/standard-events.mjs";
17
17
  import { Attribute, CartCost, CartData, CartErrorGroup, CartErrorState, CartLine, CartLineConnection, CartLineCost, CartLineMerchandise, CartNetworkEntry, CartPending, CartState, CartUserError, CartWarning, DiscountCode, EMPTY_CART_DATA, EMPTY_CART_STATE, createEmptyCartErrors, createEmptyErrorGroup } from "./cart/state.mjs";
18
- import { CartNetworkError, CartStore, CreateCartStoreOptions, STANDARD_ACTION_TIMEOUT_IN_MS, configureCartEndpoint, createCartStore } from "./cart/cart.mjs";
18
+ import { CartActions, CartNetworkError, CartStore, CreateCartStoreOptions, STANDARD_ACTION_TIMEOUT_IN_MS, configureCartEndpoint, createCartStore } from "./cart/cart.mjs";
19
19
  import { trackCartAnalytics } from "./analytics/cart-tracker.mjs";
20
20
  import { AnalyticsEvent, AnalyticsEventName } from "./analytics/events.mjs";
21
21
  import { ShopifyGlobal } from "../globals.mjs";
22
22
  import { ShopifyRoutesOptions, ShopifyScriptTagDescriptor, ShopifyScriptTagDescriptors, ShopifyScriptTagsOptions, ShopifyScriptsAnalyticsConfig, ShopifyScriptsI18n, ShopifyScriptsOptions, ShopifyScriptsShop } from "./shopify-scripts/types.mjs";
23
- import { AnalyticsCart, AnalyticsCartLine, AnalyticsEventMap, CartLineUpdatePayload, CartUpdatePayload, CartViewPayload, CollectionViewPayload, ConsentConfig, EventPayloads, OtherData, PageViewPayload, PayloadFor, ProductPayload, ProductViewPayload, SearchViewPayload, ShopAnalytics, StorefrontAnalytics, StorefrontAnalyticsConfig, StorefrontAnalyticsDestination, StorefrontAnalyticsDestinationSetupContext } from "./analytics/types.mjs";
23
+ import { AnalyticsCart, AnalyticsCartLine, AnalyticsEventMap, AnalyticsTrackingValues, CartLineUpdatePayload, CartUpdatePayload, CartViewPayload, CollectionViewPayload, ConsentConfig, ConsentPreferences, ConsentSetup, OtherData, PageViewPayload, PayloadFor, ProductPayload, ProductViewPayload, SearchViewPayload, ShopAnalytics, StorefrontAnalytics, StorefrontAnalyticsConfig, StorefrontAnalyticsDestination, StorefrontAnalyticsDestinationEventContext, StorefrontAnalyticsDestinationSetupContext } from "./analytics/types.mjs";
24
24
  import { CartFormRegister, QuantityInputAttributes, SetButtonAttributes, createCartFormRegister } from "./cart/form.mjs";
25
25
  import { attachQuantityInput } from "./cart/attach-quantity-input.mjs";
26
26
  import { CartAction, CartAttributeInput, CartLineAddInput, CartLineUpdateInput, parseCartRequest } from "./cart/actions.mjs";
@@ -32,7 +32,7 @@ import { DEFAULT_MINIMUM_QUANTITY, NO_QUANTITY_LIMIT, sanitizeQuantity } from ".
32
32
  import { initializeShopifyScripts } from "./shopify-scripts/initialize.mjs";
33
33
  import { getShopifyScriptTags, renderShopifyScriptTags } from "./shopify-scripts/index.mjs";
34
34
  import { AvailableFilter, AvailableFilterValue, CollectionState, FilterPresentation, FilterType, createInitialCollectionState } from "./collection/state.mjs";
35
- import { CollectionData, CollectionStore, CreateCollectionStoreOptions, createCollectionStore } from "./collection/collection.mjs";
35
+ import { CollectionActions, CollectionData, CollectionStore, CreateCollectionStoreOptions, createCollectionStore } from "./collection/collection.mjs";
36
36
  import { CollectionReconciler, ReconcilerCallbacks, createCollectionReconciler } from "./collection/reconciler.mjs";
37
37
  import { CollectionParams, filterEquals, getFilterRemovalUrl, getSortByValue, isFilterInputActive, normalizeCollectionSearch, parseCollectionParams, parseSortByValue, serializeCollectionParams } from "./collection/url.mjs";
38
38
  import { ProductInput, ProductOptionInput, ProductOptionValueInput, ProductVariantFrom, ProductVariantInput, SelectedOption, VariantOptionState, VariantOptionValueState } from "./product/state.mjs";
@@ -45,7 +45,7 @@ import { PredictiveSearchFormAttributes, PredictiveSearchFormRegister, Predictiv
45
45
  import { CreatePredictiveSearchQueriesOptions, PredictiveSearchFragments, makePredictiveSearchQueries } from "./predictive-search/queries.mjs";
46
46
  import { PredictiveSearchData, QueryPredictiveSearchOptions, queryPredictiveSearch } from "./predictive-search/search.mjs";
47
47
  import { CreatePredictiveSearchServerHandlersOptions, createPredictiveSearchServerHandlers } from "./predictive-search/server-handlers.mjs";
48
- import { CreatePredictiveSearchStoreOptions, PredictiveSearchState, PredictiveSearchStatus, PredictiveSearchStore, createPredictiveSearchStore } from "./predictive-search/store.mjs";
48
+ import { CreatePredictiveSearchStoreOptions, PredictiveSearchActions, PredictiveSearchState, PredictiveSearchStatus, PredictiveSearchStore, createPredictiveSearchStore } from "./predictive-search/store.mjs";
49
49
  import { PredictiveSearchArticleItem, PredictiveSearchCollectionItem, PredictiveSearchItem, PredictiveSearchItemUrlOptions, PredictiveSearchPageItem, PredictiveSearchProductItem, PredictiveSearchQueryItem, PredictiveSearchQueryItemUrlOptions, PredictiveSearchResourceItem, getPredictiveSearchItemUrl, getSearchResultUrl } from "./predictive-search/url.mjs";
50
50
  import { FormatMoneyOptions, FormattedMoney, FormattedMoneyRange, MoneyV2 } from "./money/types.mjs";
51
51
  import { formatMoney } from "./money/format.mjs";
@@ -53,4 +53,4 @@ import { flattenConnection } from "./analytics/utils/flatten-connection.mjs";
53
53
  import { HydrogenLogger, LogContext, LogLevel } from "./logging/types.mjs";
54
54
  import { ConfigureLoggingOptions, configureLogging } from "./logging/logging.mjs";
55
55
  import { handleShopifyRoutesDev } from "./request-routing/handle-shopify-routes.development.mjs";
56
- export { AnalyticsCart, AnalyticsCartLine, AnalyticsEvent, AnalyticsEventMap, AnalyticsEventName, AnyStorefrontQueryString, Attribute, AvailableFilter, AvailableFilterValue, Cache, CacheInstance, CacheOptions, CachingStrategy, CallableRouteHandler, CartAction, CartAttributeInput, CartCost, CartData, CartDataFromHandlers, CartDataFromQuery, CartError, CartErrorCode, CartErrorGroup, CartErrorState, CartFormRegister, CartGetData, CartGetHandler, CartGetResult, CartLine, CartLineAddInput, CartLineConnection, CartLineCost, CartLineMerchandise, CartLineUpdateInput, CartLineUpdatePayload, CartNetworkEntry, CartNetworkError, CartPending, CartPostHandler, CartPostResult, CartResult, CartServerHandlers, CartServerHandlersWithCustomerSession, CartState, CartStore, CartUpdatePayload, CartUserError, CartViewPayload, CartWarning, ClientType, CollectionData, CollectionParams, CollectionReconciler, CollectionState, CollectionStore, CollectionViewPayload, ConfigureLoggingOptions, ConsentConfig, CreateCartServerHandlersOptions, CreateCartStoreOptions, CreateCollectionStoreOptions, CreatePredictiveSearchQueriesOptions, CreatePredictiveSearchServerHandlersOptions, CreatePredictiveSearchStoreOptions, CreateProductFormStoreOptions, CreateStorefrontClientArgs, DEFAULT_MINIMUM_QUANTITY, DiscountCode, EMPTY_CART_DATA, EMPTY_CART_STATE, EventPayloads, FilterPresentation, FilterType, FormatMoneyOptions, FormattedMoney, FormattedMoneyRange, GqlRestParam, GraphQLFormattedError, HydrogenLogger, I18nConfig, InferResult, InferVariables, LogContext, LogLevel, MoneyV2, NO_QUANTITY_LIMIT, OtherData, PageViewPayload, PayloadFor, PredictiveSearchArticleItem, PredictiveSearchCollectionItem, PredictiveSearchData, PredictiveSearchFormAttributes, PredictiveSearchFormRegister, PredictiveSearchFragments, PredictiveSearchItem, PredictiveSearchItemUrlOptions, PredictiveSearchPageItem, PredictiveSearchProductItem, PredictiveSearchQueryInputAttributes, PredictiveSearchQueryItem, PredictiveSearchQueryItemUrlOptions, PredictiveSearchResourceItem, PredictiveSearchState, PredictiveSearchStatus, PredictiveSearchStore, PrivateClientOptions, PrivateNoBuyerContextClientOptions, PrivateNoBuyerContextStorefrontClient, PrivateStorefrontClient, ProductAddToCartProps, ProductCollectionSortKeys, ProductFilter, ProductFormErrors, ProductFormOptions, ProductFormRegister, ProductFormStore, ProductFormStoreState, ProductInput, ProductMerchandiseIdProps, ProductOptionInput, ProductOptionValueInput, ProductOptionValueProps, ProductPayload, ProductQuantityDefaultProps, ProductQuantityProps, ProductSelectionLinkStyle, ProductVariantFrom, ProductVariantInput, ProductViewPayload, PublicClientOptions, PublicStorefrontClient, QuantityInputAttributes, QueryPredictiveSearchOptions, ReconcilerCallbacks, RedirectOptions, RequestScopedPrivateStorefrontClient, SHOP_PAY_BUTTON_TAG_NAME, STANDARD_ACTION_TIMEOUT_IN_MS, SearchViewPayload, SelectedOption, SetButtonAttributes, ShopAnalytics, ShopPayButtonOptions, ShopifyGlobal, ShopifyRedirectStatus, ShopifyRequestContext, ShopifyRequestContextWithBuyerIp, ShopifyRouteError, ShopifyRouteErrorResult, ShopifyRouteHandler, ShopifyRouteHandlerContext, ShopifyRouteHandlerGroup, ShopifyRouteHandlerResult, ShopifyRouteJsonResult, ShopifyRouteRedirectResult, ShopifyRouteTemplates, ShopifyRoutesOptions, ShopifyScriptTagDescriptor, ShopifyScriptTagDescriptors, ShopifyScriptTagsOptions, ShopifyScriptsAnalyticsConfig, ShopifyScriptsI18n, ShopifyScriptsOptions, ShopifyScriptsShop, StorefrontAnalytics, StorefrontAnalyticsConfig, StorefrontAnalyticsDestination, StorefrontAnalyticsDestinationSetupContext, StorefrontApi, StorefrontApiError, StorefrontClient, StorefrontClientOptions, StorefrontGraphql, StorefrontGraphqlOptions, StorefrontGraphqlResult, StorefrontQueryString, StorefrontTimeoutError, ValidProductSelectionResult, VariantOptionState, VariantOptionValueState, VariantSelectionResult, attachQuantityInput, buildProductSelectionSearchParams, canAddToCart, cartQueries, configureCartEndpoint, configureLogging, createCartCookie, createCartFormRegister, createCartServerHandlers, createCartStore, createCollectionReconciler, createCollectionStore, createEmptyCartErrors, createEmptyErrorGroup, createFetchWithCache, createInitialCollectionState, createPredictiveSearchFormRegister, createPredictiveSearchServerHandlers, createPredictiveSearchStore, createProductFormRegister, createProductFormStore, createRunWithCache, createShopPayButton, createShopifyRequestContext, createShopifyRouteHandler, createShopifyRouteTemplates, createStorefrontClient, defineShopPayButton, filterEquals, findCartLineByMerchandiseId, flattenConnection, formatMoney, getCart, getCartId, getFilterRemovalUrl, getPredictiveSearchFormAttributes, getPredictiveSearchItemUrl, getSearchResultUrl, getSelectedProductOptions, getSelectedVariant, getShopPayButtonUrl, getShopifyScriptTags, getSortByValue, gql, handleShopifyRedirects, handleShopifyRoutesDev as handleShopifyRoutes, initializeShopifyScripts, isFilterInputActive, makePredictiveSearchQueries, normalizeCollectionSearch, parseCartRequest, parseCollectionParams, parseSortByValue, queryPredictiveSearch, readPredictiveSearchFormTerm, renderShopPayButton, renderShopifyScriptTags, sanitizeQuantity, serializeCollectionParams, trackCartAnalytics };
56
+ export { AnalyticsCart, AnalyticsCartLine, AnalyticsEvent, AnalyticsEventMap, AnalyticsEventName, AnalyticsTrackingValues, AnyStorefrontQueryString, Attribute, AvailableFilter, AvailableFilterValue, Cache, CacheInstance, CacheOptions, CachingStrategy, CallableRouteHandler, CartAction, CartActions, CartAttributeInput, CartCost, CartData, CartDataFromHandlers, CartDataFromQuery, CartError, CartErrorCode, CartErrorGroup, CartErrorState, CartFormRegister, CartGetData, CartGetHandler, CartGetResult, CartLine, CartLineAddInput, CartLineConnection, CartLineCost, CartLineMerchandise, CartLineUpdateInput, CartLineUpdatePayload, CartNetworkEntry, CartNetworkError, CartPending, CartPostHandler, CartPostResult, CartResult, CartServerHandlers, CartServerHandlersWithCustomerSession, CartState, CartStore, CartUpdatePayload, CartUserError, CartViewPayload, CartWarning, ClientType, CollectionActions, CollectionData, CollectionParams, CollectionReconciler, CollectionState, CollectionStore, CollectionViewPayload, ConfigureLoggingOptions, ConsentConfig, ConsentPreferences, ConsentSetup, CreateCartServerHandlersOptions, CreateCartStoreOptions, CreateCollectionStoreOptions, CreatePredictiveSearchQueriesOptions, CreatePredictiveSearchServerHandlersOptions, CreatePredictiveSearchStoreOptions, CreateProductFormStoreOptions, CreateStorefrontClientArgs, DEFAULT_MINIMUM_QUANTITY, DiscountCode, EMPTY_CART_DATA, EMPTY_CART_STATE, FilterPresentation, FilterType, FormatMoneyOptions, FormattedMoney, FormattedMoneyRange, GqlRestParam, GraphQLFormattedError, HydrogenLogger, I18nConfig, InferResult, InferVariables, LogContext, LogLevel, MoneyV2, NO_QUANTITY_LIMIT, OtherData, PageViewPayload, PayloadFor, PredictiveSearchActions, PredictiveSearchArticleItem, PredictiveSearchCollectionItem, PredictiveSearchData, PredictiveSearchFormAttributes, PredictiveSearchFormRegister, PredictiveSearchFragments, PredictiveSearchItem, PredictiveSearchItemUrlOptions, PredictiveSearchPageItem, PredictiveSearchProductItem, PredictiveSearchQueryInputAttributes, PredictiveSearchQueryItem, PredictiveSearchQueryItemUrlOptions, PredictiveSearchResourceItem, PredictiveSearchState, PredictiveSearchStatus, PredictiveSearchStore, PrivateClientOptions, PrivateNoBuyerContextClientOptions, PrivateNoBuyerContextStorefrontClient, PrivateStorefrontClient, ProductAddToCartProps, ProductCollectionSortKeys, ProductFilter, ProductFormErrors, ProductFormOptions, ProductFormRegister, ProductFormStore, ProductFormStoreState, ProductInput, ProductMerchandiseIdProps, ProductOptionInput, ProductOptionValueInput, ProductOptionValueProps, ProductPayload, ProductQuantityDefaultProps, ProductQuantityProps, ProductSelectionLinkStyle, ProductVariantFrom, ProductVariantInput, ProductViewPayload, PublicClientOptions, PublicStorefrontClient, QuantityInputAttributes, QueryPredictiveSearchOptions, ReconcilerCallbacks, RedirectOptions, RequestScopedPrivateStorefrontClient, SHOP_PAY_BUTTON_TAG_NAME, STANDARD_ACTION_TIMEOUT_IN_MS, SearchViewPayload, SelectedOption, SetButtonAttributes, ShopAnalytics, ShopPayButtonOptions, ShopifyGlobal, ShopifyRedirectStatus, ShopifyRequestContext, ShopifyRequestContextWithBuyerIp, ShopifyRouteError, ShopifyRouteErrorResult, ShopifyRouteHandler, ShopifyRouteHandlerContext, ShopifyRouteHandlerGroup, ShopifyRouteHandlerResult, ShopifyRouteJsonResult, ShopifyRouteRedirectResult, ShopifyRouteTemplates, ShopifyRoutesOptions, ShopifyScriptTagDescriptor, ShopifyScriptTagDescriptors, ShopifyScriptTagsOptions, ShopifyScriptsAnalyticsConfig, ShopifyScriptsI18n, ShopifyScriptsOptions, ShopifyScriptsShop, StorefrontAnalytics, StorefrontAnalyticsConfig, StorefrontAnalyticsDestination, StorefrontAnalyticsDestinationEventContext, StorefrontAnalyticsDestinationSetupContext, StorefrontApi, StorefrontApiError, StorefrontClient, StorefrontClientOptions, StorefrontGraphql, StorefrontGraphqlOptions, StorefrontGraphqlResult, StorefrontQueryString, StorefrontTimeoutError, ValidProductSelectionResult, VariantOptionState, VariantOptionValueState, VariantSelectionResult, attachQuantityInput, buildProductSelectionSearchParams, canAddToCart, cartQueries, configureCartEndpoint, configureLogging, createCartCookie, createCartFormRegister, createCartServerHandlers, createCartStore, createCollectionReconciler, createCollectionStore, createEmptyCartErrors, createEmptyErrorGroup, createFetchWithCache, createInitialCollectionState, createPredictiveSearchFormRegister, createPredictiveSearchServerHandlers, createPredictiveSearchStore, createProductFormRegister, createProductFormStore, createRunWithCache, createShopPayButton, createShopifyRequestContext, createShopifyRouteHandler, createShopifyRouteTemplates, createStorefrontClient, defineShopPayButton, filterEquals, findCartLineByMerchandiseId, flattenConnection, formatMoney, getCart, getCartId, getFilterRemovalUrl, getPredictiveSearchFormAttributes, getPredictiveSearchItemUrl, getSearchResultUrl, getSelectedProductOptions, getSelectedVariant, getShopPayButtonUrl, getShopifyScriptTags, getSortByValue, gql, handleShopifyRedirects, handleShopifyRoutesDev as handleShopifyRoutes, initializeShopifyScripts, isFilterInputActive, makePredictiveSearchQueries, normalizeCollectionSearch, parseCartRequest, parseCollectionParams, parseSortByValue, queryPredictiveSearch, readPredictiveSearchFormTerm, renderShopPayButton, renderShopifyScriptTags, sanitizeQuantity, serializeCollectionParams, trackCartAnalytics };
@@ -2,12 +2,12 @@
2
2
  const CACHE_CONTROL_HEADER = "Cache-Control";
3
3
  const CONSENT_MANAGEMENT_HEADER = "Shopify-Storefront-Consent-Management";
4
4
  const HYDROGEN_VERSION_HEADER = "X-Hydrogen-Version";
5
- const SERVER_TIMING_HEADER = "Server-Timing";
6
5
  const SURROGATE_CONTROL_HEADER = "Surrogate-Control";
7
6
  const REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
8
7
  const SDK_VARIANT_HEADER = "X-SDK-Variant";
9
8
  const SDK_VARIANT_SOURCE_HEADER = "X-SDK-Variant-Source";
10
9
  const SDK_VERSION_HEADER = "X-SDK-Version";
10
+ const SEC_GPC_HEADER = "Sec-GPC";
11
11
  const SHOPIFY_STOREFRONT_ORIGIN_HEADER = "Sec-Shopify-Storefront-Origin";
12
12
  const STOREFRONT_ID_HEADER = "Shopify-Storefront-Id";
13
13
  const SHOPIFY_STOREFRONT_S_HEADER = "Shopify-Storefront-S";
@@ -39,5 +39,5 @@ const AJAX_API_REQUEST_HEADER_ALLOWLIST = defineHeaderList(...COMMON_PROXY_HEADE
39
39
  const PROXY_REQUEST_HEADER_DENYLIST = defineHeaderList("cf-connecting-ip", "connection", "content-length", "host", "keep-alive", "proxy-authenticate", "proxy-authorization", "te", "trailer", "transfer-encoding", "upgrade");
40
40
 
41
41
  //#endregion
42
- export { AJAX_API_REQUEST_HEADER_ALLOWLIST, CONSENT_MANAGEMENT_HEADER, HYDROGEN_VERSION_HEADER, MCP_REQUEST_HEADER_ALLOWLIST, PROXY_REQUEST_HEADER_DENYLIST, REQUEST_GROUP_ID_HEADER, SDK_VARIANT_HEADER, SDK_VARIANT_SOURCE_HEADER, SDK_VERSION_HEADER, SERVER_TIMING_HEADER, SFAPI_REQUEST_HEADER_ALLOWLIST, SHOPIFY_STOREFRONT_ORIGIN_HEADER, SHOPIFY_STOREFRONT_S_HEADER, SHOPIFY_STOREFRONT_Y_HEADER, SHOPIFY_UNIQUE_TOKEN_HEADER, SHOPIFY_VISIT_TOKEN_HEADER, STOREFRONT_ACCESS_TOKEN_HEADER, STOREFRONT_BUYER_IP_HEADER, STOREFRONT_ID_HEADER, STOREFRONT_PRIVATE_TOKEN_HEADER, STOREFRONT_URL_HEADER, applyPrivateResponseCacheHeaders, extractHeaders };
42
+ export { AJAX_API_REQUEST_HEADER_ALLOWLIST, CONSENT_MANAGEMENT_HEADER, HYDROGEN_VERSION_HEADER, MCP_REQUEST_HEADER_ALLOWLIST, PROXY_REQUEST_HEADER_DENYLIST, REQUEST_GROUP_ID_HEADER, SDK_VARIANT_HEADER, SDK_VARIANT_SOURCE_HEADER, SDK_VERSION_HEADER, SEC_GPC_HEADER, SFAPI_REQUEST_HEADER_ALLOWLIST, SHOPIFY_STOREFRONT_ORIGIN_HEADER, SHOPIFY_STOREFRONT_S_HEADER, SHOPIFY_STOREFRONT_Y_HEADER, SHOPIFY_UNIQUE_TOKEN_HEADER, SHOPIFY_VISIT_TOKEN_HEADER, STOREFRONT_ACCESS_TOKEN_HEADER, STOREFRONT_BUYER_IP_HEADER, STOREFRONT_ID_HEADER, STOREFRONT_PRIVATE_TOKEN_HEADER, STOREFRONT_URL_HEADER, applyPrivateResponseCacheHeaders, extractHeaders };
43
43
  //# sourceMappingURL=headers.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"headers.mjs","names":[],"sources":["../../../src/core/headers.ts"],"sourcesContent":["export const CACHE_CONTROL_HEADER = \"Cache-Control\";\nexport const CONSENT_MANAGEMENT_HEADER = \"Shopify-Storefront-Consent-Management\";\nexport const HYDROGEN_VERSION_HEADER = \"X-Hydrogen-Version\";\nexport const SERVER_TIMING_HEADER = \"Server-Timing\";\nexport const SURROGATE_CONTROL_HEADER = \"Surrogate-Control\";\nexport const REQUEST_GROUP_ID_HEADER = \"Custom-Storefront-Request-Group-ID\";\nexport const SDK_VARIANT_HEADER = \"X-SDK-Variant\";\nexport const SDK_VARIANT_SOURCE_HEADER = \"X-SDK-Variant-Source\";\nexport const SDK_VERSION_HEADER = \"X-SDK-Version\";\nexport const SHOPIFY_STOREFRONT_ORIGIN_HEADER = \"Sec-Shopify-Storefront-Origin\";\nexport const STOREFRONT_ID_HEADER = \"Shopify-Storefront-Id\";\nexport const SHOPIFY_STOREFRONT_S_HEADER = \"Shopify-Storefront-S\";\nexport const SHOPIFY_STOREFRONT_Y_HEADER = \"Shopify-Storefront-Y\";\nexport const SHOPIFY_UNIQUE_TOKEN_HEADER = \"X-Shopify-UniqueToken\";\nexport const SHOPIFY_VISIT_TOKEN_HEADER = \"X-Shopify-VisitToken\";\nexport const STOREFRONT_ACCESS_TOKEN_HEADER = \"X-Shopify-Storefront-Access-Token\";\nexport const STOREFRONT_BUYER_IP_HEADER = \"Shopify-Storefront-Buyer-IP\";\nexport const STOREFRONT_PRIVATE_TOKEN_HEADER = \"Shopify-Storefront-Private-Token\";\nexport const STOREFRONT_URL_HEADER = \"x-storefront-url\";\n\nexport type StandardHeaderName =\n | \"accept\"\n | \"accept-encoding\"\n | \"accept-language\"\n | \"access-control-request-headers\"\n | \"access-control-request-method\"\n | typeof CACHE_CONTROL_HEADER\n | \"cf-connecting-ip\"\n | \"connection\"\n | \"content-length\"\n | \"content-type\"\n | \"cookie\"\n | \"host\"\n | \"keep-alive\"\n | \"origin\"\n | \"proxy-authenticate\"\n | \"proxy-authorization\"\n | \"referer\"\n | typeof SERVER_TIMING_HEADER\n | typeof SURROGATE_CONTROL_HEADER\n | \"te\"\n | \"trailer\"\n | \"transfer-encoding\"\n | \"upgrade\"\n | \"user-agent\"\n | \"x-requested-with\";\n\nexport type ShopifyHeaderName =\n | typeof CONSENT_MANAGEMENT_HEADER\n | typeof HYDROGEN_VERSION_HEADER\n | typeof REQUEST_GROUP_ID_HEADER\n | typeof SDK_VARIANT_HEADER\n | typeof SDK_VARIANT_SOURCE_HEADER\n | typeof SDK_VERSION_HEADER\n | typeof SHOPIFY_STOREFRONT_ORIGIN_HEADER\n | typeof SHOPIFY_STOREFRONT_S_HEADER\n | typeof SHOPIFY_STOREFRONT_Y_HEADER\n | typeof SHOPIFY_UNIQUE_TOKEN_HEADER\n | typeof SHOPIFY_VISIT_TOKEN_HEADER\n | typeof STOREFRONT_ACCESS_TOKEN_HEADER\n | typeof STOREFRONT_BUYER_IP_HEADER\n | typeof STOREFRONT_ID_HEADER\n | typeof STOREFRONT_PRIVATE_TOKEN_HEADER\n | typeof STOREFRONT_URL_HEADER;\n\ntype KnownHeaderName = StandardHeaderName | ShopifyHeaderName;\n\nexport function defineHeaderList<const HeaderNames extends readonly KnownHeaderName[]>(\n ...headerNames: HeaderNames\n): HeaderNames {\n return headerNames;\n}\n\nexport function extractHeaders(\n getter: (key: string) => string | null,\n allowlist: readonly string[],\n): [string, string][] {\n return allowlist.reduce<[string, string][]>((acc, key) => {\n const value = getter(key);\n if (value != null) acc.push([key, value]);\n return acc;\n }, []);\n}\n\nexport function applyPrivateResponseCacheHeaders(headers: Headers): void {\n headers.set(CACHE_CONTROL_HEADER, \"private, no-store, max-age=0, must-revalidate\");\n for (const header of Array.from(headers.keys())) {\n if (\n /^(?:.+-)?cdn-cache-control$/i.test(header) ||\n header.toLowerCase() === SURROGATE_CONTROL_HEADER.toLowerCase()\n ) {\n headers.delete(header);\n }\n }\n}\n\nconst COMMON_PROXY_HEADER_ALLOWLIST = defineHeaderList(\n \"accept\",\n \"accept-encoding\",\n \"accept-language\",\n \"content-type\",\n \"cookie\",\n \"origin\",\n \"referer\",\n \"user-agent\",\n);\n\nexport const SFAPI_REQUEST_HEADER_ALLOWLIST = defineHeaderList(\n ...COMMON_PROXY_HEADER_ALLOWLIST,\n \"content-length\",\n \"access-control-request-headers\",\n \"access-control-request-method\",\n STOREFRONT_ACCESS_TOKEN_HEADER,\n STOREFRONT_PRIVATE_TOKEN_HEADER,\n SHOPIFY_UNIQUE_TOKEN_HEADER,\n SHOPIFY_VISIT_TOKEN_HEADER,\n);\n\nexport const MCP_REQUEST_HEADER_ALLOWLIST = defineHeaderList(...COMMON_PROXY_HEADER_ALLOWLIST);\n\nexport const AJAX_API_REQUEST_HEADER_ALLOWLIST = defineHeaderList(\n ...COMMON_PROXY_HEADER_ALLOWLIST,\n \"content-length\",\n \"x-requested-with\",\n);\n\n// Hop-by-hop headers must not be forwarded by proxies. `host` and\n// `content-length` are also removed so fetch can derive them for the upstream\n// request. See https://www.rfc-editor.org/rfc/rfc9110.html#section-7.6.1\nexport const PROXY_REQUEST_HEADER_DENYLIST = defineHeaderList(\n \"cf-connecting-ip\",\n \"connection\",\n \"content-length\",\n \"host\",\n \"keep-alive\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n);\n"],"mappings":";AAAA,MAAa,uBAAuB;AACpC,MAAa,4BAA4B;AACzC,MAAa,0BAA0B;AACvC,MAAa,uBAAuB;AACpC,MAAa,2BAA2B;AACxC,MAAa,0BAA0B;AACvC,MAAa,qBAAqB;AAClC,MAAa,4BAA4B;AACzC,MAAa,qBAAqB;AAClC,MAAa,mCAAmC;AAChD,MAAa,uBAAuB;AACpC,MAAa,8BAA8B;AAC3C,MAAa,8BAA8B;AAC3C,MAAa,8BAA8B;AAC3C,MAAa,6BAA6B;AAC1C,MAAa,iCAAiC;AAC9C,MAAa,6BAA6B;AAC1C,MAAa,kCAAkC;AAC/C,MAAa,wBAAwB;AAiDrC,SAAgB,iBACd,GAAG,aACU;AACb,QAAO;;AAGT,SAAgB,eACd,QACA,WACoB;AACpB,QAAO,UAAU,QAA4B,KAAK,QAAQ;EACxD,MAAM,QAAQ,OAAO,IAAI;AACzB,MAAI,SAAS,KAAM,KAAI,KAAK,CAAC,KAAK,MAAM,CAAC;AACzC,SAAO;IACN,EAAE,CAAC;;AAGR,SAAgB,iCAAiC,SAAwB;AACvE,SAAQ,IAAI,sBAAsB,gDAAgD;AAClF,MAAK,MAAM,UAAU,MAAM,KAAK,QAAQ,MAAM,CAAC,CAC7C,KACE,+BAA+B,KAAK,OAAO,IAC3C,OAAO,aAAa,yBAA8B,aAAa,CAE/D,SAAQ,OAAO,OAAO;;AAK5B,MAAM,gCAAgC,iBACpC,UACA,mBACA,mBACA,gBACA,UACA,UACA,WACA,aACD;AAED,MAAa,iCAAiC,iBAC5C,GAAG,+BACH,kBACA,kCACA,iCACA,gCACA,iCACA,6BACA,2BACD;AAED,MAAa,+BAA+B,iBAAiB,GAAG,8BAA8B;AAE9F,MAAa,oCAAoC,iBAC/C,GAAG,+BACH,kBACA,mBACD;AAKD,MAAa,gCAAgC,iBAC3C,oBACA,cACA,kBACA,QACA,cACA,sBACA,uBACA,MACA,WACA,qBACA,UACD"}
1
+ {"version":3,"file":"headers.mjs","names":[],"sources":["../../../src/core/headers.ts"],"sourcesContent":["export const CACHE_CONTROL_HEADER = \"Cache-Control\";\nexport const CONSENT_MANAGEMENT_HEADER = \"Shopify-Storefront-Consent-Management\";\nexport const HYDROGEN_VERSION_HEADER = \"X-Hydrogen-Version\";\nexport const SURROGATE_CONTROL_HEADER = \"Surrogate-Control\";\nexport const REQUEST_GROUP_ID_HEADER = \"Custom-Storefront-Request-Group-ID\";\nexport const SDK_VARIANT_HEADER = \"X-SDK-Variant\";\nexport const SDK_VARIANT_SOURCE_HEADER = \"X-SDK-Variant-Source\";\nexport const SDK_VERSION_HEADER = \"X-SDK-Version\";\nexport const SEC_GPC_HEADER = \"Sec-GPC\";\nexport const SHOPIFY_STOREFRONT_ORIGIN_HEADER = \"Sec-Shopify-Storefront-Origin\";\nexport const STOREFRONT_ID_HEADER = \"Shopify-Storefront-Id\";\nexport const SHOPIFY_STOREFRONT_S_HEADER = \"Shopify-Storefront-S\";\nexport const SHOPIFY_STOREFRONT_Y_HEADER = \"Shopify-Storefront-Y\";\nexport const SHOPIFY_UNIQUE_TOKEN_HEADER = \"X-Shopify-UniqueToken\";\nexport const SHOPIFY_VISIT_TOKEN_HEADER = \"X-Shopify-VisitToken\";\nexport const STOREFRONT_ACCESS_TOKEN_HEADER = \"X-Shopify-Storefront-Access-Token\";\nexport const STOREFRONT_BUYER_IP_HEADER = \"Shopify-Storefront-Buyer-IP\";\nexport const STOREFRONT_PRIVATE_TOKEN_HEADER = \"Shopify-Storefront-Private-Token\";\nexport const STOREFRONT_URL_HEADER = \"x-storefront-url\";\n\nexport type StandardHeaderName =\n | \"accept\"\n | \"accept-encoding\"\n | \"accept-language\"\n | \"access-control-request-headers\"\n | \"access-control-request-method\"\n | typeof CACHE_CONTROL_HEADER\n | \"cf-connecting-ip\"\n | \"connection\"\n | \"content-length\"\n | \"content-type\"\n | \"cookie\"\n | \"host\"\n | \"keep-alive\"\n | \"origin\"\n | \"proxy-authenticate\"\n | \"proxy-authorization\"\n | \"referer\"\n | typeof SEC_GPC_HEADER\n | typeof SURROGATE_CONTROL_HEADER\n | \"te\"\n | \"trailer\"\n | \"transfer-encoding\"\n | \"upgrade\"\n | \"user-agent\"\n | \"x-requested-with\";\n\nexport type ShopifyHeaderName =\n | typeof CONSENT_MANAGEMENT_HEADER\n | typeof HYDROGEN_VERSION_HEADER\n | typeof REQUEST_GROUP_ID_HEADER\n | typeof SDK_VARIANT_HEADER\n | typeof SDK_VARIANT_SOURCE_HEADER\n | typeof SDK_VERSION_HEADER\n | typeof SHOPIFY_STOREFRONT_ORIGIN_HEADER\n | typeof SHOPIFY_STOREFRONT_S_HEADER\n | typeof SHOPIFY_STOREFRONT_Y_HEADER\n | typeof SHOPIFY_UNIQUE_TOKEN_HEADER\n | typeof SHOPIFY_VISIT_TOKEN_HEADER\n | typeof STOREFRONT_ACCESS_TOKEN_HEADER\n | typeof STOREFRONT_BUYER_IP_HEADER\n | typeof STOREFRONT_ID_HEADER\n | typeof STOREFRONT_PRIVATE_TOKEN_HEADER\n | typeof STOREFRONT_URL_HEADER;\n\ntype KnownHeaderName = StandardHeaderName | ShopifyHeaderName;\n\nexport function defineHeaderList<const HeaderNames extends readonly KnownHeaderName[]>(\n ...headerNames: HeaderNames\n): HeaderNames {\n return headerNames;\n}\n\nexport function extractHeaders(\n getter: (key: string) => string | null,\n allowlist: readonly string[],\n): [string, string][] {\n return allowlist.reduce<[string, string][]>((acc, key) => {\n const value = getter(key);\n if (value != null) acc.push([key, value]);\n return acc;\n }, []);\n}\n\nexport function applyPrivateResponseCacheHeaders(headers: Headers): void {\n headers.set(CACHE_CONTROL_HEADER, \"private, no-store, max-age=0, must-revalidate\");\n for (const header of Array.from(headers.keys())) {\n if (\n /^(?:.+-)?cdn-cache-control$/i.test(header) ||\n header.toLowerCase() === SURROGATE_CONTROL_HEADER.toLowerCase()\n ) {\n headers.delete(header);\n }\n }\n}\n\nconst COMMON_PROXY_HEADER_ALLOWLIST = defineHeaderList(\n \"accept\",\n \"accept-encoding\",\n \"accept-language\",\n \"content-type\",\n \"cookie\",\n \"origin\",\n \"referer\",\n \"user-agent\",\n);\n\nexport const SFAPI_REQUEST_HEADER_ALLOWLIST = defineHeaderList(\n ...COMMON_PROXY_HEADER_ALLOWLIST,\n \"content-length\",\n \"access-control-request-headers\",\n \"access-control-request-method\",\n STOREFRONT_ACCESS_TOKEN_HEADER,\n STOREFRONT_PRIVATE_TOKEN_HEADER,\n SHOPIFY_UNIQUE_TOKEN_HEADER,\n SHOPIFY_VISIT_TOKEN_HEADER,\n);\n\nexport const MCP_REQUEST_HEADER_ALLOWLIST = defineHeaderList(...COMMON_PROXY_HEADER_ALLOWLIST);\n\nexport const AJAX_API_REQUEST_HEADER_ALLOWLIST = defineHeaderList(\n ...COMMON_PROXY_HEADER_ALLOWLIST,\n \"content-length\",\n \"x-requested-with\",\n);\n\n// Hop-by-hop headers must not be forwarded by proxies. `host` and\n// `content-length` are also removed so fetch can derive them for the upstream\n// request. See https://www.rfc-editor.org/rfc/rfc9110.html#section-7.6.1\nexport const PROXY_REQUEST_HEADER_DENYLIST = defineHeaderList(\n \"cf-connecting-ip\",\n \"connection\",\n \"content-length\",\n \"host\",\n \"keep-alive\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n);\n"],"mappings":";AAAA,MAAa,uBAAuB;AACpC,MAAa,4BAA4B;AACzC,MAAa,0BAA0B;AACvC,MAAa,2BAA2B;AACxC,MAAa,0BAA0B;AACvC,MAAa,qBAAqB;AAClC,MAAa,4BAA4B;AACzC,MAAa,qBAAqB;AAClC,MAAa,iBAAiB;AAC9B,MAAa,mCAAmC;AAChD,MAAa,uBAAuB;AACpC,MAAa,8BAA8B;AAC3C,MAAa,8BAA8B;AAC3C,MAAa,8BAA8B;AAC3C,MAAa,6BAA6B;AAC1C,MAAa,iCAAiC;AAC9C,MAAa,6BAA6B;AAC1C,MAAa,kCAAkC;AAC/C,MAAa,wBAAwB;AAiDrC,SAAgB,iBACd,GAAG,aACU;AACb,QAAO;;AAGT,SAAgB,eACd,QACA,WACoB;AACpB,QAAO,UAAU,QAA4B,KAAK,QAAQ;EACxD,MAAM,QAAQ,OAAO,IAAI;AACzB,MAAI,SAAS,KAAM,KAAI,KAAK,CAAC,KAAK,MAAM,CAAC;AACzC,SAAO;IACN,EAAE,CAAC;;AAGR,SAAgB,iCAAiC,SAAwB;AACvE,SAAQ,IAAI,sBAAsB,gDAAgD;AAClF,MAAK,MAAM,UAAU,MAAM,KAAK,QAAQ,MAAM,CAAC,CAC7C,KACE,+BAA+B,KAAK,OAAO,IAC3C,OAAO,aAAa,yBAA8B,aAAa,CAE/D,SAAQ,OAAO,OAAO;;AAK5B,MAAM,gCAAgC,iBACpC,UACA,mBACA,mBACA,gBACA,UACA,UACA,WACA,aACD;AAED,MAAa,iCAAiC,iBAC5C,GAAG,+BACH,kBACA,kCACA,iCACA,gCACA,iCACA,6BACA,2BACD;AAED,MAAa,+BAA+B,iBAAiB,GAAG,8BAA8B;AAE9F,MAAa,oCAAoC,iBAC/C,GAAG,+BACH,kBACA,mBACD;AAKD,MAAa,gCAAgC,iBAC3C,oBACA,cACA,kBACA,QACA,cACA,sBACA,uBACA,MACA,WACA,qBACA,UACD"}
@@ -15,12 +15,12 @@ import { createFetchWithCache } from "./cache/fetch-with-cache.mjs";
15
15
  import { StorefrontApiError, StorefrontTimeoutError } from "../client/errors.mjs";
16
16
  import { ProductFilter } from "../vendor/standard-events.mjs";
17
17
  import { Attribute, CartCost, CartData, CartErrorGroup, CartErrorState, CartLine, CartLineConnection, CartLineCost, CartLineMerchandise, CartNetworkEntry, CartPending, CartState, CartUserError, CartWarning, DiscountCode, EMPTY_CART_DATA, EMPTY_CART_STATE, createEmptyCartErrors, createEmptyErrorGroup } from "./cart/state.mjs";
18
- import { CartNetworkError, CartStore, CreateCartStoreOptions, STANDARD_ACTION_TIMEOUT_IN_MS, configureCartEndpoint, createCartStore } from "./cart/cart.mjs";
18
+ import { CartActions, CartNetworkError, CartStore, CreateCartStoreOptions, STANDARD_ACTION_TIMEOUT_IN_MS, configureCartEndpoint, createCartStore } from "./cart/cart.mjs";
19
19
  import { trackCartAnalytics } from "./analytics/cart-tracker.mjs";
20
20
  import { AnalyticsEvent, AnalyticsEventName } from "./analytics/events.mjs";
21
21
  import { ShopifyGlobal } from "../globals.mjs";
22
22
  import { ShopifyRoutesOptions, ShopifyScriptTagDescriptor, ShopifyScriptTagDescriptors, ShopifyScriptTagsOptions, ShopifyScriptsAnalyticsConfig, ShopifyScriptsI18n, ShopifyScriptsOptions, ShopifyScriptsShop } from "./shopify-scripts/types.mjs";
23
- import { AnalyticsCart, AnalyticsCartLine, AnalyticsEventMap, CartLineUpdatePayload, CartUpdatePayload, CartViewPayload, CollectionViewPayload, ConsentConfig, EventPayloads, OtherData, PageViewPayload, PayloadFor, ProductPayload, ProductViewPayload, SearchViewPayload, ShopAnalytics, StorefrontAnalytics, StorefrontAnalyticsConfig, StorefrontAnalyticsDestination, StorefrontAnalyticsDestinationSetupContext } from "./analytics/types.mjs";
23
+ import { AnalyticsCart, AnalyticsCartLine, AnalyticsEventMap, AnalyticsTrackingValues, CartLineUpdatePayload, CartUpdatePayload, CartViewPayload, CollectionViewPayload, ConsentConfig, ConsentPreferences, ConsentSetup, OtherData, PageViewPayload, PayloadFor, ProductPayload, ProductViewPayload, SearchViewPayload, ShopAnalytics, StorefrontAnalytics, StorefrontAnalyticsConfig, StorefrontAnalyticsDestination, StorefrontAnalyticsDestinationEventContext, StorefrontAnalyticsDestinationSetupContext } from "./analytics/types.mjs";
24
24
  import { CartFormRegister, QuantityInputAttributes, SetButtonAttributes, createCartFormRegister } from "./cart/form.mjs";
25
25
  import { attachQuantityInput } from "./cart/attach-quantity-input.mjs";
26
26
  import { CartAction, CartAttributeInput, CartLineAddInput, CartLineUpdateInput, parseCartRequest } from "./cart/actions.mjs";
@@ -32,7 +32,7 @@ import { DEFAULT_MINIMUM_QUANTITY, NO_QUANTITY_LIMIT, sanitizeQuantity } from ".
32
32
  import { initializeShopifyScripts } from "./shopify-scripts/initialize.mjs";
33
33
  import { getShopifyScriptTags, renderShopifyScriptTags } from "./shopify-scripts/index.mjs";
34
34
  import { AvailableFilter, AvailableFilterValue, CollectionState, FilterPresentation, FilterType, createInitialCollectionState } from "./collection/state.mjs";
35
- import { CollectionData, CollectionStore, CreateCollectionStoreOptions, createCollectionStore } from "./collection/collection.mjs";
35
+ import { CollectionActions, CollectionData, CollectionStore, CreateCollectionStoreOptions, createCollectionStore } from "./collection/collection.mjs";
36
36
  import { CollectionReconciler, ReconcilerCallbacks, createCollectionReconciler } from "./collection/reconciler.mjs";
37
37
  import { CollectionParams, filterEquals, getFilterRemovalUrl, getSortByValue, isFilterInputActive, normalizeCollectionSearch, parseCollectionParams, parseSortByValue, serializeCollectionParams } from "./collection/url.mjs";
38
38
  import { ProductInput, ProductOptionInput, ProductOptionValueInput, ProductVariantFrom, ProductVariantInput, SelectedOption, VariantOptionState, VariantOptionValueState } from "./product/state.mjs";
@@ -45,7 +45,7 @@ import { PredictiveSearchFormAttributes, PredictiveSearchFormRegister, Predictiv
45
45
  import { CreatePredictiveSearchQueriesOptions, PredictiveSearchFragments, makePredictiveSearchQueries } from "./predictive-search/queries.mjs";
46
46
  import { PredictiveSearchData, QueryPredictiveSearchOptions, queryPredictiveSearch } from "./predictive-search/search.mjs";
47
47
  import { CreatePredictiveSearchServerHandlersOptions, createPredictiveSearchServerHandlers } from "./predictive-search/server-handlers.mjs";
48
- import { CreatePredictiveSearchStoreOptions, PredictiveSearchState, PredictiveSearchStatus, PredictiveSearchStore, createPredictiveSearchStore } from "./predictive-search/store.mjs";
48
+ import { CreatePredictiveSearchStoreOptions, PredictiveSearchActions, PredictiveSearchState, PredictiveSearchStatus, PredictiveSearchStore, createPredictiveSearchStore } from "./predictive-search/store.mjs";
49
49
  import { PredictiveSearchArticleItem, PredictiveSearchCollectionItem, PredictiveSearchItem, PredictiveSearchItemUrlOptions, PredictiveSearchPageItem, PredictiveSearchProductItem, PredictiveSearchQueryItem, PredictiveSearchQueryItemUrlOptions, PredictiveSearchResourceItem, getPredictiveSearchItemUrl, getSearchResultUrl } from "./predictive-search/url.mjs";
50
50
  import { FormatMoneyOptions, FormattedMoney, FormattedMoneyRange, MoneyV2 } from "./money/types.mjs";
51
51
  import { formatMoney } from "./money/format.mjs";
@@ -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\nexport function 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/** @internal */\nexport function resetLoggingForTests(): void {\n state.logger = consoleLogger;\n state.level = DEFAULT_LOG_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,SAAgB,gBAAgB,OAAoB,OAAuB;AACzE,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;;AAkBhB,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 * 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"}
@@ -3,15 +3,18 @@ import { getFormatter } from "./cache.mjs";
3
3
  //#region src/core/money/format.ts
4
4
  /**
5
5
  * Intl.NumberFormat part types that make up the raw numeric amount.
6
- * Used to extract "19.99" from a formatted string like "$19.99" by
7
- * filtering out currency symbols, sign, and other non-numeric parts.
6
+ * Used to extract "19.99" from a formatted string like "$19.99" (or
7
+ * "-19.99" from "-$19.99") by filtering out currency symbols and other
8
+ * non-numeric parts. The minus sign is kept so negative amounts such as
9
+ * refunds and discounts don't render as positive.
8
10
  */
9
11
  const NUMERIC_PART_TYPES = new Set([
10
12
  "decimal",
11
13
  "fraction",
12
14
  "group",
13
15
  "integer",
14
- "literal"
16
+ "literal",
17
+ "minusSign"
15
18
  ]);
16
19
  const WELL_FORMED_CURRENCY_CODE = /^[a-z]{3}$/i;
17
20
  function parseAmount(money) {
@@ -1 +1 @@
1
- {"version":3,"file":"format.mjs","names":["#amount","#options","#currencyCode","#supported","#defaultFormatter","#amountString","#currencySymbol","#currencyNarrowSymbol","#currencyName","#parts","#withoutTrailingZeros","#withoutTrailingZerosAndCurrency","#formatterParts"],"sources":["../../../../src/core/money/format.ts"],"sourcesContent":["import { getFormatter } from \"./cache\";\nimport type { FormatMoneyOptions, FormattedMoney, FormattedMoneyRange, MoneyV2 } from \"./types\";\n\n/**\n * Intl.NumberFormat part types that make up the raw numeric amount.\n * Used to extract \"19.99\" from a formatted string like \"$19.99\" by\n * filtering out currency symbols, sign, and other non-numeric parts.\n */\nconst NUMERIC_PART_TYPES = new Set([\"decimal\", \"fraction\", \"group\", \"integer\", \"literal\"]);\nconst WELL_FORMED_CURRENCY_CODE = /^[a-z]{3}$/i;\n\ntype RangeFormatter = Intl.NumberFormat & {\n formatRange?: (start: number, end: number) => string;\n};\n\ntype MoneyRangeEntry = {\n money: MoneyV2;\n amount: number;\n currencyCode: string;\n};\n\nfunction parseAmount(money: MoneyV2): number {\n const parsed = parseFloat(money.amount);\n if (Number.isNaN(parsed)) {\n throw new Error(\n `formatMoney: \"${money.amount}\" is not a valid numeric amount for currency ${money.currencyCode}`,\n );\n }\n return parsed;\n}\n\nfunction normalizeCurrencyCode(currencyCode: string): string {\n return currencyCode.toUpperCase();\n}\n\n/**\n * Intl accepts well-formed three-letter currency codes. Non-standard but\n * well-formed codes render as codes; malformed codes such as USDC throw.\n */\nfunction isWellFormedCurrencyCode(currencyCode: string): boolean {\n return WELL_FORMED_CURRENCY_CODE.test(currencyCode);\n}\n\nfunction hasTrailingZeros(amount: number): boolean {\n return amount % 1 === 0;\n}\n\nfunction shouldStripTrailingZeros(amount: number, options: FormatMoneyOptions): boolean {\n return options.withoutTrailingZeros === true && hasTrailingZeros(amount);\n}\n\nfunction hasExplicitFractionDigits(options: FormatMoneyOptions): boolean {\n return options.minimumFractionDigits != null || options.maximumFractionDigits != null;\n}\n\nfunction decimalFractionOptions(options: FormatMoneyOptions): Intl.NumberFormatOptions {\n return {\n minimumFractionDigits: options.minimumFractionDigits ?? 2,\n maximumFractionDigits: options.maximumFractionDigits ?? 2,\n };\n}\n\nfunction buildFormatOptions(\n currencyCode: string,\n supported: boolean,\n options: FormatMoneyOptions,\n amount: number,\n): Intl.NumberFormatOptions {\n const withoutCurrency = options.withoutCurrency === true || !supported;\n const trailingZeros = shouldStripTrailingZeros(amount, options);\n\n if (withoutCurrency) {\n return trailingZeros\n ? { minimumFractionDigits: 0, maximumFractionDigits: 0 }\n : decimalFractionOptions(options);\n }\n\n return {\n style: \"currency\",\n currency: currencyCode,\n ...(options.currencyDisplay && { currencyDisplay: options.currencyDisplay }),\n ...(trailingZeros && {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n }),\n ...(!trailingZeros &&\n options.minimumFractionDigits != null && {\n minimumFractionDigits: options.minimumFractionDigits,\n }),\n ...(!trailingZeros &&\n options.maximumFractionDigits != null && {\n maximumFractionDigits: options.maximumFractionDigits,\n }),\n };\n}\n\nfunction appendUnsupportedCurrencyCode(formatted: string, currencyCode: string): string {\n return `${formatted} ${currencyCode}`;\n}\n\nfunction extractNumericAmount(parts: Intl.NumberFormatPart[]): string {\n return parts\n .filter((part) => NUMERIC_PART_TYPES.has(part.type))\n .map((part) => part.value)\n .join(\"\");\n}\n\nfunction findCurrencyPart(parts: Intl.NumberFormatPart[]): string {\n return parts.find((part) => part.type === \"currency\")?.value ?? \"\";\n}\n\nfunction formatRange(formatter: Intl.NumberFormat, min: number, max: number): string {\n const rangeFormatter = formatter as RangeFormatter;\n return rangeFormatter.formatRange\n ? rangeFormatter.formatRange(min, max)\n : `${formatter.format(min)} - ${formatter.format(max)}`;\n}\n\nfunction isMoneyRange(money: MoneyV2 | readonly MoneyV2[]): money is readonly MoneyV2[] {\n return Array.isArray(money);\n}\n\nfunction buildRangeOptions(\n currencyCode: string,\n supported: boolean,\n options: FormatMoneyOptions,\n amounts: number[],\n): Intl.NumberFormatOptions {\n const wholeRange = amounts.every(hasTrailingZeros);\n const rangeOptions = {\n ...options,\n withoutTrailingZeros:\n options.withoutTrailingZeros ?? (wholeRange && !hasExplicitFractionDigits(options)),\n };\n\n return buildFormatOptions(currencyCode, supported, rangeOptions, amounts[0] ?? 0);\n}\n\nfunction sortMoneyRangeEntries(entries: MoneyRangeEntry[]): MoneyRangeEntry[] {\n const sorted: MoneyRangeEntry[] = [];\n\n for (const entry of entries) {\n const index = sorted.findIndex((candidate) => entry.amount < candidate.amount);\n if (index === -1) {\n sorted.push(entry);\n } else {\n sorted.splice(index, 0, entry);\n }\n }\n\n return sorted;\n}\n\n/**\n * All structured fields are computed lazily on first access and cached.\n * localizedString is the exception — it's computed eagerly in the constructor\n * so that the common `${price}` path never touches the getter machinery.\n */\nclass FormattedMoneyValue implements FormattedMoney {\n readonly #amount: number;\n readonly #options: FormatMoneyOptions;\n readonly #currencyCode: string;\n readonly #supported: boolean;\n readonly localizedString: string;\n\n readonly #defaultFormatter: Intl.NumberFormat;\n #formatterParts?: Intl.NumberFormatPart[];\n #parts?: Intl.NumberFormatPart[];\n #amountString?: string;\n #currencySymbol?: string;\n #currencyNarrowSymbol?: string;\n #currencyName?: string;\n #withoutTrailingZeros?: string;\n #withoutTrailingZerosAndCurrency?: string;\n\n constructor(money: MoneyV2, options: FormatMoneyOptions) {\n this.#amount = parseAmount(money);\n this.#options = options;\n this.#currencyCode = normalizeCurrencyCode(money.currencyCode);\n this.#supported = isWellFormedCurrencyCode(money.currencyCode);\n this.#defaultFormatter = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n );\n\n const formatted = this.#defaultFormatter.format(this.#amount);\n this.localizedString =\n this.#supported || options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, this.#currencyCode);\n }\n\n get amount(): string {\n this.#amountString ??= extractNumericAmount(this.formatterParts);\n return this.#amountString;\n }\n\n get numericAmount(): number {\n return this.#amount;\n }\n\n get currencySymbol(): string {\n this.#currencySymbol ??= this.#supported ? findCurrencyPart(this.parts) : this.#currencyCode;\n return this.#currencySymbol;\n }\n\n get currencyNarrowSymbol(): string {\n this.#currencyNarrowSymbol ??= this.#supported\n ? findCurrencyPart(\n getFormatter(this.#options.locale, {\n style: \"currency\",\n currency: this.#currencyCode,\n currencyDisplay: \"narrowSymbol\",\n ...(this.#options.minimumFractionDigits != null && {\n minimumFractionDigits: this.#options.minimumFractionDigits,\n }),\n ...(this.#options.maximumFractionDigits != null && {\n maximumFractionDigits: this.#options.maximumFractionDigits,\n }),\n }).formatToParts(this.#amount),\n )\n : this.#currencyCode;\n return this.#currencyNarrowSymbol;\n }\n\n get currencyName(): string {\n this.#currencyName ??= this.#supported\n ? findCurrencyPart(\n getFormatter(this.#options.locale, {\n style: \"currency\",\n currency: this.#currencyCode,\n currencyDisplay: \"name\",\n ...(this.#options.minimumFractionDigits != null && {\n minimumFractionDigits: this.#options.minimumFractionDigits,\n }),\n ...(this.#options.maximumFractionDigits != null && {\n maximumFractionDigits: this.#options.maximumFractionDigits,\n }),\n }).formatToParts(this.#amount),\n ) || this.#currencyCode\n : this.#currencyCode;\n return this.#currencyName;\n }\n\n get parts(): Intl.NumberFormatPart[] {\n if (!this.#parts) {\n this.#parts =\n this.#supported || this.#options.withoutCurrency === true\n ? this.formatterParts\n : [\n ...this.formatterParts,\n { type: \"literal\" as const, value: \" \" },\n { type: \"currency\" as const, value: this.#currencyCode },\n ];\n }\n\n return this.#parts;\n }\n\n get withoutTrailingZeros(): string {\n if (!this.#withoutTrailingZeros) {\n const options = { ...this.#options, withoutTrailingZeros: true };\n const formatter = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n );\n const formatted = formatter.format(this.#amount);\n this.#withoutTrailingZeros =\n this.#supported || this.#options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, this.#currencyCode);\n }\n\n return this.#withoutTrailingZeros;\n }\n\n get withoutTrailingZerosAndCurrency(): string {\n if (!this.#withoutTrailingZerosAndCurrency) {\n const options = { ...this.#options, withoutCurrency: true, withoutTrailingZeros: true };\n this.#withoutTrailingZerosAndCurrency = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n ).format(this.#amount);\n }\n\n return this.#withoutTrailingZerosAndCurrency;\n }\n\n private get formatterParts(): Intl.NumberFormatPart[] {\n this.#formatterParts ??= this.#defaultFormatter.formatToParts(this.#amount);\n return this.#formatterParts;\n }\n\n toString(): string {\n return this.localizedString;\n }\n}\n\nclass FormattedMoneyRangeValue implements FormattedMoneyRange {\n readonly localizedString: string;\n readonly min: MoneyV2;\n readonly max: MoneyV2;\n readonly currencyCode: string;\n\n constructor(range: readonly MoneyV2[], options: FormatMoneyOptions) {\n if (range.length === 0) {\n throw new Error(\"formatMoney: money range must contain at least one value\");\n }\n\n const entries = sortMoneyRangeEntries(\n range.map((money) => ({\n money,\n amount: parseAmount(money),\n currencyCode: normalizeCurrencyCode(money.currencyCode),\n })),\n );\n\n const [first] = entries;\n if (!first) {\n throw new Error(\"formatMoney: money range must contain at least one value\");\n }\n\n const mismatched = entries.find((entry) => entry.currencyCode !== first.currencyCode);\n if (mismatched) {\n throw new Error(\n `formatMoney: range values must share one currency, received ${first.currencyCode} and ${mismatched.currencyCode}`,\n );\n }\n\n const last = entries[entries.length - 1] ?? first;\n this.min = first.money;\n this.max = last.money;\n this.currencyCode = first.currencyCode;\n\n if (first.amount === last.amount) {\n this.localizedString = new FormattedMoneyValue(first.money, {\n ...options,\n withoutTrailingZeros: options.withoutTrailingZeros ?? !hasExplicitFractionDigits(options),\n }).localizedString;\n return;\n }\n\n const supported = isWellFormedCurrencyCode(first.currencyCode);\n const formatter = getFormatter(\n options.locale,\n buildRangeOptions(\n first.currencyCode,\n supported,\n options,\n entries.map((entry) => entry.amount),\n ),\n );\n\n const formatted = formatRange(formatter, first.amount, last.amount);\n this.localizedString =\n supported || options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, first.currencyCode);\n }\n\n toString(): string {\n return this.localizedString;\n }\n}\n\n/**\n * Format a MoneyV2 value or a same-currency range of MoneyV2 values for display.\n *\n * Returns an object that stringifies to the locale-formatted price via\n * toString(). Structured fields on single prices are computed lazily when\n * accessed, so the common string path stays cheap.\n */\nexport function formatMoney(money: MoneyV2, options: FormatMoneyOptions): FormattedMoney;\nexport function formatMoney(\n money: readonly MoneyV2[],\n options: FormatMoneyOptions,\n): FormattedMoneyRange;\n\n// This is the implementation of the formatMoney function overloading the other two.\n// It checks if the input is a range and creates the appropriate object.\nexport function formatMoney(\n money: MoneyV2 | readonly MoneyV2[],\n options: FormatMoneyOptions,\n): FormattedMoney | FormattedMoneyRange {\n return isMoneyRange(money)\n ? new FormattedMoneyRangeValue(money, options)\n : new FormattedMoneyValue(money, options);\n}\n"],"mappings":";;;;;;;;AAQA,MAAM,qBAAqB,IAAI,IAAI;CAAC;CAAW;CAAY;CAAS;CAAW;CAAU,CAAC;AAC1F,MAAM,4BAA4B;AAYlC,SAAS,YAAY,OAAwB;CAC3C,MAAM,SAAS,WAAW,MAAM,OAAO;AACvC,KAAI,OAAO,MAAM,OAAO,CACtB,OAAM,IAAI,MACR,iBAAiB,MAAM,OAAO,+CAA+C,MAAM,eACpF;AAEH,QAAO;;AAGT,SAAS,sBAAsB,cAA8B;AAC3D,QAAO,aAAa,aAAa;;;;;;AAOnC,SAAS,yBAAyB,cAA+B;AAC/D,QAAO,0BAA0B,KAAK,aAAa;;AAGrD,SAAS,iBAAiB,QAAyB;AACjD,QAAO,SAAS,MAAM;;AAGxB,SAAS,yBAAyB,QAAgB,SAAsC;AACtF,QAAO,QAAQ,yBAAyB,QAAQ,iBAAiB,OAAO;;AAG1E,SAAS,0BAA0B,SAAsC;AACvE,QAAO,QAAQ,yBAAyB,QAAQ,QAAQ,yBAAyB;;AAGnF,SAAS,uBAAuB,SAAuD;AACrF,QAAO;EACL,uBAAuB,QAAQ,yBAAyB;EACxD,uBAAuB,QAAQ,yBAAyB;EACzD;;AAGH,SAAS,mBACP,cACA,WACA,SACA,QAC0B;CAC1B,MAAM,kBAAkB,QAAQ,oBAAoB,QAAQ,CAAC;CAC7D,MAAM,gBAAgB,yBAAyB,QAAQ,QAAQ;AAE/D,KAAI,gBACF,QAAO,gBACH;EAAE,uBAAuB;EAAG,uBAAuB;EAAG,GACtD,uBAAuB,QAAQ;AAGrC,QAAO;EACL,OAAO;EACP,UAAU;EACV,GAAI,QAAQ,mBAAmB,EAAE,iBAAiB,QAAQ,iBAAiB;EAC3E,GAAI,iBAAiB;GACnB,uBAAuB;GACvB,uBAAuB;GACxB;EACD,GAAI,CAAC,iBACH,QAAQ,yBAAyB,QAAQ,EACvC,uBAAuB,QAAQ,uBAChC;EACH,GAAI,CAAC,iBACH,QAAQ,yBAAyB,QAAQ,EACvC,uBAAuB,QAAQ,uBAChC;EACJ;;AAGH,SAAS,8BAA8B,WAAmB,cAA8B;AACtF,QAAO,GAAG,UAAU,GAAG;;AAGzB,SAAS,qBAAqB,OAAwC;AACpE,QAAO,MACJ,QAAQ,SAAS,mBAAmB,IAAI,KAAK,KAAK,CAAC,CACnD,KAAK,SAAS,KAAK,MAAM,CACzB,KAAK,GAAG;;AAGb,SAAS,iBAAiB,OAAwC;AAChE,QAAO,MAAM,MAAM,SAAS,KAAK,SAAS,WAAW,EAAE,SAAS;;AAGlE,SAAS,YAAY,WAA8B,KAAa,KAAqB;CACnF,MAAM,iBAAiB;AACvB,QAAO,eAAe,cAClB,eAAe,YAAY,KAAK,IAAI,GACpC,GAAG,UAAU,OAAO,IAAI,CAAC,KAAK,UAAU,OAAO,IAAI;;AAGzD,SAAS,aAAa,OAAkE;AACtF,QAAO,MAAM,QAAQ,MAAM;;AAG7B,SAAS,kBACP,cACA,WACA,SACA,SAC0B;CAC1B,MAAM,aAAa,QAAQ,MAAM,iBAAiB;AAOlD,QAAO,mBAAmB,cAAc,WAAW;EALjD,GAAG;EACH,sBACE,QAAQ,yBAAyB,cAAc,CAAC,0BAA0B,QAAQ;EAGvB,EAAE,QAAQ,MAAM,EAAE;;AAGnF,SAAS,sBAAsB,SAA+C;CAC5E,MAAM,SAA4B,EAAE;AAEpC,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,QAAQ,OAAO,WAAW,cAAc,MAAM,SAAS,UAAU,OAAO;AAC9E,MAAI,UAAU,GACZ,QAAO,KAAK,MAAM;MAElB,QAAO,OAAO,OAAO,GAAG,MAAM;;AAIlC,QAAO;;;;;;;AAQT,IAAM,sBAAN,MAAoD;CAClD,CAASA;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,AAAS;CAET,CAASC;CACT;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,OAAgB,SAA6B;AACvD,QAAKJ,SAAU,YAAY,MAAM;AACjC,QAAKC,UAAW;AAChB,QAAKC,eAAgB,sBAAsB,MAAM,aAAa;AAC9D,QAAKC,YAAa,yBAAyB,MAAM,aAAa;AAC9D,QAAKC,mBAAoB,aACvB,QAAQ,QACR,mBAAmB,MAAKF,cAAe,MAAKC,WAAY,SAAS,MAAKH,OAAQ,CAC/E;EAED,MAAM,YAAY,MAAKI,iBAAkB,OAAO,MAAKJ,OAAQ;AAC7D,OAAK,kBACH,MAAKG,aAAc,QAAQ,oBAAoB,OAC3C,YACA,8BAA8B,WAAW,MAAKD,aAAc;;CAGpE,IAAI,SAAiB;AACnB,QAAKG,iBAAkB,qBAAqB,KAAK,eAAe;AAChE,SAAO,MAAKA;;CAGd,IAAI,gBAAwB;AAC1B,SAAO,MAAKL;;CAGd,IAAI,iBAAyB;AAC3B,QAAKM,mBAAoB,MAAKH,YAAa,iBAAiB,KAAK,MAAM,GAAG,MAAKD;AAC/E,SAAO,MAAKI;;CAGd,IAAI,uBAA+B;AACjC,QAAKC,yBAA0B,MAAKJ,YAChC,iBACE,aAAa,MAAKF,QAAS,QAAQ;GACjC,OAAO;GACP,UAAU,MAAKC;GACf,iBAAiB;GACjB,GAAI,MAAKD,QAAS,yBAAyB,QAAQ,EACjD,uBAAuB,MAAKA,QAAS,uBACtC;GACD,GAAI,MAAKA,QAAS,yBAAyB,QAAQ,EACjD,uBAAuB,MAAKA,QAAS,uBACtC;GACF,CAAC,CAAC,cAAc,MAAKD,OAAQ,CAC/B,GACD,MAAKE;AACT,SAAO,MAAKK;;CAGd,IAAI,eAAuB;AACzB,QAAKC,iBAAkB,MAAKL,YACxB,iBACE,aAAa,MAAKF,QAAS,QAAQ;GACjC,OAAO;GACP,UAAU,MAAKC;GACf,iBAAiB;GACjB,GAAI,MAAKD,QAAS,yBAAyB,QAAQ,EACjD,uBAAuB,MAAKA,QAAS,uBACtC;GACD,GAAI,MAAKA,QAAS,yBAAyB,QAAQ,EACjD,uBAAuB,MAAKA,QAAS,uBACtC;GACF,CAAC,CAAC,cAAc,MAAKD,OAAQ,CAC/B,IAAI,MAAKE,eACV,MAAKA;AACT,SAAO,MAAKM;;CAGd,IAAI,QAAiC;AACnC,MAAI,CAAC,MAAKC,MACR,OAAKA,QACH,MAAKN,aAAc,MAAKF,QAAS,oBAAoB,OACjD,KAAK,iBACL;GACE,GAAG,KAAK;GACR;IAAE,MAAM;IAAoB,OAAO;IAAK;GACxC;IAAE,MAAM;IAAqB,OAAO,MAAKC;IAAe;GACzD;AAGT,SAAO,MAAKO;;CAGd,IAAI,uBAA+B;AACjC,MAAI,CAAC,MAAKC,sBAAuB;GAC/B,MAAM,UAAU;IAAE,GAAG,MAAKT;IAAU,sBAAsB;IAAM;GAKhE,MAAM,YAJY,aAChB,QAAQ,QACR,mBAAmB,MAAKC,cAAe,MAAKC,WAAY,SAAS,MAAKH,OAAQ,CAErD,CAAC,OAAO,MAAKA,OAAQ;AAChD,SAAKU,uBACH,MAAKP,aAAc,MAAKF,QAAS,oBAAoB,OACjD,YACA,8BAA8B,WAAW,MAAKC,aAAc;;AAGpE,SAAO,MAAKQ;;CAGd,IAAI,kCAA0C;AAC5C,MAAI,CAAC,MAAKC,iCAAkC;GAC1C,MAAM,UAAU;IAAE,GAAG,MAAKV;IAAU,iBAAiB;IAAM,sBAAsB;IAAM;AACvF,SAAKU,kCAAmC,aACtC,QAAQ,QACR,mBAAmB,MAAKT,cAAe,MAAKC,WAAY,SAAS,MAAKH,OAAQ,CAC/E,CAAC,OAAO,MAAKA,OAAQ;;AAGxB,SAAO,MAAKW;;CAGd,IAAY,iBAA0C;AACpD,QAAKC,mBAAoB,MAAKR,iBAAkB,cAAc,MAAKJ,OAAQ;AAC3E,SAAO,MAAKY;;CAGd,WAAmB;AACjB,SAAO,KAAK;;;AAIhB,IAAM,2BAAN,MAA8D;CAC5D,AAAS;CACT,AAAS;CACT,AAAS;CACT,AAAS;CAET,YAAY,OAA2B,SAA6B;AAClE,MAAI,MAAM,WAAW,EACnB,OAAM,IAAI,MAAM,2DAA2D;EAG7E,MAAM,UAAU,sBACd,MAAM,KAAK,WAAW;GACpB;GACA,QAAQ,YAAY,MAAM;GAC1B,cAAc,sBAAsB,MAAM,aAAa;GACxD,EAAE,CACJ;EAED,MAAM,CAAC,SAAS;AAChB,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,2DAA2D;EAG7E,MAAM,aAAa,QAAQ,MAAM,UAAU,MAAM,iBAAiB,MAAM,aAAa;AACrF,MAAI,WACF,OAAM,IAAI,MACR,+DAA+D,MAAM,aAAa,OAAO,WAAW,eACrG;EAGH,MAAM,OAAO,QAAQ,QAAQ,SAAS,MAAM;AAC5C,OAAK,MAAM,MAAM;AACjB,OAAK,MAAM,KAAK;AAChB,OAAK,eAAe,MAAM;AAE1B,MAAI,MAAM,WAAW,KAAK,QAAQ;AAChC,QAAK,kBAAkB,IAAI,oBAAoB,MAAM,OAAO;IAC1D,GAAG;IACH,sBAAsB,QAAQ,wBAAwB,CAAC,0BAA0B,QAAQ;IAC1F,CAAC,CAAC;AACH;;EAGF,MAAM,YAAY,yBAAyB,MAAM,aAAa;EAW9D,MAAM,YAAY,YAVA,aAChB,QAAQ,QACR,kBACE,MAAM,cACN,WACA,SACA,QAAQ,KAAK,UAAU,MAAM,OAAO,CACrC,CAGoC,EAAE,MAAM,QAAQ,KAAK,OAAO;AACnE,OAAK,kBACH,aAAa,QAAQ,oBAAoB,OACrC,YACA,8BAA8B,WAAW,MAAM,aAAa;;CAGpE,WAAmB;AACjB,SAAO,KAAK;;;AAmBhB,SAAgB,YACd,OACA,SACsC;AACtC,QAAO,aAAa,MAAM,GACtB,IAAI,yBAAyB,OAAO,QAAQ,GAC5C,IAAI,oBAAoB,OAAO,QAAQ"}
1
+ {"version":3,"file":"format.mjs","names":["#amount","#options","#currencyCode","#supported","#defaultFormatter","#amountString","#currencySymbol","#currencyNarrowSymbol","#currencyName","#parts","#withoutTrailingZeros","#withoutTrailingZerosAndCurrency","#formatterParts"],"sources":["../../../../src/core/money/format.ts"],"sourcesContent":["import { getFormatter } from \"./cache\";\nimport type { FormatMoneyOptions, FormattedMoney, FormattedMoneyRange, MoneyV2 } from \"./types\";\n\n/**\n * Intl.NumberFormat part types that make up the raw numeric amount.\n * Used to extract \"19.99\" from a formatted string like \"$19.99\" (or\n * \"-19.99\" from \"-$19.99\") by filtering out currency symbols and other\n * non-numeric parts. The minus sign is kept so negative amounts such as\n * refunds and discounts don't render as positive.\n */\nconst NUMERIC_PART_TYPES = new Set([\n \"decimal\",\n \"fraction\",\n \"group\",\n \"integer\",\n \"literal\",\n \"minusSign\",\n]);\nconst WELL_FORMED_CURRENCY_CODE = /^[a-z]{3}$/i;\n\ntype RangeFormatter = Intl.NumberFormat & {\n formatRange?: (start: number, end: number) => string;\n};\n\ntype MoneyRangeEntry = {\n money: MoneyV2;\n amount: number;\n currencyCode: string;\n};\n\nfunction parseAmount(money: MoneyV2): number {\n const parsed = parseFloat(money.amount);\n if (Number.isNaN(parsed)) {\n throw new Error(\n `formatMoney: \"${money.amount}\" is not a valid numeric amount for currency ${money.currencyCode}`,\n );\n }\n return parsed;\n}\n\nfunction normalizeCurrencyCode(currencyCode: string): string {\n return currencyCode.toUpperCase();\n}\n\n/**\n * Intl accepts well-formed three-letter currency codes. Non-standard but\n * well-formed codes render as codes; malformed codes such as USDC throw.\n */\nfunction isWellFormedCurrencyCode(currencyCode: string): boolean {\n return WELL_FORMED_CURRENCY_CODE.test(currencyCode);\n}\n\nfunction hasTrailingZeros(amount: number): boolean {\n return amount % 1 === 0;\n}\n\nfunction shouldStripTrailingZeros(amount: number, options: FormatMoneyOptions): boolean {\n return options.withoutTrailingZeros === true && hasTrailingZeros(amount);\n}\n\nfunction hasExplicitFractionDigits(options: FormatMoneyOptions): boolean {\n return options.minimumFractionDigits != null || options.maximumFractionDigits != null;\n}\n\nfunction decimalFractionOptions(options: FormatMoneyOptions): Intl.NumberFormatOptions {\n return {\n minimumFractionDigits: options.minimumFractionDigits ?? 2,\n maximumFractionDigits: options.maximumFractionDigits ?? 2,\n };\n}\n\nfunction buildFormatOptions(\n currencyCode: string,\n supported: boolean,\n options: FormatMoneyOptions,\n amount: number,\n): Intl.NumberFormatOptions {\n const withoutCurrency = options.withoutCurrency === true || !supported;\n const trailingZeros = shouldStripTrailingZeros(amount, options);\n\n if (withoutCurrency) {\n return trailingZeros\n ? { minimumFractionDigits: 0, maximumFractionDigits: 0 }\n : decimalFractionOptions(options);\n }\n\n return {\n style: \"currency\",\n currency: currencyCode,\n ...(options.currencyDisplay && { currencyDisplay: options.currencyDisplay }),\n ...(trailingZeros && {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n }),\n ...(!trailingZeros &&\n options.minimumFractionDigits != null && {\n minimumFractionDigits: options.minimumFractionDigits,\n }),\n ...(!trailingZeros &&\n options.maximumFractionDigits != null && {\n maximumFractionDigits: options.maximumFractionDigits,\n }),\n };\n}\n\nfunction appendUnsupportedCurrencyCode(formatted: string, currencyCode: string): string {\n return `${formatted} ${currencyCode}`;\n}\n\nfunction extractNumericAmount(parts: Intl.NumberFormatPart[]): string {\n return parts\n .filter((part) => NUMERIC_PART_TYPES.has(part.type))\n .map((part) => part.value)\n .join(\"\");\n}\n\nfunction findCurrencyPart(parts: Intl.NumberFormatPart[]): string {\n return parts.find((part) => part.type === \"currency\")?.value ?? \"\";\n}\n\nfunction formatRange(formatter: Intl.NumberFormat, min: number, max: number): string {\n const rangeFormatter = formatter as RangeFormatter;\n return rangeFormatter.formatRange\n ? rangeFormatter.formatRange(min, max)\n : `${formatter.format(min)} - ${formatter.format(max)}`;\n}\n\nfunction isMoneyRange(money: MoneyV2 | readonly MoneyV2[]): money is readonly MoneyV2[] {\n return Array.isArray(money);\n}\n\nfunction buildRangeOptions(\n currencyCode: string,\n supported: boolean,\n options: FormatMoneyOptions,\n amounts: number[],\n): Intl.NumberFormatOptions {\n const wholeRange = amounts.every(hasTrailingZeros);\n const rangeOptions = {\n ...options,\n withoutTrailingZeros:\n options.withoutTrailingZeros ?? (wholeRange && !hasExplicitFractionDigits(options)),\n };\n\n return buildFormatOptions(currencyCode, supported, rangeOptions, amounts[0] ?? 0);\n}\n\nfunction sortMoneyRangeEntries(entries: MoneyRangeEntry[]): MoneyRangeEntry[] {\n const sorted: MoneyRangeEntry[] = [];\n\n for (const entry of entries) {\n const index = sorted.findIndex((candidate) => entry.amount < candidate.amount);\n if (index === -1) {\n sorted.push(entry);\n } else {\n sorted.splice(index, 0, entry);\n }\n }\n\n return sorted;\n}\n\n/**\n * All structured fields are computed lazily on first access and cached.\n * localizedString is the exception — it's computed eagerly in the constructor\n * so that the common `${price}` path never touches the getter machinery.\n */\nclass FormattedMoneyValue implements FormattedMoney {\n readonly #amount: number;\n readonly #options: FormatMoneyOptions;\n readonly #currencyCode: string;\n readonly #supported: boolean;\n readonly localizedString: string;\n\n readonly #defaultFormatter: Intl.NumberFormat;\n #formatterParts?: Intl.NumberFormatPart[];\n #parts?: Intl.NumberFormatPart[];\n #amountString?: string;\n #currencySymbol?: string;\n #currencyNarrowSymbol?: string;\n #currencyName?: string;\n #withoutTrailingZeros?: string;\n #withoutTrailingZerosAndCurrency?: string;\n\n constructor(money: MoneyV2, options: FormatMoneyOptions) {\n this.#amount = parseAmount(money);\n this.#options = options;\n this.#currencyCode = normalizeCurrencyCode(money.currencyCode);\n this.#supported = isWellFormedCurrencyCode(money.currencyCode);\n this.#defaultFormatter = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n );\n\n const formatted = this.#defaultFormatter.format(this.#amount);\n this.localizedString =\n this.#supported || options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, this.#currencyCode);\n }\n\n get amount(): string {\n this.#amountString ??= extractNumericAmount(this.formatterParts);\n return this.#amountString;\n }\n\n get numericAmount(): number {\n return this.#amount;\n }\n\n get currencySymbol(): string {\n this.#currencySymbol ??= this.#supported ? findCurrencyPart(this.parts) : this.#currencyCode;\n return this.#currencySymbol;\n }\n\n get currencyNarrowSymbol(): string {\n this.#currencyNarrowSymbol ??= this.#supported\n ? findCurrencyPart(\n getFormatter(this.#options.locale, {\n style: \"currency\",\n currency: this.#currencyCode,\n currencyDisplay: \"narrowSymbol\",\n ...(this.#options.minimumFractionDigits != null && {\n minimumFractionDigits: this.#options.minimumFractionDigits,\n }),\n ...(this.#options.maximumFractionDigits != null && {\n maximumFractionDigits: this.#options.maximumFractionDigits,\n }),\n }).formatToParts(this.#amount),\n )\n : this.#currencyCode;\n return this.#currencyNarrowSymbol;\n }\n\n get currencyName(): string {\n this.#currencyName ??= this.#supported\n ? findCurrencyPart(\n getFormatter(this.#options.locale, {\n style: \"currency\",\n currency: this.#currencyCode,\n currencyDisplay: \"name\",\n ...(this.#options.minimumFractionDigits != null && {\n minimumFractionDigits: this.#options.minimumFractionDigits,\n }),\n ...(this.#options.maximumFractionDigits != null && {\n maximumFractionDigits: this.#options.maximumFractionDigits,\n }),\n }).formatToParts(this.#amount),\n ) || this.#currencyCode\n : this.#currencyCode;\n return this.#currencyName;\n }\n\n get parts(): Intl.NumberFormatPart[] {\n if (!this.#parts) {\n this.#parts =\n this.#supported || this.#options.withoutCurrency === true\n ? this.formatterParts\n : [\n ...this.formatterParts,\n { type: \"literal\" as const, value: \" \" },\n { type: \"currency\" as const, value: this.#currencyCode },\n ];\n }\n\n return this.#parts;\n }\n\n get withoutTrailingZeros(): string {\n if (!this.#withoutTrailingZeros) {\n const options = { ...this.#options, withoutTrailingZeros: true };\n const formatter = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n );\n const formatted = formatter.format(this.#amount);\n this.#withoutTrailingZeros =\n this.#supported || this.#options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, this.#currencyCode);\n }\n\n return this.#withoutTrailingZeros;\n }\n\n get withoutTrailingZerosAndCurrency(): string {\n if (!this.#withoutTrailingZerosAndCurrency) {\n const options = { ...this.#options, withoutCurrency: true, withoutTrailingZeros: true };\n this.#withoutTrailingZerosAndCurrency = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n ).format(this.#amount);\n }\n\n return this.#withoutTrailingZerosAndCurrency;\n }\n\n private get formatterParts(): Intl.NumberFormatPart[] {\n this.#formatterParts ??= this.#defaultFormatter.formatToParts(this.#amount);\n return this.#formatterParts;\n }\n\n toString(): string {\n return this.localizedString;\n }\n}\n\nclass FormattedMoneyRangeValue implements FormattedMoneyRange {\n readonly localizedString: string;\n readonly min: MoneyV2;\n readonly max: MoneyV2;\n readonly currencyCode: string;\n\n constructor(range: readonly MoneyV2[], options: FormatMoneyOptions) {\n if (range.length === 0) {\n throw new Error(\"formatMoney: money range must contain at least one value\");\n }\n\n const entries = sortMoneyRangeEntries(\n range.map((money) => ({\n money,\n amount: parseAmount(money),\n currencyCode: normalizeCurrencyCode(money.currencyCode),\n })),\n );\n\n const [first] = entries;\n if (!first) {\n throw new Error(\"formatMoney: money range must contain at least one value\");\n }\n\n const mismatched = entries.find((entry) => entry.currencyCode !== first.currencyCode);\n if (mismatched) {\n throw new Error(\n `formatMoney: range values must share one currency, received ${first.currencyCode} and ${mismatched.currencyCode}`,\n );\n }\n\n const last = entries[entries.length - 1] ?? first;\n this.min = first.money;\n this.max = last.money;\n this.currencyCode = first.currencyCode;\n\n if (first.amount === last.amount) {\n this.localizedString = new FormattedMoneyValue(first.money, {\n ...options,\n withoutTrailingZeros: options.withoutTrailingZeros ?? !hasExplicitFractionDigits(options),\n }).localizedString;\n return;\n }\n\n const supported = isWellFormedCurrencyCode(first.currencyCode);\n const formatter = getFormatter(\n options.locale,\n buildRangeOptions(\n first.currencyCode,\n supported,\n options,\n entries.map((entry) => entry.amount),\n ),\n );\n\n const formatted = formatRange(formatter, first.amount, last.amount);\n this.localizedString =\n supported || options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, first.currencyCode);\n }\n\n toString(): string {\n return this.localizedString;\n }\n}\n\n/**\n * Format a MoneyV2 value or a same-currency range of MoneyV2 values for display.\n *\n * Returns an object that stringifies to the locale-formatted price via\n * toString(). Structured fields on single prices are computed lazily when\n * accessed, so the common string path stays cheap.\n */\nexport function formatMoney(money: MoneyV2, options: FormatMoneyOptions): FormattedMoney;\nexport function formatMoney(\n money: readonly MoneyV2[],\n options: FormatMoneyOptions,\n): FormattedMoneyRange;\n\n// This is the implementation of the formatMoney function overloading the other two.\n// It checks if the input is a range and creates the appropriate object.\nexport function formatMoney(\n money: MoneyV2 | readonly MoneyV2[],\n options: FormatMoneyOptions,\n): FormattedMoney | FormattedMoneyRange {\n return isMoneyRange(money)\n ? new FormattedMoneyRangeValue(money, options)\n : new FormattedMoneyValue(money, options);\n}\n"],"mappings":"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"}
@@ -25,10 +25,7 @@ function createObservable(initialState) {
25
25
  prev: selector ? selector(currentState) : void 0
26
26
  };
27
27
  listeners.push(listener);
28
- let subscribed = true;
29
28
  return () => {
30
- if (!subscribed) return;
31
- subscribed = false;
32
29
  listeners = listeners.filter((l) => l !== listener);
33
30
  };
34
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"observable.mjs","names":[],"sources":["../../../src/core/observable.ts"],"sourcesContent":["interface Observable<T> {\n readonly state: T;\n subscribe(fn: (state: T) => void): () => void;\n subscribe<S>(\n fn: (slice: S) => void,\n selector: (state: T) => S,\n isEqual?: (a: S, b: S) => boolean,\n ): () => void;\n}\n\ntype InternalObservable<T> = Observable<T> & {\n setState(next: T | ((prev: T) => T)): void;\n};\n\ntype Listener<T> = {\n fn: (value: unknown) => void;\n selector?: (state: T) => unknown;\n isEqual: (a: unknown, b: unknown) => boolean;\n prev: unknown;\n};\n\nexport function createObservable<T>(initialState: T): InternalObservable<T> {\n let currentState = initialState;\n let listeners: Listener<T>[] = [];\n\n function setState(next: T | ((prev: T) => T)): void {\n const nextState = typeof next === \"function\" ? (next as (prev: T) => T)(currentState) : next;\n\n if (Object.is(currentState, nextState)) return;\n\n currentState = nextState;\n\n const snapshot = listeners.slice();\n for (const listener of snapshot) {\n if (!listeners.includes(listener)) continue;\n\n if (listener.selector) {\n const nextSlice = listener.selector(currentState);\n if (listener.isEqual(listener.prev, nextSlice)) continue;\n listener.prev = nextSlice;\n listener.fn(nextSlice);\n } else {\n listener.fn(currentState);\n }\n }\n }\n\n function subscribe(fn: (state: T) => void): () => void;\n function subscribe<S>(\n fn: (slice: S) => void,\n selector: (state: T) => S,\n isEqual?: (a: S, b: S) => boolean,\n ): () => void;\n function subscribe<S>(\n fn: (value: T | S) => void,\n selector?: (state: T) => S,\n isEqual?: (a: S, b: S) => boolean,\n ): () => void {\n const listener: Listener<T> = {\n fn: fn as (value: unknown) => void,\n selector,\n isEqual: (isEqual as ((a: unknown, b: unknown) => boolean) | undefined) ?? Object.is,\n prev: selector ? selector(currentState) : undefined,\n };\n\n listeners.push(listener);\n\n let subscribed = true;\n return () => {\n if (!subscribed) return;\n subscribed = false;\n listeners = listeners.filter((l) => l !== listener);\n };\n }\n\n return {\n get state() {\n return currentState;\n },\n subscribe,\n setState,\n };\n}\n"],"mappings":";AAqBA,SAAgB,iBAAoB,cAAwC;CAC1E,IAAI,eAAe;CACnB,IAAI,YAA2B,EAAE;CAEjC,SAAS,SAAS,MAAkC;EAClD,MAAM,YAAY,OAAO,SAAS,aAAc,KAAwB,aAAa,GAAG;AAExF,MAAI,OAAO,GAAG,cAAc,UAAU,CAAE;AAExC,iBAAe;EAEf,MAAM,WAAW,UAAU,OAAO;AAClC,OAAK,MAAM,YAAY,UAAU;AAC/B,OAAI,CAAC,UAAU,SAAS,SAAS,CAAE;AAEnC,OAAI,SAAS,UAAU;IACrB,MAAM,YAAY,SAAS,SAAS,aAAa;AACjD,QAAI,SAAS,QAAQ,SAAS,MAAM,UAAU,CAAE;AAChD,aAAS,OAAO;AAChB,aAAS,GAAG,UAAU;SAEtB,UAAS,GAAG,aAAa;;;CAW/B,SAAS,UACP,IACA,UACA,SACY;EACZ,MAAM,WAAwB;GACxB;GACJ;GACA,SAAU,WAAiE,OAAO;GAClF,MAAM,WAAW,SAAS,aAAa,GAAG;GAC3C;AAED,YAAU,KAAK,SAAS;EAExB,IAAI,aAAa;AACjB,eAAa;AACX,OAAI,CAAC,WAAY;AACjB,gBAAa;AACb,eAAY,UAAU,QAAQ,MAAM,MAAM,SAAS;;;AAIvD,QAAO;EACL,IAAI,QAAQ;AACV,UAAO;;EAET;EACA;EACD"}
1
+ {"version":3,"file":"observable.mjs","names":[],"sources":["../../../src/core/observable.ts"],"sourcesContent":["interface Observable<T> {\n readonly state: T;\n subscribe(fn: (state: T) => void): () => void;\n subscribe<S>(\n fn: (slice: S) => void,\n selector: (state: T) => S,\n isEqual?: (a: S, b: S) => boolean,\n ): () => void;\n}\n\ntype InternalObservable<T> = Observable<T> & {\n setState(next: T | ((prev: T) => T)): void;\n};\n\ntype Listener<T> = {\n fn: (value: unknown) => void;\n selector?: (state: T) => unknown;\n isEqual: (a: unknown, b: unknown) => boolean;\n prev: unknown;\n};\n\nexport function createObservable<T>(initialState: T): InternalObservable<T> {\n let currentState = initialState;\n let listeners: Listener<T>[] = [];\n\n function setState(next: T | ((prev: T) => T)): void {\n const nextState = typeof next === \"function\" ? (next as (prev: T) => T)(currentState) : next;\n\n if (Object.is(currentState, nextState)) return;\n\n currentState = nextState;\n\n const snapshot = listeners.slice();\n for (const listener of snapshot) {\n if (!listeners.includes(listener)) continue;\n\n if (listener.selector) {\n const nextSlice = listener.selector(currentState);\n if (listener.isEqual(listener.prev, nextSlice)) continue;\n listener.prev = nextSlice;\n listener.fn(nextSlice);\n } else {\n listener.fn(currentState);\n }\n }\n }\n\n function subscribe(fn: (state: T) => void): () => void;\n function subscribe<S>(\n fn: (slice: S) => void,\n selector: (state: T) => S,\n isEqual?: (a: S, b: S) => boolean,\n ): () => void;\n function subscribe<S>(\n fn: (value: T | S) => void,\n selector?: (state: T) => S,\n isEqual?: (a: S, b: S) => boolean,\n ): () => void {\n const listener: Listener<T> = {\n fn: fn as (value: unknown) => void,\n selector,\n isEqual: (isEqual as ((a: unknown, b: unknown) => boolean) | undefined) ?? Object.is,\n prev: selector ? selector(currentState) : undefined,\n };\n\n listeners.push(listener);\n\n return () => {\n listeners = listeners.filter((l) => l !== listener);\n };\n }\n\n return {\n get state() {\n return currentState;\n },\n subscribe,\n setState,\n };\n}\n"],"mappings":";AAqBA,SAAgB,iBAAoB,cAAwC;CAC1E,IAAI,eAAe;CACnB,IAAI,YAA2B,EAAE;CAEjC,SAAS,SAAS,MAAkC;EAClD,MAAM,YAAY,OAAO,SAAS,aAAc,KAAwB,aAAa,GAAG;AAExF,MAAI,OAAO,GAAG,cAAc,UAAU,CAAE;AAExC,iBAAe;EAEf,MAAM,WAAW,UAAU,OAAO;AAClC,OAAK,MAAM,YAAY,UAAU;AAC/B,OAAI,CAAC,UAAU,SAAS,SAAS,CAAE;AAEnC,OAAI,SAAS,UAAU;IACrB,MAAM,YAAY,SAAS,SAAS,aAAa;AACjD,QAAI,SAAS,QAAQ,SAAS,MAAM,UAAU,CAAE;AAChD,aAAS,OAAO;AAChB,aAAS,GAAG,UAAU;SAEtB,UAAS,GAAG,aAAa;;;CAW/B,SAAS,UACP,IACA,UACA,SACY;EACZ,MAAM,WAAwB;GACxB;GACJ;GACA,SAAU,WAAiE,OAAO;GAClF,MAAM,WAAW,SAAS,aAAa,GAAG;GAC3C;AAED,YAAU,KAAK,SAAS;AAExB,eAAa;AACX,eAAY,UAAU,QAAQ,MAAM,MAAM,SAAS;;;AAIvD,QAAO;EACL,IAAI,QAAQ;AACV,UAAO;;EAET;EACA;EACD"}
@@ -25,6 +25,7 @@ type PredictiveSearchFormRegister = {
25
25
  declare function createPredictiveSearchFormRegister(): PredictiveSearchFormRegister;
26
26
  /** Returns progressive-enhancement attributes for a predictive search form. */
27
27
  declare function getPredictiveSearchFormAttributes(action?: string): PredictiveSearchFormAttributes;
28
+ /** Reads the search term from form data submitted by a predictive search form. Returns an empty string when the `"q"` field is absent or not a string. */
28
29
  declare function readPredictiveSearchFormTerm(formData: FormData): string;
29
30
  //#endregion
30
31
  export { PredictiveSearchFormAttributes, PredictiveSearchFormRegister, PredictiveSearchQueryInputAttributes, createPredictiveSearchFormRegister, getPredictiveSearchFormAttributes, readPredictiveSearchFormTerm };
@@ -21,6 +21,7 @@ function getPredictiveSearchFormAttributes(action = "/search") {
21
21
  role: "search"
22
22
  };
23
23
  }
24
+ /** Reads the search term from form data submitted by a predictive search form. Returns an empty string when the `"q"` field is absent or not a string. */
24
25
  function readPredictiveSearchFormTerm(formData) {
25
26
  const value = formData.get("q");
26
27
  return typeof value === "string" ? value : "";
@@ -1 +1 @@
1
- {"version":3,"file":"form.mjs","names":[],"sources":["../../../../src/core/predictive-search/form.ts"],"sourcesContent":["import { PREDICTIVE_SEARCH_QUERY_PARAM } from \"./constants\";\n\n/** Props returned by {@link PredictiveSearchFormRegister} for the query input. */\nexport interface PredictiveSearchQueryInputAttributes {\n name: \"q\";\n type: \"search\";\n autoComplete: \"off\";\n autoCapitalize: \"off\";\n spellCheck: false;\n}\n\n/** Props returned by {@link getPredictiveSearchFormAttributes} for the search form. */\nexport interface PredictiveSearchFormAttributes {\n action: string;\n method: \"get\";\n role: \"search\";\n}\n\n/**\n * Register function returned by predictive search form bindings.\n *\n * Predictive search forms only register the Storefront API query input.\n */\nexport type PredictiveSearchFormRegister = {\n (field: \"query\"): PredictiveSearchQueryInputAttributes;\n};\n\n/** Creates a {@link PredictiveSearchFormRegister} for framework-neutral form fields. */\nexport function createPredictiveSearchFormRegister(): PredictiveSearchFormRegister {\n return registerPredictiveSearchFormField;\n}\n\nfunction registerPredictiveSearchFormField(field: string): PredictiveSearchQueryInputAttributes {\n if (field !== \"query\") {\n throw new Error(`Unknown predictive search form field: \"${field}\".`);\n }\n\n return {\n name: \"q\",\n type: \"search\",\n autoComplete: \"off\",\n autoCapitalize: \"off\",\n spellCheck: false,\n };\n}\n\n/** Returns progressive-enhancement attributes for a predictive search form. */\nexport function getPredictiveSearchFormAttributes(\n action: string = \"/search\",\n): PredictiveSearchFormAttributes {\n return {\n action,\n method: \"get\",\n role: \"search\",\n };\n}\n\nexport function readPredictiveSearchFormTerm(formData: FormData): string {\n const value = formData.get(PREDICTIVE_SEARCH_QUERY_PARAM);\n return typeof value === \"string\" ? value : \"\";\n}\n"],"mappings":";;AA4BA,SAAgB,qCAAmE;AACjF,QAAO;;AAGT,SAAS,kCAAkC,OAAqD;AAC9F,KAAI,UAAU,QACZ,OAAM,IAAI,MAAM,0CAA0C,MAAM,IAAI;AAGtE,QAAO;EACL,MAAM;EACN,MAAM;EACN,cAAc;EACd,gBAAgB;EAChB,YAAY;EACb;;;AAIH,SAAgB,kCACd,SAAiB,WACe;AAChC,QAAO;EACL;EACA,QAAQ;EACR,MAAM;EACP;;AAGH,SAAgB,6BAA6B,UAA4B;CACvE,MAAM,QAAQ,SAAS,QAAkC;AACzD,QAAO,OAAO,UAAU,WAAW,QAAQ"}
1
+ {"version":3,"file":"form.mjs","names":[],"sources":["../../../../src/core/predictive-search/form.ts"],"sourcesContent":["import { PREDICTIVE_SEARCH_QUERY_PARAM } from \"./constants\";\n\n/** Props returned by {@link PredictiveSearchFormRegister} for the query input. */\nexport interface PredictiveSearchQueryInputAttributes {\n name: \"q\";\n type: \"search\";\n autoComplete: \"off\";\n autoCapitalize: \"off\";\n spellCheck: false;\n}\n\n/** Props returned by {@link getPredictiveSearchFormAttributes} for the search form. */\nexport interface PredictiveSearchFormAttributes {\n action: string;\n method: \"get\";\n role: \"search\";\n}\n\n/**\n * Register function returned by predictive search form bindings.\n *\n * Predictive search forms only register the Storefront API query input.\n */\nexport type PredictiveSearchFormRegister = {\n (field: \"query\"): PredictiveSearchQueryInputAttributes;\n};\n\n/** Creates a {@link PredictiveSearchFormRegister} for framework-neutral form fields. */\nexport function createPredictiveSearchFormRegister(): PredictiveSearchFormRegister {\n return registerPredictiveSearchFormField;\n}\n\nfunction registerPredictiveSearchFormField(field: string): PredictiveSearchQueryInputAttributes {\n if (field !== \"query\") {\n throw new Error(`Unknown predictive search form field: \"${field}\".`);\n }\n\n return {\n name: \"q\",\n type: \"search\",\n autoComplete: \"off\",\n autoCapitalize: \"off\",\n spellCheck: false,\n };\n}\n\n/** Returns progressive-enhancement attributes for a predictive search form. */\nexport function getPredictiveSearchFormAttributes(\n action: string = \"/search\",\n): PredictiveSearchFormAttributes {\n return {\n action,\n method: \"get\",\n role: \"search\",\n };\n}\n\n/** Reads the search term from form data submitted by a predictive search form. Returns an empty string when the `\"q\"` field is absent or not a string. */\nexport function readPredictiveSearchFormTerm(formData: FormData): string {\n const value = formData.get(PREDICTIVE_SEARCH_QUERY_PARAM);\n return typeof value === \"string\" ? value : \"\";\n}\n"],"mappings":";;AA4BA,SAAgB,qCAAmE;AACjF,QAAO;;AAGT,SAAS,kCAAkC,OAAqD;AAC9F,KAAI,UAAU,QACZ,OAAM,IAAI,MAAM,0CAA0C,MAAM,IAAI;AAGtE,QAAO;EACL,MAAM;EACN,MAAM;EACN,cAAc;EACd,gBAAgB;EAChB,YAAY;EACb;;;AAIH,SAAgB,kCACd,SAAiB,WACe;AAChC,QAAO;EACL;EACA,QAAQ;EACR,MAAM;EACP;;;AAIH,SAAgB,6BAA6B,UAA4B;CACvE,MAAM,QAAQ,SAAS,QAAkC;AACzD,QAAO,OAAO,UAAU,WAAW,QAAQ"}
@@ -2,5 +2,5 @@ import { PredictiveSearchFormAttributes, PredictiveSearchFormRegister, Predictiv
2
2
  import { CreatePredictiveSearchQueriesOptions, PredictiveSearchFragments, makePredictiveSearchQueries, predictiveSearchQueries } from "./queries.mjs";
3
3
  import { PredictiveSearchData, PredictiveSearchDataForOptions, QueryPredictiveSearchOptions, queryPredictiveSearch } from "./search.mjs";
4
4
  import { CreatePredictiveSearchServerHandlersOptions, createPredictiveSearchServerHandlers } from "./server-handlers.mjs";
5
- import { CreatePredictiveSearchStoreOptions, PredictiveSearchState, PredictiveSearchStatus, PredictiveSearchStore, createPredictiveSearchStore } from "./store.mjs";
5
+ import { CreatePredictiveSearchStoreOptions, PredictiveSearchActions, PredictiveSearchState, PredictiveSearchStatus, PredictiveSearchStore, createPredictiveSearchStore } from "./store.mjs";
6
6
  import { PredictiveSearchArticleItem, PredictiveSearchCollectionItem, PredictiveSearchItem, PredictiveSearchItemUrlOptions, PredictiveSearchPageItem, PredictiveSearchProductItem, PredictiveSearchQueryItem, PredictiveSearchQueryItemUrlOptions, PredictiveSearchResourceItem, getPredictiveSearchItemUrl, getSearchResultUrl } from "./url.mjs";