@shopify/hydrogen 2026.10.0-preview.4 → 2026.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. package/dist/cli/index.mjs +23 -12
  2. package/dist/client/client.d.mts +18 -15
  3. package/dist/client/client.mjs +8 -3
  4. package/dist/client/client.mjs.map +1 -1
  5. package/dist/client/errors.d.mts +35 -13
  6. package/dist/client/errors.mjs +23 -13
  7. package/dist/client/errors.mjs.map +1 -1
  8. package/dist/client/types.d.mts +140 -90
  9. package/dist/core/analytics/cart-tracker.d.mts +19 -10
  10. package/dist/core/analytics/cart-tracker.mjs +18 -10
  11. package/dist/core/analytics/cart-tracker.mjs.map +1 -1
  12. package/dist/core/analytics/events.d.mts +17 -11
  13. package/dist/core/analytics/events.mjs +13 -4
  14. package/dist/core/analytics/events.mjs.map +1 -1
  15. package/dist/core/analytics/types.d.mts +207 -118
  16. package/dist/core/analytics/utils/flatten-connection.d.mts +11 -9
  17. package/dist/core/analytics/utils/flatten-connection.mjs +7 -6
  18. package/dist/core/analytics/utils/flatten-connection.mjs.map +1 -1
  19. package/dist/core/cache/fetch-with-cache.d.mts +24 -4
  20. package/dist/core/cache/fetch-with-cache.mjs +17 -2
  21. package/dist/core/cache/fetch-with-cache.mjs.map +1 -1
  22. package/dist/core/cache/key.d.mts +2 -0
  23. package/dist/core/cache/key.mjs.map +1 -1
  24. package/dist/core/cache/run-with-cache.d.mts +56 -8
  25. package/dist/core/cache/run-with-cache.mjs +17 -6
  26. package/dist/core/cache/run-with-cache.mjs.map +1 -1
  27. package/dist/core/cache/store.d.mts +8 -6
  28. package/dist/core/cache/store.mjs.map +1 -1
  29. package/dist/core/cache/strategies.d.mts +27 -2
  30. package/dist/core/cache/strategies.mjs +6 -0
  31. package/dist/core/cache/strategies.mjs.map +1 -1
  32. package/dist/core/cart/actions.d.mts +37 -30
  33. package/dist/core/cart/actions.mjs +21 -9
  34. package/dist/core/cart/actions.mjs.map +1 -1
  35. package/dist/core/cart/attach-quantity-input.d.mts +8 -7
  36. package/dist/core/cart/attach-quantity-input.mjs +8 -7
  37. package/dist/core/cart/attach-quantity-input.mjs.map +1 -1
  38. package/dist/core/cart/buyer-identity-sync.d.mts +7 -6
  39. package/dist/core/cart/buyer-identity-sync.mjs.map +1 -1
  40. package/dist/core/cart/cart.d.mts +59 -46
  41. package/dist/core/cart/cart.mjs +46 -29
  42. package/dist/core/cart/cart.mjs.map +1 -1
  43. package/dist/core/cart/cookie.d.mts +8 -4
  44. package/dist/core/cart/cookie.mjs +8 -4
  45. package/dist/core/cart/cookie.mjs.map +1 -1
  46. package/dist/core/cart/form.d.mts +34 -20
  47. package/dist/core/cart/form.mjs +6 -4
  48. package/dist/core/cart/form.mjs.map +1 -1
  49. package/dist/core/cart/get-cart.d.mts +27 -15
  50. package/dist/core/cart/get-cart.mjs +13 -8
  51. package/dist/core/cart/get-cart.mjs.map +1 -1
  52. package/dist/core/cart/quantity.d.mts +16 -5
  53. package/dist/core/cart/quantity.mjs +16 -5
  54. package/dist/core/cart/quantity.mjs.map +1 -1
  55. package/dist/core/cart/queries.d.mts +25 -19
  56. package/dist/core/cart/queries.mjs +15 -1
  57. package/dist/core/cart/queries.mjs.map +1 -1
  58. package/dist/core/cart/server-handlers.d.mts +82 -45
  59. package/dist/core/cart/server-handlers.mjs +4 -0
  60. package/dist/core/cart/server-handlers.mjs.map +1 -1
  61. package/dist/core/cart/state.d.mts +110 -64
  62. package/dist/core/cart/state.mjs +24 -6
  63. package/dist/core/cart/state.mjs.map +1 -1
  64. package/dist/core/collection/collection.d.mts +52 -53
  65. package/dist/core/collection/collection.mjs +5 -3
  66. package/dist/core/collection/collection.mjs.map +1 -1
  67. package/dist/core/collection/reconciler.d.mts +23 -16
  68. package/dist/core/collection/reconciler.mjs +8 -4
  69. package/dist/core/collection/reconciler.mjs.map +1 -1
  70. package/dist/core/collection/state.d.mts +31 -26
  71. package/dist/core/collection/state.mjs +4 -3
  72. package/dist/core/collection/state.mjs.map +1 -1
  73. package/dist/core/collection/url.d.mts +70 -29
  74. package/dist/core/collection/url.mjs +63 -25
  75. package/dist/core/collection/url.mjs.map +1 -1
  76. package/dist/core/constants.d.mts +1 -0
  77. package/dist/core/constants.mjs +1 -0
  78. package/dist/core/constants.mjs.map +1 -1
  79. package/dist/core/logging/logging.d.mts +14 -8
  80. package/dist/core/logging/logging.mjs +15 -10
  81. package/dist/core/logging/logging.mjs.map +1 -1
  82. package/dist/core/logging/types.d.mts +28 -11
  83. package/dist/core/logging/types.mjs.map +1 -1
  84. package/dist/core/money/format.d.mts +6 -4
  85. package/dist/core/money/format.mjs +19 -9
  86. package/dist/core/money/format.mjs.map +1 -1
  87. package/dist/core/money/types.d.mts +41 -25
  88. package/dist/core/predictive-search/form.d.mts +37 -7
  89. package/dist/core/predictive-search/form.mjs +21 -3
  90. package/dist/core/predictive-search/form.mjs.map +1 -1
  91. package/dist/core/predictive-search/queries.d.mts +18 -30
  92. package/dist/core/predictive-search/queries.mjs +7 -3
  93. package/dist/core/predictive-search/queries.mjs.map +1 -1
  94. package/dist/core/predictive-search/search.d.mts +25 -27
  95. package/dist/core/predictive-search/search.mjs +20 -19
  96. package/dist/core/predictive-search/search.mjs.map +1 -1
  97. package/dist/core/predictive-search/server-handlers.d.mts +25 -26
  98. package/dist/core/predictive-search/server-handlers.mjs +4 -0
  99. package/dist/core/predictive-search/server-handlers.mjs.map +1 -1
  100. package/dist/core/predictive-search/store.d.mts +65 -24
  101. package/dist/core/predictive-search/store.mjs +12 -1
  102. package/dist/core/predictive-search/store.mjs.map +1 -1
  103. package/dist/core/predictive-search/url.d.mts +36 -37
  104. package/dist/core/predictive-search/url.mjs +14 -6
  105. package/dist/core/predictive-search/url.mjs.map +1 -1
  106. package/dist/core/product/form.d.mts +63 -16
  107. package/dist/core/product/form.mjs +8 -2
  108. package/dist/core/product/form.mjs.map +1 -1
  109. package/dist/core/product/options.d.mts +8 -10
  110. package/dist/core/product/options.mjs +8 -10
  111. package/dist/core/product/options.mjs.map +1 -1
  112. package/dist/core/product/product-form.d.mts +77 -29
  113. package/dist/core/product/product-form.mjs +32 -10
  114. package/dist/core/product/product-form.mjs.map +1 -1
  115. package/dist/core/product/state.d.mts +55 -32
  116. package/dist/core/product/url.d.mts +19 -13
  117. package/dist/core/product/url.mjs +10 -10
  118. package/dist/core/product/url.mjs.map +1 -1
  119. package/dist/core/request-context.d.mts +77 -19
  120. package/dist/core/request-context.mjs +5 -1
  121. package/dist/core/request-context.mjs.map +1 -1
  122. package/dist/core/request-routing/handle-shopify-redirects.d.mts +12 -4
  123. package/dist/core/request-routing/handle-shopify-redirects.mjs +9 -2
  124. package/dist/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
  125. package/dist/core/request-routing/handle-shopify-routes.d.mts +5 -5
  126. package/dist/core/request-routing/handle-shopify-routes.mjs +7 -5
  127. package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  128. package/dist/core/request-routing/interceptors/proxy.mjs +79 -43
  129. package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
  130. package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
  131. package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
  132. package/dist/core/request-routing/interceptors/url-redirects.mjs +6 -1
  133. package/dist/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
  134. package/dist/core/request-routing/interceptors/well-known.mjs +1 -1
  135. package/dist/core/request-routing/interceptors/well-known.mjs.map +1 -1
  136. package/dist/core/request-routing/registered-routes.d.mts +12 -0
  137. package/dist/core/request-routing/registered-routes.mjs +12 -0
  138. package/dist/core/request-routing/registered-routes.mjs.map +1 -1
  139. package/dist/core/request-routing/route-types.d.mts +43 -18
  140. package/dist/core/shop-pay/shop-pay.d.mts +71 -35
  141. package/dist/core/shop-pay/shop-pay.mjs +36 -10
  142. package/dist/core/shop-pay/shop-pay.mjs.map +1 -1
  143. package/dist/core/shopify-scripts/index.d.mts +15 -5
  144. package/dist/core/shopify-scripts/index.mjs +15 -5
  145. package/dist/core/shopify-scripts/index.mjs.map +1 -1
  146. package/dist/core/shopify-scripts/initialize.d.mts +12 -4
  147. package/dist/core/shopify-scripts/initialize.mjs +12 -4
  148. package/dist/core/shopify-scripts/initialize.mjs.map +1 -1
  149. package/dist/core/shopify-scripts/types.d.mts +58 -25
  150. package/dist/core/standard-routes/build.d.mts +11 -12
  151. package/dist/core/standard-routes/build.mjs +11 -12
  152. package/dist/core/standard-routes/build.mjs.map +1 -1
  153. package/dist/core/standard-routes/types.d.mts +30 -24
  154. package/dist/core/url.mjs +2 -1
  155. package/dist/core/url.mjs.map +1 -1
  156. package/dist/customer-account/client.d.mts +36 -69
  157. package/dist/customer-account/client.mjs +7 -8
  158. package/dist/customer-account/client.mjs.map +1 -1
  159. package/dist/customer-account/errors.d.mts +45 -36
  160. package/dist/customer-account/errors.mjs +21 -36
  161. package/dist/customer-account/errors.mjs.map +1 -1
  162. package/dist/customer-account/graphql.d.mts +11 -23
  163. package/dist/customer-account/graphql.mjs +3 -9
  164. package/dist/customer-account/graphql.mjs.map +1 -1
  165. package/dist/customer-account/session.d.mts +130 -100
  166. package/dist/customer-account/session.mjs +32 -10
  167. package/dist/customer-account/session.mjs.map +1 -1
  168. package/dist/customer-account/type-resolver.d.mts +7 -8
  169. package/dist/development/client/client.d.mts +18 -15
  170. package/dist/development/client/client.mjs +8 -3
  171. package/dist/development/client/client.mjs.map +1 -1
  172. package/dist/development/client/errors.d.mts +35 -13
  173. package/dist/development/client/errors.mjs +23 -13
  174. package/dist/development/client/errors.mjs.map +1 -1
  175. package/dist/development/client/types.d.mts +140 -90
  176. package/dist/development/core/analytics/cart-tracker.d.mts +19 -10
  177. package/dist/development/core/analytics/cart-tracker.mjs +18 -10
  178. package/dist/development/core/analytics/cart-tracker.mjs.map +1 -1
  179. package/dist/development/core/analytics/events.d.mts +17 -11
  180. package/dist/development/core/analytics/events.mjs +13 -4
  181. package/dist/development/core/analytics/events.mjs.map +1 -1
  182. package/dist/development/core/analytics/types.d.mts +207 -118
  183. package/dist/development/core/analytics/utils/flatten-connection.d.mts +11 -9
  184. package/dist/development/core/analytics/utils/flatten-connection.mjs +7 -6
  185. package/dist/development/core/analytics/utils/flatten-connection.mjs.map +1 -1
  186. package/dist/development/core/cache/fetch-with-cache.d.mts +24 -4
  187. package/dist/development/core/cache/fetch-with-cache.mjs +17 -2
  188. package/dist/development/core/cache/fetch-with-cache.mjs.map +1 -1
  189. package/dist/development/core/cache/key.d.mts +2 -0
  190. package/dist/development/core/cache/key.mjs.map +1 -1
  191. package/dist/development/core/cache/run-with-cache.d.mts +56 -8
  192. package/dist/development/core/cache/run-with-cache.mjs +17 -6
  193. package/dist/development/core/cache/run-with-cache.mjs.map +1 -1
  194. package/dist/development/core/cache/store.d.mts +8 -6
  195. package/dist/development/core/cache/store.mjs.map +1 -1
  196. package/dist/development/core/cache/strategies.d.mts +27 -2
  197. package/dist/development/core/cache/strategies.mjs +6 -0
  198. package/dist/development/core/cache/strategies.mjs.map +1 -1
  199. package/dist/development/core/cart/actions.d.mts +37 -30
  200. package/dist/development/core/cart/actions.mjs +21 -9
  201. package/dist/development/core/cart/actions.mjs.map +1 -1
  202. package/dist/development/core/cart/attach-quantity-input.d.mts +8 -7
  203. package/dist/development/core/cart/attach-quantity-input.mjs +8 -7
  204. package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -1
  205. package/dist/development/core/cart/buyer-identity-sync.d.mts +6 -5
  206. package/dist/development/core/cart/buyer-identity-sync.mjs.map +1 -1
  207. package/dist/development/core/cart/cart.d.mts +59 -46
  208. package/dist/development/core/cart/cart.mjs +46 -29
  209. package/dist/development/core/cart/cart.mjs.map +1 -1
  210. package/dist/development/core/cart/cookie.d.mts +8 -4
  211. package/dist/development/core/cart/cookie.mjs +8 -4
  212. package/dist/development/core/cart/cookie.mjs.map +1 -1
  213. package/dist/development/core/cart/form.d.mts +34 -20
  214. package/dist/development/core/cart/form.mjs +6 -4
  215. package/dist/development/core/cart/form.mjs.map +1 -1
  216. package/dist/development/core/cart/get-cart.d.mts +27 -15
  217. package/dist/development/core/cart/get-cart.mjs +13 -8
  218. package/dist/development/core/cart/get-cart.mjs.map +1 -1
  219. package/dist/development/core/cart/quantity.d.mts +16 -5
  220. package/dist/development/core/cart/quantity.mjs +16 -5
  221. package/dist/development/core/cart/quantity.mjs.map +1 -1
  222. package/dist/development/core/cart/queries.d.mts +25 -19
  223. package/dist/development/core/cart/queries.mjs +15 -1
  224. package/dist/development/core/cart/queries.mjs.map +1 -1
  225. package/dist/development/core/cart/server-handlers.d.mts +82 -45
  226. package/dist/development/core/cart/server-handlers.mjs +4 -0
  227. package/dist/development/core/cart/server-handlers.mjs.map +1 -1
  228. package/dist/development/core/cart/state.d.mts +110 -64
  229. package/dist/development/core/cart/state.mjs +24 -6
  230. package/dist/development/core/cart/state.mjs.map +1 -1
  231. package/dist/development/core/collection/collection.d.mts +52 -53
  232. package/dist/development/core/collection/collection.mjs +5 -3
  233. package/dist/development/core/collection/collection.mjs.map +1 -1
  234. package/dist/development/core/collection/reconciler.d.mts +23 -16
  235. package/dist/development/core/collection/reconciler.mjs +8 -4
  236. package/dist/development/core/collection/reconciler.mjs.map +1 -1
  237. package/dist/development/core/collection/state.d.mts +31 -26
  238. package/dist/development/core/collection/state.mjs +4 -3
  239. package/dist/development/core/collection/state.mjs.map +1 -1
  240. package/dist/development/core/collection/url.d.mts +70 -29
  241. package/dist/development/core/collection/url.mjs +63 -25
  242. package/dist/development/core/collection/url.mjs.map +1 -1
  243. package/dist/development/core/constants.mjs.map +1 -1
  244. package/dist/development/core/logging/logging.d.mts +14 -8
  245. package/dist/development/core/logging/logging.mjs +15 -10
  246. package/dist/development/core/logging/logging.mjs.map +1 -1
  247. package/dist/development/core/logging/types.d.mts +28 -11
  248. package/dist/development/core/logging/types.mjs.map +1 -1
  249. package/dist/development/core/money/format.d.mts +6 -4
  250. package/dist/development/core/money/format.mjs +19 -9
  251. package/dist/development/core/money/format.mjs.map +1 -1
  252. package/dist/development/core/money/types.d.mts +41 -25
  253. package/dist/development/core/predictive-search/form.d.mts +37 -7
  254. package/dist/development/core/predictive-search/form.mjs +21 -3
  255. package/dist/development/core/predictive-search/form.mjs.map +1 -1
  256. package/dist/development/core/predictive-search/queries.d.mts +18 -30
  257. package/dist/development/core/predictive-search/queries.mjs +7 -3
  258. package/dist/development/core/predictive-search/queries.mjs.map +1 -1
  259. package/dist/development/core/predictive-search/search.d.mts +25 -27
  260. package/dist/development/core/predictive-search/search.mjs +20 -19
  261. package/dist/development/core/predictive-search/search.mjs.map +1 -1
  262. package/dist/development/core/predictive-search/server-handlers.d.mts +25 -26
  263. package/dist/development/core/predictive-search/server-handlers.mjs +4 -0
  264. package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -1
  265. package/dist/development/core/predictive-search/store.d.mts +65 -24
  266. package/dist/development/core/predictive-search/store.mjs +12 -1
  267. package/dist/development/core/predictive-search/store.mjs.map +1 -1
  268. package/dist/development/core/predictive-search/url.d.mts +36 -37
  269. package/dist/development/core/predictive-search/url.mjs +14 -6
  270. package/dist/development/core/predictive-search/url.mjs.map +1 -1
  271. package/dist/development/core/product/form.d.mts +63 -16
  272. package/dist/development/core/product/form.mjs +8 -2
  273. package/dist/development/core/product/form.mjs.map +1 -1
  274. package/dist/development/core/product/options.d.mts +8 -10
  275. package/dist/development/core/product/options.mjs +8 -10
  276. package/dist/development/core/product/options.mjs.map +1 -1
  277. package/dist/development/core/product/product-form.d.mts +77 -29
  278. package/dist/development/core/product/product-form.mjs +32 -10
  279. package/dist/development/core/product/product-form.mjs.map +1 -1
  280. package/dist/development/core/product/state.d.mts +55 -32
  281. package/dist/development/core/product/url.d.mts +19 -13
  282. package/dist/development/core/product/url.mjs +10 -10
  283. package/dist/development/core/product/url.mjs.map +1 -1
  284. package/dist/development/core/request-context.d.mts +77 -19
  285. package/dist/development/core/request-context.mjs +5 -1
  286. package/dist/development/core/request-context.mjs.map +1 -1
  287. package/dist/development/core/request-routing/handle-shopify-redirects.d.mts +12 -4
  288. package/dist/development/core/request-routing/handle-shopify-redirects.mjs +9 -2
  289. package/dist/development/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
  290. package/dist/development/core/request-routing/handle-shopify-routes.development.d.mts +9 -1
  291. package/dist/development/core/request-routing/handle-shopify-routes.development.mjs +7 -0
  292. package/dist/development/core/request-routing/handle-shopify-routes.development.mjs.map +1 -1
  293. package/dist/development/core/request-routing/handle-shopify-routes.mjs +7 -5
  294. package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  295. package/dist/development/core/request-routing/interceptors/proxy.mjs +79 -43
  296. package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
  297. package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
  298. package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
  299. package/dist/development/core/request-routing/interceptors/url-redirects.mjs +6 -1
  300. package/dist/development/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
  301. package/dist/development/core/request-routing/interceptors/well-known.mjs +1 -1
  302. package/dist/development/core/request-routing/interceptors/well-known.mjs.map +1 -1
  303. package/dist/development/core/request-routing/registered-routes.d.mts +12 -0
  304. package/dist/development/core/request-routing/registered-routes.mjs +12 -0
  305. package/dist/development/core/request-routing/registered-routes.mjs.map +1 -1
  306. package/dist/development/core/request-routing/route-types.d.mts +43 -18
  307. package/dist/development/core/shop-pay/shop-pay.d.mts +71 -35
  308. package/dist/development/core/shop-pay/shop-pay.mjs +36 -10
  309. package/dist/development/core/shop-pay/shop-pay.mjs.map +1 -1
  310. package/dist/development/core/shopify-scripts/index.d.mts +15 -5
  311. package/dist/development/core/shopify-scripts/index.mjs +15 -5
  312. package/dist/development/core/shopify-scripts/index.mjs.map +1 -1
  313. package/dist/development/core/shopify-scripts/initialize.d.mts +12 -4
  314. package/dist/development/core/shopify-scripts/initialize.mjs +12 -4
  315. package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -1
  316. package/dist/development/core/shopify-scripts/types.d.mts +58 -25
  317. package/dist/development/core/standard-routes/build.d.mts +11 -12
  318. package/dist/development/core/standard-routes/build.mjs +11 -12
  319. package/dist/development/core/standard-routes/build.mjs.map +1 -1
  320. package/dist/development/core/standard-routes/types.d.mts +30 -24
  321. package/dist/development/core/url.mjs +2 -1
  322. package/dist/development/core/url.mjs.map +1 -1
  323. package/dist/development/customer-account/session.d.mts +45 -54
  324. package/dist/development/customer-account/session.mjs +20 -4
  325. package/dist/development/customer-account/session.mjs.map +1 -1
  326. package/dist/development/globals.d.mts +20 -11
  327. package/dist/development/graphql/generated/storefront-api-types.d.mts +127 -1
  328. package/dist/development/graphql/graphql.d.mts +17 -16
  329. package/dist/development/graphql/graphql.mjs +7 -8
  330. package/dist/development/graphql/graphql.mjs.map +1 -1
  331. package/dist/development/graphql/type-resolver.d.mts +15 -8
  332. package/dist/development/react/cart.d.mts +78 -32
  333. package/dist/development/react/cart.mjs +56 -29
  334. package/dist/development/react/cart.mjs.map +1 -1
  335. package/dist/development/react/collection.d.mts +40 -22
  336. package/dist/development/react/collection.mjs +27 -7
  337. package/dist/development/react/collection.mjs.map +1 -1
  338. package/dist/development/react/predictive-search.d.mts +65 -50
  339. package/dist/development/react/predictive-search.mjs +23 -21
  340. package/dist/development/react/predictive-search.mjs.map +1 -1
  341. package/dist/development/react/product.d.mts +45 -16
  342. package/dist/development/react/product.mjs +13 -11
  343. package/dist/development/react/product.mjs.map +1 -1
  344. package/dist/development/react/shop-pay.d.mts +15 -0
  345. package/dist/development/react/shop-pay.mjs +9 -0
  346. package/dist/development/react/shop-pay.mjs.map +1 -1
  347. package/dist/development/react/shopify-scripts.d.mts +23 -2
  348. package/dist/development/react/shopify-scripts.mjs +9 -0
  349. package/dist/development/react/shopify-scripts.mjs.map +1 -1
  350. package/dist/development/vue/cart.d.mts +28 -23
  351. package/dist/development/vue/cart.mjs +42 -25
  352. package/dist/development/vue/cart.mjs.map +1 -1
  353. package/dist/development/vue/collection.d.mts +20 -12
  354. package/dist/development/vue/collection.mjs +15 -7
  355. package/dist/development/vue/collection.mjs.map +1 -1
  356. package/dist/development/vue/index.d.mts +2 -2
  357. package/dist/development/vue/index.mjs +2 -2
  358. package/dist/development/vue/predictive-search.d.mts +31 -36
  359. package/dist/development/vue/predictive-search.mjs +16 -22
  360. package/dist/development/vue/predictive-search.mjs.map +1 -1
  361. package/dist/development/vue/product.d.mts +16 -15
  362. package/dist/development/vue/product.mjs +11 -11
  363. package/dist/development/vue/product.mjs.map +1 -1
  364. package/dist/development/vue/shop-pay.d.mts +14 -17
  365. package/dist/development/vue/shop-pay.mjs +8 -0
  366. package/dist/development/vue/shop-pay.mjs.map +1 -1
  367. package/dist/development/vue/shopify-scripts.d.mts +11 -0
  368. package/dist/development/vue/shopify-scripts.mjs +6 -0
  369. package/dist/development/vue/shopify-scripts.mjs.map +1 -1
  370. package/dist/globals.d.mts +20 -11
  371. package/dist/graphql/generated/storefront-api-types.d.mts +127 -1
  372. package/dist/graphql/graphql.d.mts +17 -16
  373. package/dist/graphql/graphql.mjs +7 -8
  374. package/dist/graphql/graphql.mjs.map +1 -1
  375. package/dist/graphql/type-resolver.d.mts +15 -8
  376. package/dist/react/cart.d.mts +78 -32
  377. package/dist/react/cart.mjs +56 -29
  378. package/dist/react/cart.mjs.map +1 -1
  379. package/dist/react/collection.d.mts +40 -22
  380. package/dist/react/collection.mjs +27 -7
  381. package/dist/react/collection.mjs.map +1 -1
  382. package/dist/react/predictive-search.d.mts +65 -50
  383. package/dist/react/predictive-search.mjs +23 -21
  384. package/dist/react/predictive-search.mjs.map +1 -1
  385. package/dist/react/product.d.mts +45 -16
  386. package/dist/react/product.mjs +13 -11
  387. package/dist/react/product.mjs.map +1 -1
  388. package/dist/react/shop-pay.d.mts +15 -0
  389. package/dist/react/shop-pay.mjs +9 -0
  390. package/dist/react/shop-pay.mjs.map +1 -1
  391. package/dist/react/shopify-scripts.d.mts +23 -2
  392. package/dist/react/shopify-scripts.mjs +9 -0
  393. package/dist/react/shopify-scripts.mjs.map +1 -1
  394. package/dist/ts-plugin/index.cjs +6 -0
  395. package/dist/ts-plugin/index.cjs.map +1 -1
  396. package/dist/ts-plugin/index.d.cts +6 -0
  397. package/dist/vite/local-https.d.mts +51 -17
  398. package/dist/vite/local-https.mjs +26 -5
  399. package/dist/vite/local-https.mjs.map +1 -1
  400. package/dist/vue/cart.d.mts +28 -23
  401. package/dist/vue/cart.mjs +42 -25
  402. package/dist/vue/cart.mjs.map +1 -1
  403. package/dist/vue/collection.d.mts +20 -12
  404. package/dist/vue/collection.mjs +15 -7
  405. package/dist/vue/collection.mjs.map +1 -1
  406. package/dist/vue/index.d.mts +2 -2
  407. package/dist/vue/index.mjs +2 -2
  408. package/dist/vue/predictive-search.d.mts +31 -36
  409. package/dist/vue/predictive-search.mjs +16 -22
  410. package/dist/vue/predictive-search.mjs.map +1 -1
  411. package/dist/vue/product.d.mts +16 -15
  412. package/dist/vue/product.mjs +11 -11
  413. package/dist/vue/product.mjs.map +1 -1
  414. package/dist/vue/shop-pay.d.mts +14 -17
  415. package/dist/vue/shop-pay.mjs +8 -0
  416. package/dist/vue/shop-pay.mjs.map +1 -1
  417. package/dist/vue/shopify-scripts.d.mts +11 -0
  418. package/dist/vue/shopify-scripts.mjs +6 -0
  419. package/dist/vue/shopify-scripts.mjs.map +1 -1
  420. package/package.json +3 -1
  421. package/skills/hydrogen-analytics/SKILL.md +1 -1
  422. package/skills/hydrogen-cart-ui/SKILL.md +28 -2
  423. package/skills/hydrogen-collection-browser/SKILL.md +9 -1
  424. package/skills/hydrogen-collection-browser/references/nextjs.md +1 -1
  425. package/skills/hydrogen-collection-browser/references/nuxt.md +1 -1
  426. package/skills/hydrogen-collection-browser/references/react.md +151 -32
  427. package/skills/hydrogen-customer-account/SKILL.md +2 -0
  428. package/skills/hydrogen-request-handlers/SKILL.md +21 -11
  429. package/skills/hydrogen-setup/references/analytics.md +14 -8
  430. package/skills/hydrogen-setup/steps/1-inspect-app.md +2 -2
  431. package/skills/hydrogen-setup/steps/6-product-detail-page.md +21 -11
  432. package/skills/hydrogen-storefront-client/SKILL.md +1 -1
  433. package/skills/hydrogen-storefront-client/references/caching.md +2 -0
  434. package/skills/hydrogen-variant-form/SKILL.md +25 -7
  435. package/skills/hydrogen-variant-form/references/nextjs.md +39 -4
  436. package/skills/hydrogen-variant-form/references/nuxt.md +83 -42
  437. package/skills/hydrogen-variant-form/references/react.md +45 -9
@@ -1 +1 @@
1
- {"version":3,"file":"store.mjs","names":[],"sources":["../../../src/core/predictive-search/store.ts"],"sourcesContent":["import type {\n PredictiveSearchLimitScope,\n PredictiveSearchType,\n SearchUnavailableProductsType,\n SearchableField,\n} from \"../../graphql/generated/storefront-api-types\";\nimport { createObservable } from \"../observable\";\nimport { PREDICTIVE_SEARCH_API_PATH, PREDICTIVE_SEARCH_QUERY_PARAM } from \"./constants\";\nimport { getEmptyPredictiveSearchResult, type PredictiveSearchData } from \"./search\";\n\n/** Default delay before predictive search sends a request after a term changes. */\nexport const DEFAULT_PREDICTIVE_SEARCH_DEBOUNCE_IN_MS = 150;\n\n/** Default minimum trimmed term length required before predictive search sends a request. */\nconst DEFAULT_PREDICTIVE_SEARCH_MIN_TERM_LENGTH = 1;\n\n/** Lifecycle state for the predictive search client store. */\nexport type PredictiveSearchStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\n/** Current predictive search snapshot exposed to UI bindings. */\nexport type PredictiveSearchState<TData extends PredictiveSearchData = PredictiveSearchData> = {\n /** Latest trimmed search term known to the store. */\n term: string;\n /** Request lifecycle state for the current term. */\n status: PredictiveSearchStatus;\n /** Predictive search data for the current term, or an empty result while idle. */\n result: TData;\n /** Error message for the latest failed request, or null when there is no active error. */\n error: string | null;\n};\n\n/** Options for creating a framework-neutral predictive search client store. */\nexport type CreatePredictiveSearchStoreOptions = {\n /**\n * Same-origin JSON endpoint used for browser predictive-search requests.\n *\n * The endpoint must return JSON matching the data payload from\n * `await predictiveSearchHandlers.get(...)`.\n */\n predictiveSearchEndpoint?: string;\n /** Delay before sending a request after the term changes. */\n debounceInMs?: number;\n /** Minimum trimmed term length required before searching. */\n minTermLength?: number;\n /** Fetch implementation, injectable for tests and non-browser runtimes. */\n fetch?: typeof globalThis.fetch;\n /** Maximum result count requested from the Storefront API. */\n limit?: number;\n /** Whether `limit` applies to each result type or all result types combined. */\n limitScope?: PredictiveSearchLimitScope;\n /** Storefront API result types to include in predictive search. */\n types?: PredictiveSearchType[];\n /** Storefront API fields to search. */\n searchableFields?: SearchableField[];\n /** Storefront API unavailable-product behavior. */\n unavailableProducts?: SearchUnavailableProductsType;\n};\n\n/** Framework-neutral predictive search store used by UI bindings. */\nexport type PredictiveSearchStore<TData extends PredictiveSearchData = PredictiveSearchData> = {\n /** Reactivates store updates after destroy(). */\n connect(): void;\n /** Returns the current predictive search state snapshot. */\n getState(): PredictiveSearchState<TData>;\n /** Subscribes to state changes and returns an unsubscribe callback. */\n subscribe(listener: (state: PredictiveSearchState<TData>) => void): () => void;\n /** Searches for a term using the configured debounce and request lifecycle. */\n search(term: string): Promise<void>;\n /** Cancels pending work and resets the store to an empty state. */\n clear(): void;\n /** Cancels pending work and pauses store updates until reconnect. */\n destroy(): void;\n};\n\n/** Search methods exposed by the predictive search store. */\nexport type PredictiveSearchActions = Pick<PredictiveSearchStore, \"search\" | \"clear\">;\n\ntype PredictiveSearchStoreContext<TData extends PredictiveSearchData> = {\n observable: ReturnType<typeof createObservable<PredictiveSearchState<TData>>>;\n // API route used to fetch predictive search results.\n predictiveSearchEndpoint: string;\n // Delay before firing a search request after the term changes.\n debounceInMs: number;\n // Minimum term length required before searching.\n minTermLength: number;\n // Fetch implementation, injectable for tests and non-browser runtimes.\n fetch: typeof globalThis.fetch;\n // Storefront API search options forwarded to the server handler.\n searchOptions: StoreSearchOptions;\n abortController: AbortController | null;\n // Active debounce timer waiting to start the next search.\n debounceTimer: ReturnType<typeof setTimeout> | null;\n // Monotonic counter used to ignore responses from older requests.\n requestId: number;\n // Resolver for the current debounced search promise.\n pendingResolve: (() => void) | null;\n // Prevents future work after the store has been torn down.\n destroyed: boolean;\n};\n\ntype StoreSearchOptions = Pick<\n CreatePredictiveSearchStoreOptions,\n \"limit\" | \"limitScope\" | \"types\" | \"searchableFields\" | \"unavailableProducts\"\n>;\n\nexport function createPredictiveSearchStore<\n TData extends PredictiveSearchData = PredictiveSearchData,\n>(options: CreatePredictiveSearchStoreOptions = {}): PredictiveSearchStore<TData> {\n // Prefer an injected fetch for tests and non-browser runtimes, then fall back to the ambient runtime fetch.\n const resolvedFetch = options.fetch ?? resolveGlobalFetch();\n if (typeof resolvedFetch !== \"function\") {\n throw new Error(\n \"No fetch function available. Pass a fetch option or ensure globalThis.fetch exists.\",\n );\n }\n\n const context: PredictiveSearchStoreContext<TData> = {\n observable: createObservable(createInitialState<TData>()),\n predictiveSearchEndpoint: options.predictiveSearchEndpoint ?? PREDICTIVE_SEARCH_API_PATH,\n debounceInMs: sanitizeDebounceInMs(options.debounceInMs),\n minTermLength: sanitizeMinTermLength(options.minTermLength),\n fetch: resolvedFetch,\n searchOptions: {\n limit: options.limit,\n limitScope: options.limitScope,\n types: options.types,\n searchableFields: options.searchableFields,\n unavailableProducts: options.unavailableProducts,\n },\n abortController: null,\n debounceTimer: null,\n requestId: 0,\n pendingResolve: null,\n destroyed: false,\n };\n\n return {\n connect: () => connect(context),\n getState: () => context.observable.state,\n subscribe: (listener) => context.observable.subscribe(listener),\n search: (term) => search(context, term),\n clear: () => clear(context),\n destroy: () => destroy(context),\n };\n}\n\nfunction connect<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.destroyed = false;\n}\n\nfunction resolveGlobalFetch(): typeof globalThis.fetch | undefined {\n if (typeof globalThis.fetch !== \"function\") return undefined;\n return globalThis.fetch.bind(globalThis);\n}\n\nfunction sanitizeDebounceInMs(debounceInMs: number | undefined): number {\n if (typeof debounceInMs !== \"number\") return DEFAULT_PREDICTIVE_SEARCH_DEBOUNCE_IN_MS;\n if (!Number.isFinite(debounceInMs)) return DEFAULT_PREDICTIVE_SEARCH_DEBOUNCE_IN_MS;\n if (debounceInMs < 0) return DEFAULT_PREDICTIVE_SEARCH_DEBOUNCE_IN_MS;\n return Math.trunc(debounceInMs);\n}\n\nfunction sanitizeMinTermLength(minTermLength: number | undefined): number {\n if (typeof minTermLength !== \"number\") return DEFAULT_PREDICTIVE_SEARCH_MIN_TERM_LENGTH;\n if (!Number.isFinite(minTermLength)) return DEFAULT_PREDICTIVE_SEARCH_MIN_TERM_LENGTH;\n if (minTermLength < 0) return DEFAULT_PREDICTIVE_SEARCH_MIN_TERM_LENGTH;\n return Math.trunc(minTermLength);\n}\n\nfunction createInitialState<TData extends PredictiveSearchData>(): PredictiveSearchState<TData> {\n return {\n term: \"\",\n status: \"idle\",\n result: getEmptyPredictiveSearchResult(\"\") as TData,\n error: null,\n };\n}\n\nfunction search<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n): Promise<void> {\n resolvePending(context);\n cancelScheduledWork(context);\n\n if (context.destroyed) return Promise.resolve();\n\n const trimmedTerm = term.trim();\n if (trimmedTerm.length < context.minTermLength) {\n resetState(context, trimmedTerm);\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n context.pendingResolve = resolve;\n context.requestId += 1;\n context.observable.setState({\n ...context.observable.state,\n term: trimmedTerm,\n status: \"loading\",\n error: null,\n });\n scheduleSearch(context, trimmedTerm, context.requestId);\n });\n}\n\nfunction clear<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.requestId += 1;\n resolvePending(context);\n cancelScheduledWork(context);\n resetState(context, \"\");\n}\n\nfunction destroy<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.destroyed = true;\n context.requestId += 1;\n resolvePending(context);\n cancelScheduledWork(context);\n}\n\nfunction scheduleSearch<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n requestId: number,\n): void {\n if (context.debounceInMs === 0) {\n void executeSearch(context, term, requestId);\n return;\n }\n\n context.debounceTimer = setTimeout(() => {\n context.debounceTimer = null;\n void executeSearch(context, term, requestId);\n }, context.debounceInMs);\n}\n\nasync function executeSearch<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n requestId: number,\n): Promise<void> {\n const abortController = new AbortController();\n context.abortController = abortController;\n\n try {\n const response = await context.fetch(buildRequestUrl(context, term), {\n signal: abortController.signal,\n });\n const result = await readResponse<TData>(response);\n settleSuccess(context, requestId, result);\n } catch (error) {\n if (isAbortError(error)) return;\n settleError(context, requestId, getErrorMessage(error));\n }\n}\n\nasync function readResponse<TData extends PredictiveSearchData>(\n response: Response,\n): Promise<TData> {\n const body = await readJson(response);\n if (response.ok) return body as TData;\n throw new Error(getResponseErrorMessage(body, response));\n}\n\nasync function readJson(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n}\n\nfunction settleSuccess<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n requestId: number,\n result: TData,\n): void {\n if (!isActiveRequest(context, requestId)) return;\n context.observable.setState({\n term: result.term,\n status: \"success\",\n result,\n error: null,\n });\n finishActiveRequest(context);\n}\n\nfunction settleError<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n requestId: number,\n message: string,\n): void {\n if (!isActiveRequest(context, requestId)) return;\n context.observable.setState({\n ...context.observable.state,\n status: \"error\",\n error: message,\n });\n finishActiveRequest(context);\n}\n\nfunction finishActiveRequest<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.abortController = null;\n resolvePending(context);\n}\n\nfunction resetState<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n): void {\n context.observable.setState({\n term,\n status: \"idle\",\n result: getEmptyPredictiveSearchResult(term) as TData,\n error: null,\n });\n}\n\nfunction cancelScheduledWork<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n clearDebounceTimer(context);\n abortRequest(context);\n}\n\nfunction clearDebounceTimer<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n if (context.debounceTimer === null) return;\n clearTimeout(context.debounceTimer);\n context.debounceTimer = null;\n}\n\nfunction abortRequest<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.abortController?.abort();\n context.abortController = null;\n}\n\nfunction resolvePending<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.pendingResolve?.();\n context.pendingResolve = null;\n}\n\nfunction isActiveRequest<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n requestId: number,\n): boolean {\n return !context.destroyed && context.requestId === requestId;\n}\n\nfunction buildRequestUrl<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n): string {\n const url = new URL(context.predictiveSearchEndpoint, \"https://hydrogen.local\");\n url.searchParams.set(PREDICTIVE_SEARCH_QUERY_PARAM, term);\n appendSearchOptions(url.searchParams, context.searchOptions);\n if (URL.canParse(context.predictiveSearchEndpoint)) return url.href;\n return `${url.pathname}${url.search}`;\n}\n\nfunction appendSearchOptions(searchParams: URLSearchParams, options: StoreSearchOptions): void {\n setOptionalParam(searchParams, \"limit\", options.limit);\n setOptionalParam(searchParams, \"limitScope\", options.limitScope);\n setOptionalParam(searchParams, \"types\", options.types?.join(\",\"));\n setOptionalParam(searchParams, \"searchableFields\", options.searchableFields?.join(\",\"));\n setOptionalParam(searchParams, \"unavailableProducts\", options.unavailableProducts);\n}\n\nfunction setOptionalParam(\n searchParams: URLSearchParams,\n name: string,\n value: number | string | undefined,\n): void {\n if (value === undefined) return;\n searchParams.set(name, String(value));\n}\n\nfunction getResponseErrorMessage(body: unknown, response: Response): string {\n const message = getNestedErrorMessage(body);\n if (message) return message;\n return `Predictive search request failed with status ${response.status}`;\n}\n\nfunction getNestedErrorMessage(body: unknown): string | null {\n if (!body || typeof body !== \"object\") return null;\n if (!(\"error\" in body)) return null;\n\n const error = body.error;\n if (!error || typeof error !== \"object\") return null;\n if (!(\"message\" in error)) return null;\n\n return typeof error.message === \"string\" ? error.message : null;\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : \"Predictive search request failed\";\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === \"AbortError\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"store.mjs","names":[],"sources":["../../../src/core/predictive-search/store.ts"],"sourcesContent":["import type {\n PredictiveSearchLimitScope,\n PredictiveSearchType,\n SearchUnavailableProductsType,\n SearchableField,\n} from \"../../graphql/generated/storefront-api-types\";\nimport { createObservable } from \"../observable\";\nimport { PREDICTIVE_SEARCH_API_PATH, PREDICTIVE_SEARCH_QUERY_PARAM } from \"./constants\";\nimport { getEmptyPredictiveSearchResult, type PredictiveSearchData } from \"./search\";\n\n/** Default wait, in milliseconds, between the latest term change and the search request. */\nexport const DEFAULT_PREDICTIVE_SEARCH_DEBOUNCE_IN_MS = 150;\n\n/** Default minimum trimmed term length required before predictive search sends a request. */\nconst DEFAULT_PREDICTIVE_SEARCH_MIN_TERM_LENGTH = 1;\n\n/** Status of the current search. */\nexport type PredictiveSearchStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\n/** Current search term, status, results, and error of a predictive search. */\nexport type PredictiveSearchState<TData extends PredictiveSearchData = PredictiveSearchData> = {\n /** Latest search term, without surrounding whitespace. */\n term: string;\n /** Status of the search for the current term. */\n status: PredictiveSearchStatus;\n /** Results of the latest successful search. Previous results stay in place while a new search loads and after a search fails. The idle state holds empty results. */\n result: TData;\n /** Message from the latest failed search, or `null` when the current search hasn't failed. A custom endpoint sets the message with a JSON body of `{error: {message}}`. */\n error: string | null;\n};\n\n/**\n * Endpoint, timing, and search settings for a predictive search store.\n *\n * The store sends each search setting to the endpoint as a URL parameter of the same name. The server handlers apply their own defaults to omitted settings.\n */\nexport type CreatePredictiveSearchStoreOptions = {\n /**\n * Route that returns predictive search results as JSON, in the shape that `createPredictiveSearchServerHandlers` returns.\n *\n * Defaults to `/api/predictive-search`.\n */\n predictiveSearchEndpoint?: string;\n /**\n * Milliseconds to wait after the latest term change before searching.\n *\n * Defaults to `150`. Pass `0` to search right away. Negative and non-finite values use the default.\n */\n debounceInMs?: number;\n /**\n * Number of characters a term needs, after trimming, before the store searches.\n *\n * Defaults to `1`. Negative and non-finite values use the default.\n */\n minTermLength?: number;\n /** Function that sends search requests. Defaults to the global `fetch`. Pass a function for tests or for runtimes without a global `fetch`. */\n fetch?: typeof globalThis.fetch;\n /** Maximum number of results, from 1 to 10. The server handlers clamp other values to that range. */\n limit?: number;\n /** Whether the limit applies to each result type or to all result types combined. */\n limitScope?: PredictiveSearchLimitScope;\n /** Result types to include. */\n types?: PredictiveSearchType[];\n /** Fields to search for page, article, and collection results. */\n searchableFields?: SearchableField[];\n /** Whether results hide unavailable products, show them, or list them last. */\n unavailableProducts?: SearchUnavailableProductsType;\n};\n\n/** Holds the search term, status, and results, and runs searches as the customer types. */\nexport type PredictiveSearchStore<TData extends PredictiveSearchData = PredictiveSearchData> = {\n /** Restarts a store after `destroy()`. A new store doesn't need this call. */\n connect(): void;\n /** Returns the current search term, status, results, and error. */\n getState(): PredictiveSearchState<TData>;\n /** Calls your function with the new state on every change. Returns a function that stops the calls. */\n subscribe(listener: (state: PredictiveSearchState<TData>) => void): () => void;\n /**\n * Searches for a term after the debounce delay. Call the method on every keystroke.\n *\n * Each call cancels the previous pending search. A term shorter than the minimum length, after trimming, returns the store to `idle` with empty results. Otherwise, the status changes to `loading` until the search ends with `success` or `error`. Responses for earlier terms never replace newer results.\n *\n * The returned promise resolves when the search ends, or when a later search, `clear()`, or `destroy()` cancels the search.\n */\n search(term: string): Promise<void>;\n /**\n * Cancels any pending search and returns the store to the idle state with no term and empty results.\n *\n * Call `clear()` when the customer closes the search UI.\n */\n clear(): void;\n /**\n * Stops the store. Call `destroy()` when the search UI unmounts.\n *\n * The store cancels any pending search, keeps its current state, and ignores searches until you call `connect()`.\n */\n destroy(): void;\n};\n\n/** Methods that search for a term and clear the results. */\nexport type PredictiveSearchActions = Pick<PredictiveSearchStore, \"search\" | \"clear\">;\n\ntype PredictiveSearchStoreContext<TData extends PredictiveSearchData> = {\n observable: ReturnType<typeof createObservable<PredictiveSearchState<TData>>>;\n // API route used to fetch predictive search results.\n predictiveSearchEndpoint: string;\n // Delay before firing a search request after the term changes.\n debounceInMs: number;\n // Minimum term length required before searching.\n minTermLength: number;\n // Fetch implementation, injectable for tests and non-browser runtimes.\n fetch: typeof globalThis.fetch;\n // Storefront API search options forwarded to the server handler.\n searchOptions: StoreSearchOptions;\n abortController: AbortController | null;\n // Active debounce timer waiting to start the next search.\n debounceTimer: ReturnType<typeof setTimeout> | null;\n // Monotonic counter used to ignore responses from older requests.\n requestId: number;\n // Resolver for the current debounced search promise.\n pendingResolve: (() => void) | null;\n // Prevents future work after the store has been torn down.\n destroyed: boolean;\n};\n\ntype StoreSearchOptions = Pick<\n CreatePredictiveSearchStoreOptions,\n \"limit\" | \"limitScope\" | \"types\" | \"searchableFields\" | \"unavailableProducts\"\n>;\n\n/**\n * Creates a store that searches as the customer types and holds the latest predictive search results. Use the store in DOM code or in UI frameworks without Hydrogen bindings. In React and Vue, use `PredictiveSearchProvider`.\n *\n * Call `search()` with each new term, read results with `getState()` or `subscribe()`, and call `destroy()` when the search UI unmounts. By default, the store requests results from the route that `createPredictiveSearchServerHandlers` serves.\n *\n * The function throws an error when you omit the `fetch` option in a runtime without a global `fetch`.\n *\n * @param options Endpoint, timing, and search settings for the store.\n * @returns A store with no term and empty results, ready to search.\n * @publicDocs\n */\nexport function createPredictiveSearchStore<\n TData extends PredictiveSearchData = PredictiveSearchData,\n>(options: CreatePredictiveSearchStoreOptions = {}): PredictiveSearchStore<TData> {\n // Prefer an injected fetch for tests and non-browser runtimes, then fall back to the ambient runtime fetch.\n const resolvedFetch = options.fetch ?? resolveGlobalFetch();\n if (typeof resolvedFetch !== \"function\") {\n throw new Error(\n \"No fetch function available. Pass a fetch option or ensure globalThis.fetch exists.\",\n );\n }\n\n const context: PredictiveSearchStoreContext<TData> = {\n observable: createObservable(createInitialState<TData>()),\n predictiveSearchEndpoint: options.predictiveSearchEndpoint ?? PREDICTIVE_SEARCH_API_PATH,\n debounceInMs: sanitizeDebounceInMs(options.debounceInMs),\n minTermLength: sanitizeMinTermLength(options.minTermLength),\n fetch: resolvedFetch,\n searchOptions: {\n limit: options.limit,\n limitScope: options.limitScope,\n types: options.types,\n searchableFields: options.searchableFields,\n unavailableProducts: options.unavailableProducts,\n },\n abortController: null,\n debounceTimer: null,\n requestId: 0,\n pendingResolve: null,\n destroyed: false,\n };\n\n return {\n connect: () => connect(context),\n getState: () => context.observable.state,\n subscribe: (listener) => context.observable.subscribe(listener),\n search: (term) => search(context, term),\n clear: () => clear(context),\n destroy: () => destroy(context),\n };\n}\n\nfunction connect<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.destroyed = false;\n}\n\nfunction resolveGlobalFetch(): typeof globalThis.fetch | undefined {\n if (typeof globalThis.fetch !== \"function\") return undefined;\n return globalThis.fetch.bind(globalThis);\n}\n\nfunction sanitizeDebounceInMs(debounceInMs: number | undefined): number {\n if (typeof debounceInMs !== \"number\") return DEFAULT_PREDICTIVE_SEARCH_DEBOUNCE_IN_MS;\n if (!Number.isFinite(debounceInMs)) return DEFAULT_PREDICTIVE_SEARCH_DEBOUNCE_IN_MS;\n if (debounceInMs < 0) return DEFAULT_PREDICTIVE_SEARCH_DEBOUNCE_IN_MS;\n return Math.trunc(debounceInMs);\n}\n\nfunction sanitizeMinTermLength(minTermLength: number | undefined): number {\n if (typeof minTermLength !== \"number\") return DEFAULT_PREDICTIVE_SEARCH_MIN_TERM_LENGTH;\n if (!Number.isFinite(minTermLength)) return DEFAULT_PREDICTIVE_SEARCH_MIN_TERM_LENGTH;\n if (minTermLength < 0) return DEFAULT_PREDICTIVE_SEARCH_MIN_TERM_LENGTH;\n return Math.trunc(minTermLength);\n}\n\nfunction createInitialState<TData extends PredictiveSearchData>(): PredictiveSearchState<TData> {\n return {\n term: \"\",\n status: \"idle\",\n result: getEmptyPredictiveSearchResult(\"\") as TData,\n error: null,\n };\n}\n\nfunction search<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n): Promise<void> {\n resolvePending(context);\n cancelScheduledWork(context);\n\n if (context.destroyed) return Promise.resolve();\n\n const trimmedTerm = term.trim();\n if (trimmedTerm.length < context.minTermLength) {\n resetState(context, trimmedTerm);\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n context.pendingResolve = resolve;\n context.requestId += 1;\n context.observable.setState({\n ...context.observable.state,\n term: trimmedTerm,\n status: \"loading\",\n error: null,\n });\n scheduleSearch(context, trimmedTerm, context.requestId);\n });\n}\n\nfunction clear<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.requestId += 1;\n resolvePending(context);\n cancelScheduledWork(context);\n resetState(context, \"\");\n}\n\nfunction destroy<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.destroyed = true;\n context.requestId += 1;\n resolvePending(context);\n cancelScheduledWork(context);\n}\n\nfunction scheduleSearch<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n requestId: number,\n): void {\n if (context.debounceInMs === 0) {\n void executeSearch(context, term, requestId);\n return;\n }\n\n context.debounceTimer = setTimeout(() => {\n context.debounceTimer = null;\n void executeSearch(context, term, requestId);\n }, context.debounceInMs);\n}\n\nasync function executeSearch<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n requestId: number,\n): Promise<void> {\n const abortController = new AbortController();\n context.abortController = abortController;\n\n try {\n const response = await context.fetch(buildRequestUrl(context, term), {\n signal: abortController.signal,\n });\n const result = await readResponse<TData>(response);\n settleSuccess(context, requestId, result);\n } catch (error) {\n if (isAbortError(error)) return;\n settleError(context, requestId, getErrorMessage(error));\n }\n}\n\nasync function readResponse<TData extends PredictiveSearchData>(\n response: Response,\n): Promise<TData> {\n const body = await readJson(response);\n if (response.ok) return body as TData;\n throw new Error(getResponseErrorMessage(body, response));\n}\n\nasync function readJson(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n}\n\nfunction settleSuccess<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n requestId: number,\n result: TData,\n): void {\n if (!isActiveRequest(context, requestId)) return;\n context.observable.setState({\n term: result.term,\n status: \"success\",\n result,\n error: null,\n });\n finishActiveRequest(context);\n}\n\nfunction settleError<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n requestId: number,\n message: string,\n): void {\n if (!isActiveRequest(context, requestId)) return;\n context.observable.setState({\n ...context.observable.state,\n status: \"error\",\n error: message,\n });\n finishActiveRequest(context);\n}\n\nfunction finishActiveRequest<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.abortController = null;\n resolvePending(context);\n}\n\nfunction resetState<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n): void {\n context.observable.setState({\n term,\n status: \"idle\",\n result: getEmptyPredictiveSearchResult(term) as TData,\n error: null,\n });\n}\n\nfunction cancelScheduledWork<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n clearDebounceTimer(context);\n abortRequest(context);\n}\n\nfunction clearDebounceTimer<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n if (context.debounceTimer === null) return;\n clearTimeout(context.debounceTimer);\n context.debounceTimer = null;\n}\n\nfunction abortRequest<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.abortController?.abort();\n context.abortController = null;\n}\n\nfunction resolvePending<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n): void {\n context.pendingResolve?.();\n context.pendingResolve = null;\n}\n\nfunction isActiveRequest<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n requestId: number,\n): boolean {\n return !context.destroyed && context.requestId === requestId;\n}\n\nfunction buildRequestUrl<TData extends PredictiveSearchData>(\n context: PredictiveSearchStoreContext<TData>,\n term: string,\n): string {\n const url = new URL(context.predictiveSearchEndpoint, \"https://hydrogen.local\");\n url.searchParams.set(PREDICTIVE_SEARCH_QUERY_PARAM, term);\n appendSearchOptions(url.searchParams, context.searchOptions);\n if (URL.canParse(context.predictiveSearchEndpoint)) return url.href;\n return `${url.pathname}${url.search}`;\n}\n\nfunction appendSearchOptions(searchParams: URLSearchParams, options: StoreSearchOptions): void {\n setOptionalParam(searchParams, \"limit\", options.limit);\n setOptionalParam(searchParams, \"limitScope\", options.limitScope);\n setOptionalParam(searchParams, \"types\", options.types?.join(\",\"));\n setOptionalParam(searchParams, \"searchableFields\", options.searchableFields?.join(\",\"));\n setOptionalParam(searchParams, \"unavailableProducts\", options.unavailableProducts);\n}\n\nfunction setOptionalParam(\n searchParams: URLSearchParams,\n name: string,\n value: number | string | undefined,\n): void {\n if (value === undefined) return;\n searchParams.set(name, String(value));\n}\n\nfunction getResponseErrorMessage(body: unknown, response: Response): string {\n const message = getNestedErrorMessage(body);\n if (message) return message;\n return `Predictive search request failed with status ${response.status}`;\n}\n\nfunction getNestedErrorMessage(body: unknown): string | null {\n if (!body || typeof body !== \"object\") return null;\n if (!(\"error\" in body)) return null;\n\n const error = body.error;\n if (!error || typeof error !== \"object\") return null;\n if (!(\"message\" in error)) return null;\n\n return typeof error.message === \"string\" ? error.message : null;\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : \"Predictive search request failed\";\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === \"AbortError\";\n}\n"],"mappings":"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"}
@@ -3,73 +3,72 @@ import { PredictiveSearchData } from "./search.mjs";
3
3
 
4
4
  //#region src/core/predictive-search/url.d.ts
5
5
  type PredictiveSearchItems = PredictiveSearchData["items"];
6
- /** A product returned by predictive search. */
6
+ /** A product result from predictive search. */
7
7
  type PredictiveSearchProductItem = PredictiveSearchItems["products"][number];
8
- /** A collection returned by predictive search. */
8
+ /** A collection result from predictive search. */
9
9
  type PredictiveSearchCollectionItem = PredictiveSearchItems["collections"][number];
10
- /** A page returned by predictive search. */
10
+ /** A page result from predictive search. */
11
11
  type PredictiveSearchPageItem = PredictiveSearchItems["pages"][number];
12
- /** An article returned by predictive search. Includes a nested `blog.handle` used for URL generation via {@link getPredictiveSearchItemUrl}. */
12
+ /** An article result from predictive search. */
13
13
  type PredictiveSearchArticleItem = PredictiveSearchItems["articles"][number];
14
- /** A search query suggestion returned by predictive search. Uses `text` as the search term when generating its URL via {@link getPredictiveSearchItemUrl}. */
14
+ /** A suggested search term from predictive search. */
15
15
  type PredictiveSearchQueryItem = PredictiveSearchItems["queries"][number];
16
- /** Union of product, collection, page, and article items. Excludes query suggestions. */
16
+ /** A product, collection, page, or article result. Excludes query suggestions. */
17
17
  type PredictiveSearchResourceItem = PredictiveSearchProductItem | PredictiveSearchCollectionItem | PredictiveSearchPageItem | PredictiveSearchArticleItem;
18
- /** Any item returned by predictive search, including query suggestions. */
18
+ /** Any predictive search result, including query suggestions. */
19
19
  type PredictiveSearchItem = PredictiveSearchResourceItem | PredictiveSearchQueryItem;
20
20
  /**
21
- * Options for generating URLs for resource items (products, collections,
22
- * pages, articles) via {@link getPredictiveSearchItemUrl}.
21
+ * Options for the link of a product, collection, page, or article result.
23
22
  *
24
- * Required for resource items because their URLs depend on route templates
25
- * and the current search term.
23
+ * Pass the route templates and the customer's search term.
26
24
  */
27
25
  type PredictiveSearchItemUrlOptions = {
28
- /** Optional path prefix prepended to the generated route (e.g., a locale prefix). */pathPrefix?: string; /** Route templates used to resolve the resource's URL pattern. */
29
- routes: ShopifyRouteTemplates; /** Search term appended as a query parameter to the generated URL. */
26
+ /** Prefix to add before the route path, such as a locale prefix. */pathPrefix?: string; /** Route templates for product, collection, page, and article paths, from `createShopifyRouteTemplates`. */
27
+ routes: ShopifyRouteTemplates; /** Search term that the customer typed. The link carries the term in the `q` URL parameter. */
30
28
  term: string;
31
29
  };
32
30
  /**
33
- * Options for generating URLs for query suggestion items via
34
- * {@link getPredictiveSearchItemUrl}.
31
+ * Options for the link of a query suggestion.
35
32
  *
36
- * Optional because query suggestions use their own `text` as the search
37
- * term and default to the standard search route.
33
+ * By default, a suggestion links to the standard search route and searches for the suggestion's text.
38
34
  */
39
35
  type PredictiveSearchQueryItemUrlOptions = {
40
- /** Optional path prefix prepended to the search route (e.g., a locale prefix). Ignored when `searchPath` is set. */pathPrefix?: string; /** Route templates. When omitted, the standard search route is used. */
41
- routes?: ShopifyRouteTemplates; /** Custom search page path, used as-is (no `pathPrefix` applied). Takes precedence over `routes` and `pathPrefix`. */
36
+ /** Prefix to add before the search route, such as a locale prefix. Has no effect when you set a search path. */pathPrefix?: string; /** Route templates that set the search page path. Defaults to Hydrogen's standard search route. */
37
+ routes?: ShopifyRouteTemplates; /** Search page path or URL to use as is, without the route templates or the path prefix. */
42
38
  searchPath?: string;
43
39
  };
40
+ /** Options for a search result URL. */
44
41
  type SearchResultUrlOptions = {
45
- /** Search page path or absolute URL. */baseUrl: string; /** Search term, set under `searchParamName`. */
46
- term: string; /** Storefront API `trackingParameters` query string, appended after the other parameters. */
47
- trackingParameters?: string | null; /** Extra query parameters, set before the term, so a key matching `searchParamName` is overwritten. */
48
- params?: Record<string, string>; /** Query parameter for the term. Defaults to `"q"`. */
42
+ /** Search page path or absolute URL. */baseUrl: string; /** Search term to add to the URL. */
43
+ term: string; /** Tracking parameters from a Storefront API search result. Pass the result's `trackingParameters` value unchanged. */
44
+ trackingParameters?: string | null; /** Extra URL parameters, such as a result type filter. The search term replaces an extra parameter with the same name. */
45
+ params?: Record<string, string>; /** URL parameter that carries the search term. Defaults to `q`. */
49
46
  searchParamName?: string;
50
47
  };
51
48
  /**
52
- * Generates a URL for a predictive search result item.
49
+ * Builds the link for a predictive search result. A product, collection, page, or
50
+ * article result links to the resource's page with the search term. A query suggestion links to
51
+ * the search page and searches for the suggestion's text. Every link keeps the
52
+ * result's tracking parameters.
53
53
  *
54
- * For query suggestions, builds a search page URL using the suggestion's
55
- * `text` as the search term. For resource items (products, collections,
56
- * pages, articles), builds the resource URL using the provided route
57
- * templates and appends the given search term as a query parameter.
54
+ * For a product, collection, page, or article, pass the route templates and the search term. The function throws an error without them.
58
55
  *
59
- * Always appends the item's `trackingParameters` when present.
60
- *
61
- * @throws {Error} When a resource item is passed without `routes` and `term` in options.
56
+ * @throws {Error} When you pass a resource item without `routes` and `term` in the options.
57
+ * @publicDocs
62
58
  */
63
59
  declare function getPredictiveSearchItemUrl(item: PredictiveSearchQueryItem, options?: PredictiveSearchQueryItemUrlOptions): string;
64
60
  declare function getPredictiveSearchItemUrl(item: PredictiveSearchResourceItem, options: PredictiveSearchItemUrlOptions): string;
65
61
  /**
66
- * Builds a search result URL from a base URL, search term, and optional
67
- * extra parameters.
62
+ * Builds a search page URL from a path, a search term, and optional extra
63
+ * parameters. Use the function for search page links, such as a link to every
64
+ * result for the current term.
65
+ *
66
+ * The search term and the extra parameters replace values with the same names in the base URL.
67
+ *
68
+ * @param options - The base URL, the search term, the extra and tracking parameters, and the search parameter name.
69
+ * @returns A relative URL, or an absolute URL when the base URL is absolute.
68
70
  *
69
- * Sets the term under `searchParamName` (defaults to `"q"`), replacing any
70
- * existing value. `params` are set the same way, and `trackingParameters`
71
- * are appended. Returns a relative URL unless the base
72
- * URL is absolute.
71
+ * @publicDocs
73
72
  */
74
73
  declare function getSearchResultUrl({
75
74
  baseUrl,
@@ -2,6 +2,11 @@ import { getStandardRoute } from "../standard-routes/build.mjs";
2
2
 
3
3
  //#region src/core/predictive-search/url.ts
4
4
  const RELATIVE_URL_BASE = "https://hydrogen.local";
5
+ /**
6
+ * @param item The predictive search result to link to.
7
+ * @param options Route templates, search term, and path settings for the link.
8
+ * @returns The link URL. The URL is relative unless you pass an absolute search path.
9
+ */
5
10
  function getPredictiveSearchItemUrl(item, options) {
6
11
  return getSearchResultUrl({
7
12
  baseUrl: getPredictiveSearchItemBaseUrl(item, options),
@@ -10,13 +15,16 @@ function getPredictiveSearchItemUrl(item, options) {
10
15
  });
11
16
  }
12
17
  /**
13
- * Builds a search result URL from a base URL, search term, and optional
14
- * extra parameters.
18
+ * Builds a search page URL from a path, a search term, and optional extra
19
+ * parameters. Use the function for search page links, such as a link to every
20
+ * result for the current term.
21
+ *
22
+ * The search term and the extra parameters replace values with the same names in the base URL.
23
+ *
24
+ * @param options - The base URL, the search term, the extra and tracking parameters, and the search parameter name.
25
+ * @returns A relative URL, or an absolute URL when the base URL is absolute.
15
26
  *
16
- * Sets the term under `searchParamName` (defaults to `"q"`), replacing any
17
- * existing value. `params` are set the same way, and `trackingParameters`
18
- * are appended. Returns a relative URL unless the base
19
- * URL is absolute.
27
+ * @publicDocs
20
28
  */
21
29
  function getSearchResultUrl({ baseUrl, term, trackingParameters, params, searchParamName = "q" }) {
22
30
  const url = new URL(baseUrl, RELATIVE_URL_BASE);
@@ -1 +1 @@
1
- {"version":3,"file":"url.mjs","names":[],"sources":["../../../src/core/predictive-search/url.ts"],"sourcesContent":["import { getStandardRoute, type ShopifyRouteTemplates } from \"../standard-routes/index\";\nimport type { PredictiveSearchData } from \"./search\";\n\nconst RELATIVE_URL_BASE = \"https://hydrogen.local\";\n\ntype PredictiveSearchItems = PredictiveSearchData[\"items\"];\n\n/** A product returned by predictive search. */\nexport type PredictiveSearchProductItem = PredictiveSearchItems[\"products\"][number];\n/** A collection returned by predictive search. */\nexport type PredictiveSearchCollectionItem = PredictiveSearchItems[\"collections\"][number];\n/** A page returned by predictive search. */\nexport type PredictiveSearchPageItem = PredictiveSearchItems[\"pages\"][number];\n/** An article returned by predictive search. Includes a nested `blog.handle` used for URL generation via {@link getPredictiveSearchItemUrl}. */\nexport type PredictiveSearchArticleItem = PredictiveSearchItems[\"articles\"][number];\n/** A search query suggestion returned by predictive search. Uses `text` as the search term when generating its URL via {@link getPredictiveSearchItemUrl}. */\nexport type PredictiveSearchQueryItem = PredictiveSearchItems[\"queries\"][number];\n\n/** Union of product, collection, page, and article items. Excludes query suggestions. */\nexport type PredictiveSearchResourceItem =\n | PredictiveSearchProductItem\n | PredictiveSearchCollectionItem\n | PredictiveSearchPageItem\n | PredictiveSearchArticleItem;\n\n/** Any item returned by predictive search, including query suggestions. */\nexport type PredictiveSearchItem = PredictiveSearchResourceItem | PredictiveSearchQueryItem;\n\n/**\n * Options for generating URLs for resource items (products, collections,\n * pages, articles) via {@link getPredictiveSearchItemUrl}.\n *\n * Required for resource items because their URLs depend on route templates\n * and the current search term.\n */\nexport type PredictiveSearchItemUrlOptions = {\n /** Optional path prefix prepended to the generated route (e.g., a locale prefix). */\n pathPrefix?: string;\n /** Route templates used to resolve the resource's URL pattern. */\n routes: ShopifyRouteTemplates;\n /** Search term appended as a query parameter to the generated URL. */\n term: string;\n};\n\n/**\n * Options for generating URLs for query suggestion items via\n * {@link getPredictiveSearchItemUrl}.\n *\n * Optional because query suggestions use their own `text` as the search\n * term and default to the standard search route.\n */\nexport type PredictiveSearchQueryItemUrlOptions = {\n /** Optional path prefix prepended to the search route (e.g., a locale prefix). Ignored when `searchPath` is set. */\n pathPrefix?: string;\n /** Route templates. When omitted, the standard search route is used. */\n routes?: ShopifyRouteTemplates;\n /** Custom search page path, used as-is (no `pathPrefix` applied). Takes precedence over `routes` and `pathPrefix`. */\n searchPath?: string;\n};\n\ntype AnyPredictiveSearchItemUrlOptions =\n | PredictiveSearchItemUrlOptions\n | PredictiveSearchQueryItemUrlOptions;\n\ntype SearchResultUrlOptions = {\n /** Search page path or absolute URL. */\n baseUrl: string;\n /** Search term, set under `searchParamName`. */\n term: string;\n /** Storefront API `trackingParameters` query string, appended after the other parameters. */\n trackingParameters?: string | null;\n /** Extra query parameters, set before the term, so a key matching `searchParamName` is overwritten. */\n params?: Record<string, string>;\n /** Query parameter for the term. Defaults to `\"q\"`. */\n searchParamName?: string;\n};\n\n/**\n * Generates a URL for a predictive search result item.\n *\n * For query suggestions, builds a search page URL using the suggestion's\n * `text` as the search term. For resource items (products, collections,\n * pages, articles), builds the resource URL using the provided route\n * templates and appends the given search term as a query parameter.\n *\n * Always appends the item's `trackingParameters` when present.\n *\n * @throws {Error} When a resource item is passed without `routes` and `term` in options.\n */\nexport function getPredictiveSearchItemUrl(\n item: PredictiveSearchQueryItem,\n options?: PredictiveSearchQueryItemUrlOptions,\n): string;\nexport function getPredictiveSearchItemUrl(\n item: PredictiveSearchResourceItem,\n options: PredictiveSearchItemUrlOptions,\n): string;\nexport function getPredictiveSearchItemUrl(\n item: PredictiveSearchItem,\n options?: AnyPredictiveSearchItemUrlOptions,\n): string {\n return getSearchResultUrl({\n baseUrl: getPredictiveSearchItemBaseUrl(item, options),\n term: getPredictiveSearchItemSearchTerm(item, options),\n trackingParameters: item.trackingParameters,\n });\n}\n\n/**\n * Builds a search result URL from a base URL, search term, and optional\n * extra parameters.\n *\n * Sets the term under `searchParamName` (defaults to `\"q\"`), replacing any\n * existing value. `params` are set the same way, and `trackingParameters`\n * are appended. Returns a relative URL unless the base\n * URL is absolute.\n */\nexport function getSearchResultUrl({\n baseUrl,\n term,\n trackingParameters,\n params,\n searchParamName = \"q\",\n}: SearchResultUrlOptions): string {\n const url = new URL(baseUrl, RELATIVE_URL_BASE);\n\n for (const [name, value] of Object.entries(params ?? {})) {\n url.searchParams.set(name, value);\n }\n\n url.searchParams.set(searchParamName, term);\n appendTrackingParameters(url.searchParams, trackingParameters);\n\n if (isAbsoluteUrl(baseUrl)) return url.href;\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\nfunction getPredictiveSearchItemBaseUrl(\n item: PredictiveSearchItem,\n options: AnyPredictiveSearchItemUrlOptions | undefined,\n): string {\n if (item.__typename === \"SearchQuerySuggestion\") return getQueryBaseUrl(options);\n\n const { pathPrefix, routes } = requirePredictiveSearchItemUrlOptions(options);\n return getResourceBaseUrl(item, routes, pathPrefix);\n}\n\nfunction getQueryBaseUrl(options: AnyPredictiveSearchItemUrlOptions | undefined): string {\n if (options && \"searchPath\" in options && options.searchPath) return options.searchPath;\n\n return getStandardRoute(\n options?.routes ?? {},\n \"search\",\n {},\n {\n pathPrefix: options?.pathPrefix,\n },\n );\n}\n\nfunction getResourceBaseUrl(\n item: PredictiveSearchResourceItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n switch (item.__typename) {\n case \"Product\":\n return getProductBaseUrl(item, routes, pathPrefix);\n case \"Collection\":\n return getCollectionBaseUrl(item, routes, pathPrefix);\n case \"Page\":\n return getPageBaseUrl(item, routes, pathPrefix);\n case \"Article\":\n return getArticleBaseUrl(item, routes, pathPrefix);\n }\n}\n\nfunction getProductBaseUrl(\n product: PredictiveSearchProductItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n return getStandardRoute(routes, \"product\", { productHandle: product.handle }, { pathPrefix });\n}\n\nfunction getCollectionBaseUrl(\n collection: PredictiveSearchCollectionItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n return getStandardRoute(\n routes,\n \"collection\",\n { collectionHandle: collection.handle },\n { pathPrefix },\n );\n}\n\nfunction getPageBaseUrl(\n page: PredictiveSearchPageItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n return getStandardRoute(routes, \"page\", { pageHandle: page.handle }, { pathPrefix });\n}\n\nfunction getArticleBaseUrl(\n article: PredictiveSearchArticleItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n return getStandardRoute(\n routes,\n \"article\",\n { articleHandle: article.handle, blogHandle: article.blog.handle },\n { pathPrefix },\n );\n}\n\nfunction getPredictiveSearchItemSearchTerm(\n item: PredictiveSearchItem,\n options: AnyPredictiveSearchItemUrlOptions | undefined,\n): string {\n if (item.__typename === \"SearchQuerySuggestion\") return item.text;\n return requirePredictiveSearchItemUrlOptions(options).term;\n}\n\nfunction requirePredictiveSearchItemUrlOptions(\n options: AnyPredictiveSearchItemUrlOptions | undefined,\n): PredictiveSearchItemUrlOptions {\n if (options && \"term\" in options && \"routes\" in options) return options;\n\n throw new Error(\n \"Predictive search resource URLs require route templates and the typed search term.\",\n );\n}\n\nfunction appendTrackingParameters(params: URLSearchParams, trackingParameters?: string | null) {\n if (!trackingParameters) return;\n\n const tracking = new URLSearchParams(trackingParameters);\n for (const [name, value] of tracking) {\n params.append(name, value);\n }\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n return URL.canParse(url);\n}\n"],"mappings":";;;AAGA,MAAM,oBAAoB;AA8F1B,SAAgB,2BACd,MACA,SACQ;AACR,QAAO,mBAAmB;EACxB,SAAS,+BAA+B,MAAM,QAAQ;EACtD,MAAM,kCAAkC,MAAM,QAAQ;EACtD,oBAAoB,KAAK;EAC1B,CAAC;;;;;;;;;;;AAYJ,SAAgB,mBAAmB,EACjC,SACA,MACA,oBACA,QACA,kBAAkB,OACe;CACjC,MAAM,MAAM,IAAI,IAAI,SAAS,kBAAkB;AAE/C,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,UAAU,EAAE,CAAC,CACtD,KAAI,aAAa,IAAI,MAAM,MAAM;AAGnC,KAAI,aAAa,IAAI,iBAAiB,KAAK;AAC3C,0BAAyB,IAAI,cAAc,mBAAmB;AAE9D,KAAI,cAAc,QAAQ,CAAE,QAAO,IAAI;AACvC,QAAO,GAAG,IAAI,WAAW,IAAI,SAAS,IAAI;;AAG5C,SAAS,+BACP,MACA,SACQ;AACR,KAAI,KAAK,eAAe,wBAAyB,QAAO,gBAAgB,QAAQ;CAEhF,MAAM,EAAE,YAAY,WAAW,sCAAsC,QAAQ;AAC7E,QAAO,mBAAmB,MAAM,QAAQ,WAAW;;AAGrD,SAAS,gBAAgB,SAAgE;AACvF,KAAI,WAAW,gBAAgB,WAAW,QAAQ,WAAY,QAAO,QAAQ;AAE7E,QAAO,iBACL,SAAS,UAAU,EAAE,EACrB,UACA,EAAE,EACF,EACE,YAAY,SAAS,YACtB,CACF;;AAGH,SAAS,mBACP,MACA,QACA,YACQ;AACR,SAAQ,KAAK,YAAb;EACE,KAAK,UACH,QAAO,kBAAkB,MAAM,QAAQ,WAAW;EACpD,KAAK,aACH,QAAO,qBAAqB,MAAM,QAAQ,WAAW;EACvD,KAAK,OACH,QAAO,eAAe,MAAM,QAAQ,WAAW;EACjD,KAAK,UACH,QAAO,kBAAkB,MAAM,QAAQ,WAAW;;;AAIxD,SAAS,kBACP,SACA,QACA,YACQ;AACR,QAAO,iBAAiB,QAAQ,WAAW,EAAE,eAAe,QAAQ,QAAQ,EAAE,EAAE,YAAY,CAAC;;AAG/F,SAAS,qBACP,YACA,QACA,YACQ;AACR,QAAO,iBACL,QACA,cACA,EAAE,kBAAkB,WAAW,QAAQ,EACvC,EAAE,YAAY,CACf;;AAGH,SAAS,eACP,MACA,QACA,YACQ;AACR,QAAO,iBAAiB,QAAQ,QAAQ,EAAE,YAAY,KAAK,QAAQ,EAAE,EAAE,YAAY,CAAC;;AAGtF,SAAS,kBACP,SACA,QACA,YACQ;AACR,QAAO,iBACL,QACA,WACA;EAAE,eAAe,QAAQ;EAAQ,YAAY,QAAQ,KAAK;EAAQ,EAClE,EAAE,YAAY,CACf;;AAGH,SAAS,kCACP,MACA,SACQ;AACR,KAAI,KAAK,eAAe,wBAAyB,QAAO,KAAK;AAC7D,QAAO,sCAAsC,QAAQ,CAAC;;AAGxD,SAAS,sCACP,SACgC;AAChC,KAAI,WAAW,UAAU,WAAW,YAAY,QAAS,QAAO;AAEhE,OAAM,IAAI,MACR,qFACD;;AAGH,SAAS,yBAAyB,QAAyB,oBAAoC;AAC7F,KAAI,CAAC,mBAAoB;CAEzB,MAAM,WAAW,IAAI,gBAAgB,mBAAmB;AACxD,MAAK,MAAM,CAAC,MAAM,UAAU,SAC1B,QAAO,OAAO,MAAM,MAAM;;AAI9B,SAAS,cAAc,KAAsB;AAC3C,QAAO,IAAI,SAAS,IAAI"}
1
+ {"version":3,"file":"url.mjs","names":[],"sources":["../../../src/core/predictive-search/url.ts"],"sourcesContent":["import { getStandardRoute, type ShopifyRouteTemplates } from \"../standard-routes/index\";\nimport type { PredictiveSearchData } from \"./search\";\n\nconst RELATIVE_URL_BASE = \"https://hydrogen.local\";\n\ntype PredictiveSearchItems = PredictiveSearchData[\"items\"];\n\n/** A product result from predictive search. */\nexport type PredictiveSearchProductItem = PredictiveSearchItems[\"products\"][number];\n/** A collection result from predictive search. */\nexport type PredictiveSearchCollectionItem = PredictiveSearchItems[\"collections\"][number];\n/** A page result from predictive search. */\nexport type PredictiveSearchPageItem = PredictiveSearchItems[\"pages\"][number];\n/** An article result from predictive search. */\nexport type PredictiveSearchArticleItem = PredictiveSearchItems[\"articles\"][number];\n/** A suggested search term from predictive search. */\nexport type PredictiveSearchQueryItem = PredictiveSearchItems[\"queries\"][number];\n\n/** A product, collection, page, or article result. Excludes query suggestions. */\nexport type PredictiveSearchResourceItem =\n | PredictiveSearchProductItem\n | PredictiveSearchCollectionItem\n | PredictiveSearchPageItem\n | PredictiveSearchArticleItem;\n\n/** Any predictive search result, including query suggestions. */\nexport type PredictiveSearchItem = PredictiveSearchResourceItem | PredictiveSearchQueryItem;\n\n/**\n * Options for the link of a product, collection, page, or article result.\n *\n * Pass the route templates and the customer's search term.\n */\nexport type PredictiveSearchItemUrlOptions = {\n /** Prefix to add before the route path, such as a locale prefix. */\n pathPrefix?: string;\n /** Route templates for product, collection, page, and article paths, from `createShopifyRouteTemplates`. */\n routes: ShopifyRouteTemplates;\n /** Search term that the customer typed. The link carries the term in the `q` URL parameter. */\n term: string;\n};\n\n/**\n * Options for the link of a query suggestion.\n *\n * By default, a suggestion links to the standard search route and searches for the suggestion's text.\n */\nexport type PredictiveSearchQueryItemUrlOptions = {\n /** Prefix to add before the search route, such as a locale prefix. Has no effect when you set a search path. */\n pathPrefix?: string;\n /** Route templates that set the search page path. Defaults to Hydrogen's standard search route. */\n routes?: ShopifyRouteTemplates;\n /** Search page path or URL to use as is, without the route templates or the path prefix. */\n searchPath?: string;\n};\n\n/** URL options for any predictive search result, either resource options or query suggestion options. */\ntype AnyPredictiveSearchItemUrlOptions =\n | PredictiveSearchItemUrlOptions\n | PredictiveSearchQueryItemUrlOptions;\n\n/** Options for a search result URL. */\ntype SearchResultUrlOptions = {\n /** Search page path or absolute URL. */\n baseUrl: string;\n /** Search term to add to the URL. */\n term: string;\n /** Tracking parameters from a Storefront API search result. Pass the result's `trackingParameters` value unchanged. */\n trackingParameters?: string | null;\n /** Extra URL parameters, such as a result type filter. The search term replaces an extra parameter with the same name. */\n params?: Record<string, string>;\n /** URL parameter that carries the search term. Defaults to `q`. */\n searchParamName?: string;\n};\n\n/**\n * Builds the link for a predictive search result. A product, collection, page, or\n * article result links to the resource's page with the search term. A query suggestion links to\n * the search page and searches for the suggestion's text. Every link keeps the\n * result's tracking parameters.\n *\n * For a product, collection, page, or article, pass the route templates and the search term. The function throws an error without them.\n *\n * @throws {Error} When you pass a resource item without `routes` and `term` in the options.\n * @publicDocs\n */\nexport function getPredictiveSearchItemUrl(\n item: PredictiveSearchQueryItem,\n options?: PredictiveSearchQueryItemUrlOptions,\n): string;\nexport function getPredictiveSearchItemUrl(\n item: PredictiveSearchResourceItem,\n options: PredictiveSearchItemUrlOptions,\n): string;\n/**\n * @param item The predictive search result to link to.\n * @param options Route templates, search term, and path settings for the link.\n * @returns The link URL. The URL is relative unless you pass an absolute search path.\n */\nexport function getPredictiveSearchItemUrl(\n item: PredictiveSearchItem,\n options?: AnyPredictiveSearchItemUrlOptions,\n): string {\n return getSearchResultUrl({\n baseUrl: getPredictiveSearchItemBaseUrl(item, options),\n term: getPredictiveSearchItemSearchTerm(item, options),\n trackingParameters: item.trackingParameters,\n });\n}\n\n/**\n * Builds a search page URL from a path, a search term, and optional extra\n * parameters. Use the function for search page links, such as a link to every\n * result for the current term.\n *\n * The search term and the extra parameters replace values with the same names in the base URL.\n *\n * @param options - The base URL, the search term, the extra and tracking parameters, and the search parameter name.\n * @returns A relative URL, or an absolute URL when the base URL is absolute.\n *\n * @publicDocs\n */\nexport function getSearchResultUrl({\n baseUrl,\n term,\n trackingParameters,\n params,\n searchParamName = \"q\",\n}: SearchResultUrlOptions): string {\n const url = new URL(baseUrl, RELATIVE_URL_BASE);\n\n for (const [name, value] of Object.entries(params ?? {})) {\n url.searchParams.set(name, value);\n }\n\n url.searchParams.set(searchParamName, term);\n appendTrackingParameters(url.searchParams, trackingParameters);\n\n if (isAbsoluteUrl(baseUrl)) return url.href;\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\nfunction getPredictiveSearchItemBaseUrl(\n item: PredictiveSearchItem,\n options: AnyPredictiveSearchItemUrlOptions | undefined,\n): string {\n if (item.__typename === \"SearchQuerySuggestion\") return getQueryBaseUrl(options);\n\n const { pathPrefix, routes } = requirePredictiveSearchItemUrlOptions(options);\n return getResourceBaseUrl(item, routes, pathPrefix);\n}\n\nfunction getQueryBaseUrl(options: AnyPredictiveSearchItemUrlOptions | undefined): string {\n if (options && \"searchPath\" in options && options.searchPath) return options.searchPath;\n\n return getStandardRoute(\n options?.routes ?? {},\n \"search\",\n {},\n {\n pathPrefix: options?.pathPrefix,\n },\n );\n}\n\nfunction getResourceBaseUrl(\n item: PredictiveSearchResourceItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n switch (item.__typename) {\n case \"Product\":\n return getProductBaseUrl(item, routes, pathPrefix);\n case \"Collection\":\n return getCollectionBaseUrl(item, routes, pathPrefix);\n case \"Page\":\n return getPageBaseUrl(item, routes, pathPrefix);\n case \"Article\":\n return getArticleBaseUrl(item, routes, pathPrefix);\n }\n}\n\nfunction getProductBaseUrl(\n product: PredictiveSearchProductItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n return getStandardRoute(routes, \"product\", { productHandle: product.handle }, { pathPrefix });\n}\n\nfunction getCollectionBaseUrl(\n collection: PredictiveSearchCollectionItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n return getStandardRoute(\n routes,\n \"collection\",\n { collectionHandle: collection.handle },\n { pathPrefix },\n );\n}\n\nfunction getPageBaseUrl(\n page: PredictiveSearchPageItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n return getStandardRoute(routes, \"page\", { pageHandle: page.handle }, { pathPrefix });\n}\n\nfunction getArticleBaseUrl(\n article: PredictiveSearchArticleItem,\n routes: ShopifyRouteTemplates,\n pathPrefix: string | undefined,\n): string {\n return getStandardRoute(\n routes,\n \"article\",\n { articleHandle: article.handle, blogHandle: article.blog.handle },\n { pathPrefix },\n );\n}\n\nfunction getPredictiveSearchItemSearchTerm(\n item: PredictiveSearchItem,\n options: AnyPredictiveSearchItemUrlOptions | undefined,\n): string {\n if (item.__typename === \"SearchQuerySuggestion\") return item.text;\n return requirePredictiveSearchItemUrlOptions(options).term;\n}\n\nfunction requirePredictiveSearchItemUrlOptions(\n options: AnyPredictiveSearchItemUrlOptions | undefined,\n): PredictiveSearchItemUrlOptions {\n if (options && \"term\" in options && \"routes\" in options) return options;\n\n throw new Error(\n \"Predictive search resource URLs require route templates and the typed search term.\",\n );\n}\n\nfunction appendTrackingParameters(params: URLSearchParams, trackingParameters?: string | null) {\n if (!trackingParameters) return;\n\n const tracking = new URLSearchParams(trackingParameters);\n for (const [name, value] of tracking) {\n params.append(name, value);\n }\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n return URL.canParse(url);\n}\n"],"mappings":";;;AAGA,MAAM,oBAAoB;;;;;;AAgG1B,SAAgB,2BACd,MACA,SACQ;AACR,QAAO,mBAAmB;EACxB,SAAS,+BAA+B,MAAM,QAAQ;EACtD,MAAM,kCAAkC,MAAM,QAAQ;EACtD,oBAAoB,KAAK;EAC1B,CAAC;;;;;;;;;;;;;;AAeJ,SAAgB,mBAAmB,EACjC,SACA,MACA,oBACA,QACA,kBAAkB,OACe;CACjC,MAAM,MAAM,IAAI,IAAI,SAAS,kBAAkB;AAE/C,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,UAAU,EAAE,CAAC,CACtD,KAAI,aAAa,IAAI,MAAM,MAAM;AAGnC,KAAI,aAAa,IAAI,iBAAiB,KAAK;AAC3C,0BAAyB,IAAI,cAAc,mBAAmB;AAE9D,KAAI,cAAc,QAAQ,CAAE,QAAO,IAAI;AACvC,QAAO,GAAG,IAAI,WAAW,IAAI,SAAS,IAAI;;AAG5C,SAAS,+BACP,MACA,SACQ;AACR,KAAI,KAAK,eAAe,wBAAyB,QAAO,gBAAgB,QAAQ;CAEhF,MAAM,EAAE,YAAY,WAAW,sCAAsC,QAAQ;AAC7E,QAAO,mBAAmB,MAAM,QAAQ,WAAW;;AAGrD,SAAS,gBAAgB,SAAgE;AACvF,KAAI,WAAW,gBAAgB,WAAW,QAAQ,WAAY,QAAO,QAAQ;AAE7E,QAAO,iBACL,SAAS,UAAU,EAAE,EACrB,UACA,EAAE,EACF,EACE,YAAY,SAAS,YACtB,CACF;;AAGH,SAAS,mBACP,MACA,QACA,YACQ;AACR,SAAQ,KAAK,YAAb;EACE,KAAK,UACH,QAAO,kBAAkB,MAAM,QAAQ,WAAW;EACpD,KAAK,aACH,QAAO,qBAAqB,MAAM,QAAQ,WAAW;EACvD,KAAK,OACH,QAAO,eAAe,MAAM,QAAQ,WAAW;EACjD,KAAK,UACH,QAAO,kBAAkB,MAAM,QAAQ,WAAW;;;AAIxD,SAAS,kBACP,SACA,QACA,YACQ;AACR,QAAO,iBAAiB,QAAQ,WAAW,EAAE,eAAe,QAAQ,QAAQ,EAAE,EAAE,YAAY,CAAC;;AAG/F,SAAS,qBACP,YACA,QACA,YACQ;AACR,QAAO,iBACL,QACA,cACA,EAAE,kBAAkB,WAAW,QAAQ,EACvC,EAAE,YAAY,CACf;;AAGH,SAAS,eACP,MACA,QACA,YACQ;AACR,QAAO,iBAAiB,QAAQ,QAAQ,EAAE,YAAY,KAAK,QAAQ,EAAE,EAAE,YAAY,CAAC;;AAGtF,SAAS,kBACP,SACA,QACA,YACQ;AACR,QAAO,iBACL,QACA,WACA;EAAE,eAAe,QAAQ;EAAQ,YAAY,QAAQ,KAAK;EAAQ,EAClE,EAAE,YAAY,CACf;;AAGH,SAAS,kCACP,MACA,SACQ;AACR,KAAI,KAAK,eAAe,wBAAyB,QAAO,KAAK;AAC7D,QAAO,sCAAsC,QAAQ,CAAC;;AAGxD,SAAS,sCACP,SACgC;AAChC,KAAI,WAAW,UAAU,WAAW,YAAY,QAAS,QAAO;AAEhE,OAAM,IAAI,MACR,qFACD;;AAGH,SAAS,yBAAyB,QAAyB,oBAAoC;AAC7F,KAAI,CAAC,mBAAoB;CAEzB,MAAM,WAAW,IAAI,gBAAgB,mBAAmB;AACxD,MAAK,MAAM,CAAC,MAAM,UAAU,SAC1B,QAAO,OAAO,MAAM,MAAM;;AAI9B,SAAS,cAAc,KAAsB;AAC3C,QAAO,IAAI,SAAS,IAAI"}
@@ -1,54 +1,95 @@
1
1
  import { ProductVariantInput } from "./state.mjs";
2
2
 
3
3
  //#region src/core/product/form.d.ts
4
- /** Props returned by {@link ProductFormRegister} for the `merchandiseId` input. */
4
+ /**
5
+ * Props for the hidden input that tells the cart which variant to add.
6
+ *
7
+ * @publicDocs
8
+ */
5
9
  interface ProductMerchandiseIdProps {
10
+ /** Field name that the cart reads as the variant to add. */
6
11
  name: "merchandiseId";
7
- /** The currently selected variant ID, or an empty string when no variant is resolved. */
12
+ /** Selected variant ID. Empty when the selection doesn't resolve to a variant. */
8
13
  value: string;
9
14
  }
10
- /** Props returned by {@link ProductFormRegister} for a quantity input. */
15
+ /**
16
+ * Props for a controlled quantity input.
17
+ *
18
+ * @publicDocs
19
+ */
11
20
  interface ProductQuantityProps {
21
+ /** Field name that the cart reads as the quantity to add. */
12
22
  name: "quantity";
23
+ /** Quantity to add. Defaults to `1`. */
13
24
  value: string;
14
25
  }
15
- /** Props returned by {@link ProductFormRegister} for an uncontrolled quantity input. */
26
+ /**
27
+ * Props for an uncontrolled quantity input.
28
+ *
29
+ * @publicDocs
30
+ */
16
31
  interface ProductQuantityDefaultProps {
32
+ /** Field name that the cart reads as the quantity to add. */
17
33
  name: "quantity";
34
+ /** Starting quantity for the input. */
18
35
  defaultValue: string;
19
36
  }
20
- /** Props returned by {@link ProductFormRegister} for a variant option value control. */
37
+ /**
38
+ * Props for a control that selects one option value, such as a swatch or a radio button.
39
+ *
40
+ * @publicDocs
41
+ */
21
42
  interface ProductOptionValueProps {
43
+ /** Option name, such as "Color". */
22
44
  name: string;
45
+ /** Option value that the control selects, such as "Red". */
23
46
  value: string;
47
+ /** Selects the option value in the product form. */
24
48
  onChange: () => void;
49
+ /** Selects the option value in the product form. Matches the change handler. */
25
50
  onClick: () => void;
26
51
  }
27
- /** Props returned by {@link ProductFormRegister} for the add-to-cart submit button. */
52
+ /**
53
+ * Props for the button that submits the product form and adds the selected variant to the cart.
54
+ *
55
+ * @publicDocs
56
+ */
28
57
  interface ProductAddToCartProps {
58
+ /** Identifies the add-to-cart button. The button has no value, which tells the cart to add the variant from the merchandise ID field. */
29
59
  name: "add-to-cart";
60
+ /** Makes the button submit the product form. */
30
61
  type: "submit";
31
62
  }
63
+ /** Line-item attribute field name. The cart uses the text after `attributes.` as the attribute key. */
32
64
  type AttributeValueName = `attributes.${string}`;
33
- /** Props returned by {@link ProductFormRegister} for a line-item attribute input. */
65
+ /**
66
+ * Props for an input that adds a custom attribute to the cart line, such as a gift message.
67
+ *
68
+ * @publicDocs
69
+ */
34
70
  interface ProductAttributeValueProps {
71
+ /** Field name in the format `attributes.<key>`. The cart saves the key and value as an attribute of the cart line. */
35
72
  name: AttributeValueName;
73
+ /** Attribute value to save on the cart line. */
36
74
  value: string;
37
75
  }
38
- /** Props returned by {@link ProductFormRegister} for an uncontrolled line-item attribute input. */
76
+ /**
77
+ * Props for an uncontrolled input that adds a custom attribute to the cart line.
78
+ *
79
+ * @publicDocs
80
+ */
39
81
  interface ProductAttributeDefaultValueProps {
82
+ /** Field name in the format `attributes.<key>`. The cart saves the key and value as an attribute of the cart line. */
40
83
  name: AttributeValueName;
84
+ /** Starting attribute value for the input. */
41
85
  defaultValue: string;
42
86
  }
43
87
  /**
44
- * Register function returned by `useProductForm`.
88
+ * Returns the props for one product form field. Spread the props on the matching input or button.
45
89
  *
46
- * Product forms only register product-relevant fields:
47
- * `merchandiseId`, `quantity`, `optionValue`, `attributeValue`, and `addToCart`.
90
+ * Pass `merchandiseId`, `quantity`, `optionValue`, `attributeValue`, or `addToCart` as the field. Any other field name throws an error. An attribute value field with an empty key throws a `TypeError`.
48
91
  *
49
- * Option values return form identity and activation handlers. UI props such as
50
- * `type`, `checked`, `disabled`, and `aria-pressed` belong to the caller — use
51
- * the state available on `options` (e.g. `value.selected`, `value.available`).
92
+ * Option value fields return the name, the value, and the selection handlers. Set the input type and the checked, disabled, and pressed states from the option state yourself.
52
93
  */
53
94
  type ProductFormRegister = {
54
95
  (field: "merchandiseId", opts: {}): ProductMerchandiseIdProps;
@@ -73,8 +114,14 @@ type ProductFormRegister = {
73
114
  (field: "addToCart", opts: {}): ProductAddToCartProps;
74
115
  };
75
116
  /**
76
- * Creates a {@link ProductFormRegister} function bound to the current variant
77
- * state and `selectOption` dispatcher.
117
+ * Creates the register function that returns props for each product form field.
118
+ *
119
+ * The merchandise ID field submits the variant that you pass at creation. Create a new register function each time the product form state changes. The product form hooks create a new register function for you.
120
+ *
121
+ * @param selectedVariant The variant that the merchandise ID field submits, or `null` when the selection doesn't resolve to a variant.
122
+ * @param selectOption The function that option value fields call with the option name and value.
123
+ * @returns A register function for the merchandise ID, quantity, option value, attribute, and add-to-cart fields.
124
+ * @publicDocs
78
125
  */
79
126
  declare function createProductFormRegister(selectedVariant: ProductVariantInput | null, selectOption: (name: string, value: string) => void): ProductFormRegister;
80
127
  //#endregion
@@ -1,7 +1,13 @@
1
1
  //#region src/core/product/form.ts
2
2
  /**
3
- * Creates a {@link ProductFormRegister} function bound to the current variant
4
- * state and `selectOption` dispatcher.
3
+ * Creates the register function that returns props for each product form field.
4
+ *
5
+ * The merchandise ID field submits the variant that you pass at creation. Create a new register function each time the product form state changes. The product form hooks create a new register function for you.
6
+ *
7
+ * @param selectedVariant The variant that the merchandise ID field submits, or `null` when the selection doesn't resolve to a variant.
8
+ * @param selectOption The function that option value fields call with the option name and value.
9
+ * @returns A register function for the merchandise ID, quantity, option value, attribute, and add-to-cart fields.
10
+ * @publicDocs
5
11
  */
6
12
  function createProductFormRegister(selectedVariant, selectOption) {
7
13
  return ((field, opts) => {
@@ -1 +1 @@
1
- {"version":3,"file":"form.mjs","names":[],"sources":["../../../src/core/product/form.ts"],"sourcesContent":["import type { ProductVariantInput } from \"./state\";\n\n/** Props returned by {@link ProductFormRegister} for the `merchandiseId` input. */\nexport interface ProductMerchandiseIdProps {\n name: \"merchandiseId\";\n /** The currently selected variant ID, or an empty string when no variant is resolved. */\n value: string;\n}\n\n/** Props returned by {@link ProductFormRegister} for a quantity input. */\nexport interface ProductQuantityProps {\n name: \"quantity\";\n value: string;\n}\n\n/** Props returned by {@link ProductFormRegister} for an uncontrolled quantity input. */\nexport interface ProductQuantityDefaultProps {\n name: \"quantity\";\n defaultValue: string;\n}\n\n/** Props returned by {@link ProductFormRegister} for a variant option value control. */\nexport interface ProductOptionValueProps {\n name: string;\n value: string;\n onChange: () => void;\n onClick: () => void;\n}\n\n/** Props returned by {@link ProductFormRegister} for the add-to-cart submit button. */\nexport interface ProductAddToCartProps {\n name: \"add-to-cart\";\n type: \"submit\";\n}\n\ntype AttributeValueName = `attributes.${string}`;\n\n/** Props returned by {@link ProductFormRegister} for a line-item attribute input. */\nexport interface ProductAttributeValueProps {\n name: AttributeValueName;\n value: string;\n}\n\n/** Props returned by {@link ProductFormRegister} for an uncontrolled line-item attribute input. */\nexport interface ProductAttributeDefaultValueProps {\n name: AttributeValueName;\n defaultValue: string;\n}\n\n/**\n * Register function returned by `useProductForm`.\n *\n * Product forms only register product-relevant fields:\n * `merchandiseId`, `quantity`, `optionValue`, `attributeValue`, and `addToCart`.\n *\n * Option values return form identity and activation handlers. UI props such as\n * `type`, `checked`, `disabled`, and `aria-pressed` belong to the caller — use\n * the state available on `options` (e.g. `value.selected`, `value.available`).\n */\nexport type ProductFormRegister = {\n (field: \"merchandiseId\", opts: {}): ProductMerchandiseIdProps;\n (field: \"quantity\", opts: { value: number }): ProductQuantityProps;\n (field: \"quantity\", opts: { defaultValue: number }): ProductQuantityDefaultProps;\n (field: \"optionValue\", opts: { optionName: string; value: string }): ProductOptionValueProps;\n (field: \"attributeValue\", opts: { key: string; value: string }): ProductAttributeValueProps;\n (\n field: \"attributeValue\",\n opts: { key: string; defaultValue: string },\n ): ProductAttributeDefaultValueProps;\n (field: \"addToCart\", opts: {}): ProductAddToCartProps;\n};\n\n/**\n * Creates a {@link ProductFormRegister} function bound to the current variant\n * state and `selectOption` dispatcher.\n */\nexport function createProductFormRegister(\n selectedVariant: ProductVariantInput | null,\n selectOption: (name: string, value: string) => void,\n): ProductFormRegister {\n return ((field: string, opts?: Record<string, unknown>) => {\n if (field === \"merchandiseId\") {\n return { name: \"merchandiseId\", value: selectedVariant?.id ?? \"\" };\n }\n\n if (field === \"quantity\") {\n if (typeof opts?.defaultValue === \"number\") {\n return { name: \"quantity\" as const, defaultValue: String(opts.defaultValue) };\n }\n const value = typeof opts?.value === \"number\" ? opts.value : 1;\n return { name: \"quantity\" as const, value: String(value) };\n }\n\n if (field === \"optionValue\") {\n const optionName = String(opts?.optionName ?? \"\");\n const value = String(opts?.value ?? \"\");\n const handleSelect = () => selectOption(optionName, value);\n return { name: optionName, value, onChange: handleSelect, onClick: handleSelect };\n }\n\n if (field === \"attributeValue\") {\n const key = String(opts?.key ?? \"\");\n if (!key) throw new TypeError('Product attribute values require a non-empty \"key\".');\n const name = `attributes.${key}` as AttributeValueName;\n if (opts && \"defaultValue\" in opts) {\n return { name, defaultValue: String(opts.defaultValue) };\n }\n return { name, value: String(opts?.value ?? \"\") };\n }\n\n if (field === \"addToCart\") {\n return { name: \"add-to-cart\", type: \"submit\" } satisfies ProductAddToCartProps;\n }\n\n throw new Error(`Unknown product form field: \"${field}\".`);\n }) as ProductFormRegister;\n}\n"],"mappings":";;;;;AA4EA,SAAgB,0BACd,iBACA,cACqB;AACrB,UAAS,OAAe,SAAmC;AACzD,MAAI,UAAU,gBACZ,QAAO;GAAE,MAAM;GAAiB,OAAO,iBAAiB,MAAM;GAAI;AAGpE,MAAI,UAAU,YAAY;AACxB,OAAI,OAAO,MAAM,iBAAiB,SAChC,QAAO;IAAE,MAAM;IAAqB,cAAc,OAAO,KAAK,aAAa;IAAE;GAE/E,MAAM,QAAQ,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AAC7D,UAAO;IAAE,MAAM;IAAqB,OAAO,OAAO,MAAM;IAAE;;AAG5D,MAAI,UAAU,eAAe;GAC3B,MAAM,aAAa,OAAO,MAAM,cAAc,GAAG;GACjD,MAAM,QAAQ,OAAO,MAAM,SAAS,GAAG;GACvC,MAAM,qBAAqB,aAAa,YAAY,MAAM;AAC1D,UAAO;IAAE,MAAM;IAAY;IAAO,UAAU;IAAc,SAAS;IAAc;;AAGnF,MAAI,UAAU,kBAAkB;GAC9B,MAAM,MAAM,OAAO,MAAM,OAAO,GAAG;AACnC,OAAI,CAAC,IAAK,OAAM,IAAI,UAAU,wDAAsD;GACpF,MAAM,OAAO,cAAc;AAC3B,OAAI,QAAQ,kBAAkB,KAC5B,QAAO;IAAE;IAAM,cAAc,OAAO,KAAK,aAAa;IAAE;AAE1D,UAAO;IAAE;IAAM,OAAO,OAAO,MAAM,SAAS,GAAG;IAAE;;AAGnD,MAAI,UAAU,YACZ,QAAO;GAAE,MAAM;GAAe,MAAM;GAAU;AAGhD,QAAM,IAAI,MAAM,gCAAgC,MAAM,IAAI"}
1
+ {"version":3,"file":"form.mjs","names":[],"sources":["../../../src/core/product/form.ts"],"sourcesContent":["import type { ProductVariantInput } from \"./state\";\n\n/**\n * Props for the hidden input that tells the cart which variant to add.\n *\n * @publicDocs\n */\nexport interface ProductMerchandiseIdProps {\n /** Field name that the cart reads as the variant to add. */\n name: \"merchandiseId\";\n /** Selected variant ID. Empty when the selection doesn't resolve to a variant. */\n value: string;\n}\n\n/**\n * Props for a controlled quantity input.\n *\n * @publicDocs\n */\nexport interface ProductQuantityProps {\n /** Field name that the cart reads as the quantity to add. */\n name: \"quantity\";\n /** Quantity to add. Defaults to `1`. */\n value: string;\n}\n\n/**\n * Props for an uncontrolled quantity input.\n *\n * @publicDocs\n */\nexport interface ProductQuantityDefaultProps {\n /** Field name that the cart reads as the quantity to add. */\n name: \"quantity\";\n /** Starting quantity for the input. */\n defaultValue: string;\n}\n\n/**\n * Props for a control that selects one option value, such as a swatch or a radio button.\n *\n * @publicDocs\n */\nexport interface ProductOptionValueProps {\n /** Option name, such as \"Color\". */\n name: string;\n /** Option value that the control selects, such as \"Red\". */\n value: string;\n /** Selects the option value in the product form. */\n onChange: () => void;\n /** Selects the option value in the product form. Matches the change handler. */\n onClick: () => void;\n}\n\n/**\n * Props for the button that submits the product form and adds the selected variant to the cart.\n *\n * @publicDocs\n */\nexport interface ProductAddToCartProps {\n /** Identifies the add-to-cart button. The button has no value, which tells the cart to add the variant from the merchandise ID field. */\n name: \"add-to-cart\";\n /** Makes the button submit the product form. */\n type: \"submit\";\n}\n\n/** Line-item attribute field name. The cart uses the text after `attributes.` as the attribute key. */\ntype AttributeValueName = `attributes.${string}`;\n\n/**\n * Props for an input that adds a custom attribute to the cart line, such as a gift message.\n *\n * @publicDocs\n */\nexport interface ProductAttributeValueProps {\n /** Field name in the format `attributes.<key>`. The cart saves the key and value as an attribute of the cart line. */\n name: AttributeValueName;\n /** Attribute value to save on the cart line. */\n value: string;\n}\n\n/**\n * Props for an uncontrolled input that adds a custom attribute to the cart line.\n *\n * @publicDocs\n */\nexport interface ProductAttributeDefaultValueProps {\n /** Field name in the format `attributes.<key>`. The cart saves the key and value as an attribute of the cart line. */\n name: AttributeValueName;\n /** Starting attribute value for the input. */\n defaultValue: string;\n}\n\n/**\n * Returns the props for one product form field. Spread the props on the matching input or button.\n *\n * Pass `merchandiseId`, `quantity`, `optionValue`, `attributeValue`, or `addToCart` as the field. Any other field name throws an error. An attribute value field with an empty key throws a `TypeError`.\n *\n * Option value fields return the name, the value, and the selection handlers. Set the input type and the checked, disabled, and pressed states from the option state yourself.\n */\nexport type ProductFormRegister = {\n (field: \"merchandiseId\", opts: {}): ProductMerchandiseIdProps;\n (field: \"quantity\", opts: { value: number }): ProductQuantityProps;\n (field: \"quantity\", opts: { defaultValue: number }): ProductQuantityDefaultProps;\n (field: \"optionValue\", opts: { optionName: string; value: string }): ProductOptionValueProps;\n (field: \"attributeValue\", opts: { key: string; value: string }): ProductAttributeValueProps;\n (\n field: \"attributeValue\",\n opts: { key: string; defaultValue: string },\n ): ProductAttributeDefaultValueProps;\n (field: \"addToCart\", opts: {}): ProductAddToCartProps;\n};\n\n/**\n * Creates the register function that returns props for each product form field.\n *\n * The merchandise ID field submits the variant that you pass at creation. Create a new register function each time the product form state changes. The product form hooks create a new register function for you.\n *\n * @param selectedVariant The variant that the merchandise ID field submits, or `null` when the selection doesn't resolve to a variant.\n * @param selectOption The function that option value fields call with the option name and value.\n * @returns A register function for the merchandise ID, quantity, option value, attribute, and add-to-cart fields.\n * @publicDocs\n */\nexport function createProductFormRegister(\n selectedVariant: ProductVariantInput | null,\n selectOption: (name: string, value: string) => void,\n): ProductFormRegister {\n return ((field: string, opts?: Record<string, unknown>) => {\n if (field === \"merchandiseId\") {\n return { name: \"merchandiseId\", value: selectedVariant?.id ?? \"\" };\n }\n\n if (field === \"quantity\") {\n if (typeof opts?.defaultValue === \"number\") {\n return { name: \"quantity\" as const, defaultValue: String(opts.defaultValue) };\n }\n const value = typeof opts?.value === \"number\" ? opts.value : 1;\n return { name: \"quantity\" as const, value: String(value) };\n }\n\n if (field === \"optionValue\") {\n const optionName = String(opts?.optionName ?? \"\");\n const value = String(opts?.value ?? \"\");\n const handleSelect = () => selectOption(optionName, value);\n return { name: optionName, value, onChange: handleSelect, onClick: handleSelect };\n }\n\n if (field === \"attributeValue\") {\n const key = String(opts?.key ?? \"\");\n if (!key) throw new TypeError('Product attribute values require a non-empty \"key\".');\n const name = `attributes.${key}` as AttributeValueName;\n if (opts && \"defaultValue\" in opts) {\n return { name, defaultValue: String(opts.defaultValue) };\n }\n return { name, value: String(opts?.value ?? \"\") };\n }\n\n if (field === \"addToCart\") {\n return { name: \"add-to-cart\", type: \"submit\" } satisfies ProductAddToCartProps;\n }\n\n throw new Error(`Unknown product form field: \"${field}\".`);\n }) as ProductFormRegister;\n}\n"],"mappings":";;;;;;;;;;;AA2HA,SAAgB,0BACd,iBACA,cACqB;AACrB,UAAS,OAAe,SAAmC;AACzD,MAAI,UAAU,gBACZ,QAAO;GAAE,MAAM;GAAiB,OAAO,iBAAiB,MAAM;GAAI;AAGpE,MAAI,UAAU,YAAY;AACxB,OAAI,OAAO,MAAM,iBAAiB,SAChC,QAAO;IAAE,MAAM;IAAqB,cAAc,OAAO,KAAK,aAAa;IAAE;GAE/E,MAAM,QAAQ,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AAC7D,UAAO;IAAE,MAAM;IAAqB,OAAO,OAAO,MAAM;IAAE;;AAG5D,MAAI,UAAU,eAAe;GAC3B,MAAM,aAAa,OAAO,MAAM,cAAc,GAAG;GACjD,MAAM,QAAQ,OAAO,MAAM,SAAS,GAAG;GACvC,MAAM,qBAAqB,aAAa,YAAY,MAAM;AAC1D,UAAO;IAAE,MAAM;IAAY;IAAO,UAAU;IAAc,SAAS;IAAc;;AAGnF,MAAI,UAAU,kBAAkB;GAC9B,MAAM,MAAM,OAAO,MAAM,OAAO,GAAG;AACnC,OAAI,CAAC,IAAK,OAAM,IAAI,UAAU,wDAAsD;GACpF,MAAM,OAAO,cAAc;AAC3B,OAAI,QAAQ,kBAAkB,KAC5B,QAAO;IAAE;IAAM,cAAc,OAAO,KAAK,aAAa;IAAE;AAE1D,UAAO;IAAE;IAAM,OAAO,OAAO,MAAM,SAAS,GAAG;IAAE;;AAGnD,MAAI,UAAU,YACZ,QAAO;GAAE,MAAM;GAAe,MAAM;GAAU;AAGhD,QAAM,IAAI,MAAM,gCAAgC,MAAM,IAAI"}
@@ -2,20 +2,17 @@ import { SelectedOption } from "./state.mjs";
2
2
 
3
3
  //#region src/core/product/options.d.ts
4
4
  /**
5
- * Extracts selected product options from search params.
5
+ * Reads the customer's option selection from a product URL's search params.
6
6
  *
7
- * Each query parameter is treated as an option name/value pair
8
- * (e.g. `?Color=Red&Size=M` → `[{name:"Color",value:"Red"},{name:"Size",value:"M"}]`).
7
+ * Each param becomes an option name and value. For example, `?Color=Red&Size=M`
8
+ * becomes Color set to Red and Size set to M. The function always skips the `variant` param,
9
+ * which holds a numeric variant ID in Liquid storefront URLs.
9
10
  *
10
- * The `variant` param is reserved for numeric variant ids (Liquid parity) and is
11
- * never treated as an option name, even when listed in `allowedOptionNames`.
11
+ * Pass the result as the `selectedOptions` argument of your Storefront API product query, or as the starting selection of a product form store.
12
12
  *
13
- * When `allowedOptionNames` is provided, the search params are filtered to only
14
- * entries whose decoded param name exactly matches a product option name.
15
- * Passing an empty array filters out every option.
13
+ * @param input The search params to read and an optional list of allowed option names. With `allowedOptionNames`, the function keeps only params whose name exactly matches an allowed name, and an empty list keeps no params. Pass the product's option names to keep unrelated params out of the selection.
16
14
  *
17
- * Pass the result to `createProductFormStore` as `selectedOptions`. Used as a
18
- * fallback when the product has no `selectedOrFirstAvailableVariant`.
15
+ * @returns The selected options, in the order that they appear in the URL.
19
16
  *
20
17
  * @example
21
18
  * ```ts
@@ -30,6 +27,7 @@ import { SelectedOption } from "./state.mjs";
30
27
  * allowedOptionNames: product.options.map((option) => option.name),
31
28
  * });
32
29
  * ```
30
+ * @publicDocs
33
31
  */
34
32
  declare function getSelectedProductOptions({
35
33
  searchParams,
@@ -5,20 +5,17 @@ import { VARIANT_SEARCH_PARAM } from "./url.mjs";
5
5
  const log = getLogger("product");
6
6
  const INCLUSIVE_RANGE_END_OFFSET = 1;
7
7
  /**
8
- * Extracts selected product options from search params.
8
+ * Reads the customer's option selection from a product URL's search params.
9
9
  *
10
- * Each query parameter is treated as an option name/value pair
11
- * (e.g. `?Color=Red&Size=M` → `[{name:"Color",value:"Red"},{name:"Size",value:"M"}]`).
10
+ * Each param becomes an option name and value. For example, `?Color=Red&Size=M`
11
+ * becomes Color set to Red and Size set to M. The function always skips the `variant` param,
12
+ * which holds a numeric variant ID in Liquid storefront URLs.
12
13
  *
13
- * The `variant` param is reserved for numeric variant ids (Liquid parity) and is
14
- * never treated as an option name, even when listed in `allowedOptionNames`.
14
+ * Pass the result as the `selectedOptions` argument of your Storefront API product query, or as the starting selection of a product form store.
15
15
  *
16
- * When `allowedOptionNames` is provided, the search params are filtered to only
17
- * entries whose decoded param name exactly matches a product option name.
18
- * Passing an empty array filters out every option.
16
+ * @param input The search params to read and an optional list of allowed option names. With `allowedOptionNames`, the function keeps only params whose name exactly matches an allowed name, and an empty list keeps no params. Pass the product's option names to keep unrelated params out of the selection.
19
17
  *
20
- * Pass the result to `createProductFormStore` as `selectedOptions`. Used as a
21
- * fallback when the product has no `selectedOrFirstAvailableVariant`.
18
+ * @returns The selected options, in the order that they appear in the URL.
22
19
  *
23
20
  * @example
24
21
  * ```ts
@@ -33,6 +30,7 @@ const INCLUSIVE_RANGE_END_OFFSET = 1;
33
30
  * allowedOptionNames: product.options.map((option) => option.name),
34
31
  * });
35
32
  * ```
33
+ * @publicDocs
36
34
  */
37
35
  function getSelectedProductOptions({ searchParams, allowedOptionNames }) {
38
36
  const allowedOptionNameSet = allowedOptionNames ? new Set(allowedOptionNames) : null;