@shopify/hydrogen 2026.4.5 → 2026.10.0-preview.2

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 (764) hide show
  1. package/README.md +73 -13
  2. package/bin/hydrogen.mjs +7 -0
  3. package/dist/cli/index.mjs +647 -0
  4. package/dist/client/client.d.mts +34 -0
  5. package/dist/client/client.mjs +206 -0
  6. package/dist/client/client.mjs.map +1 -0
  7. package/dist/client/errors.d.mts +48 -0
  8. package/dist/client/errors.mjs +52 -0
  9. package/dist/client/errors.mjs.map +1 -0
  10. package/dist/client/index.d.mts +4 -0
  11. package/dist/client/types.d.mts +147 -0
  12. package/dist/core/analytics/cart-tracker.d.mts +8 -0
  13. package/dist/core/analytics/cart-tracker.mjs +144 -0
  14. package/dist/core/analytics/cart-tracker.mjs.map +1 -0
  15. package/dist/core/analytics/events.d.mts +15 -0
  16. package/dist/core/analytics/events.mjs +15 -0
  17. package/dist/core/analytics/events.mjs.map +1 -0
  18. package/dist/core/analytics/index.d.mts +3 -0
  19. package/dist/core/analytics/types.d.mts +143 -0
  20. package/dist/core/analytics/utils/flatten-connection.d.mts +19 -0
  21. package/dist/core/analytics/utils/flatten-connection.mjs +25 -0
  22. package/dist/core/analytics/utils/flatten-connection.mjs.map +1 -0
  23. package/dist/core/cache/cache-status.mjs +17 -0
  24. package/dist/core/cache/cache-status.mjs.map +1 -0
  25. package/dist/core/cache/fetch-with-cache.d.mts +43 -0
  26. package/dist/core/cache/fetch-with-cache.mjs +188 -0
  27. package/dist/core/cache/fetch-with-cache.mjs.map +1 -0
  28. package/dist/core/cache/key.d.mts +6 -0
  29. package/dist/core/cache/key.mjs +27 -0
  30. package/dist/core/cache/key.mjs.map +1 -0
  31. package/dist/core/cache/run-with-cache.d.mts +36 -0
  32. package/dist/core/cache/run-with-cache.mjs +114 -0
  33. package/dist/core/cache/run-with-cache.mjs.map +1 -0
  34. package/dist/core/cache/store.d.mts +19 -0
  35. package/dist/core/cache/store.mjs +62 -0
  36. package/dist/core/cache/store.mjs.map +1 -0
  37. package/dist/core/cache/strategies.d.mts +39 -0
  38. package/dist/core/cache/strategies.mjs +70 -0
  39. package/dist/core/cache/strategies.mjs.map +1 -0
  40. package/dist/core/cart/actions.d.mts +50 -0
  41. package/dist/core/cart/actions.mjs +240 -0
  42. package/dist/core/cart/actions.mjs.map +1 -0
  43. package/dist/core/cart/attach-quantity-input.d.mts +5 -0
  44. package/dist/core/cart/attach-quantity-input.mjs +17 -0
  45. package/dist/core/cart/attach-quantity-input.mjs.map +1 -0
  46. package/dist/core/cart/buyer-identity-sync.d.mts +23 -0
  47. package/dist/core/cart/buyer-identity-sync.mjs +9 -0
  48. package/dist/core/cart/buyer-identity-sync.mjs.map +1 -0
  49. package/dist/core/cart/cart.d.mts +37 -0
  50. package/dist/core/cart/cart.mjs +1707 -0
  51. package/dist/core/cart/cart.mjs.map +1 -0
  52. package/dist/core/cart/cookie.d.mts +5 -0
  53. package/dist/core/cart/cookie.mjs +28 -0
  54. package/dist/core/cart/cookie.mjs.map +1 -0
  55. package/dist/core/cart/form.d.mts +130 -0
  56. package/dist/core/cart/form.mjs +75 -0
  57. package/dist/core/cart/form.mjs.map +1 -0
  58. package/dist/core/cart/get-cart.d.mts +25 -0
  59. package/dist/core/cart/get-cart.mjs +32 -0
  60. package/dist/core/cart/get-cart.mjs.map +1 -0
  61. package/dist/core/cart/index.d.mts +10 -0
  62. package/dist/core/cart/quantity.d.mts +10 -0
  63. package/dist/core/cart/quantity.mjs +14 -0
  64. package/dist/core/cart/quantity.mjs.map +1 -0
  65. package/dist/core/cart/queries.d.mts +4296 -0
  66. package/dist/core/cart/queries.mjs +70 -0
  67. package/dist/core/cart/queries.mjs.map +1 -0
  68. package/dist/core/cart/server-handlers.d.mts +82 -0
  69. package/dist/core/cart/server-handlers.mjs +322 -0
  70. package/dist/core/cart/server-handlers.mjs.map +1 -0
  71. package/dist/core/cart/state.d.mts +137 -0
  72. package/dist/core/cart/state.mjs +76 -0
  73. package/dist/core/cart/state.mjs.map +1 -0
  74. package/dist/core/cart/sync-quantity-inputs.mjs +16 -0
  75. package/dist/core/cart/sync-quantity-inputs.mjs.map +1 -0
  76. package/dist/core/collection/collection.d.mts +134 -0
  77. package/dist/core/collection/collection.mjs +170 -0
  78. package/dist/core/collection/collection.mjs.map +1 -0
  79. package/dist/core/collection/index.d.mts +6 -0
  80. package/dist/core/collection/reconciler.d.mts +32 -0
  81. package/dist/core/collection/reconciler.mjs +89 -0
  82. package/dist/core/collection/reconciler.mjs.map +1 -0
  83. package/dist/core/collection/state.d.mts +66 -0
  84. package/dist/core/collection/state.mjs +20 -0
  85. package/dist/core/collection/state.mjs.map +1 -0
  86. package/dist/core/collection/url.d.mts +71 -0
  87. package/dist/core/collection/url.mjs +376 -0
  88. package/dist/core/collection/url.mjs.map +1 -0
  89. package/dist/core/constants.d.mts +5 -0
  90. package/dist/core/constants.mjs +8 -0
  91. package/dist/core/constants.mjs.map +1 -0
  92. package/dist/core/headers.mjs +43 -0
  93. package/dist/core/headers.mjs.map +1 -0
  94. package/dist/core/index.d.mts +56 -0
  95. package/dist/core/index.mjs +45 -0
  96. package/dist/core/logging/logging.d.mts +21 -0
  97. package/dist/core/logging/logging.mjs +99 -0
  98. package/dist/core/logging/logging.mjs.map +1 -0
  99. package/dist/core/logging/types.d.mts +40 -0
  100. package/dist/core/logging/types.mjs +18 -0
  101. package/dist/core/logging/types.mjs.map +1 -0
  102. package/dist/core/money/cache.mjs +25 -0
  103. package/dist/core/money/cache.mjs.map +1 -0
  104. package/dist/core/money/format.d.mts +15 -0
  105. package/dist/core/money/format.mjs +243 -0
  106. package/dist/core/money/format.mjs.map +1 -0
  107. package/dist/core/money/types.d.mts +47 -0
  108. package/dist/core/observable.mjs +46 -0
  109. package/dist/core/observable.mjs.map +1 -0
  110. package/dist/core/predictive-search/constants.d.mts +5 -0
  111. package/dist/core/predictive-search/constants.mjs +6 -0
  112. package/dist/core/predictive-search/constants.mjs.map +1 -0
  113. package/dist/core/predictive-search/form.d.mts +31 -0
  114. package/dist/core/predictive-search/form.mjs +31 -0
  115. package/dist/core/predictive-search/form.mjs.map +1 -0
  116. package/dist/core/predictive-search/index.d.mts +6 -0
  117. package/dist/core/predictive-search/queries.d.mts +118 -0
  118. package/dist/core/predictive-search/queries.mjs +81 -0
  119. package/dist/core/predictive-search/queries.mjs.map +1 -0
  120. package/dist/core/predictive-search/search.d.mts +32 -0
  121. package/dist/core/predictive-search/search.mjs +74 -0
  122. package/dist/core/predictive-search/search.mjs.map +1 -0
  123. package/dist/core/predictive-search/server-handlers.d.mts +36 -0
  124. package/dist/core/predictive-search/server-handlers.mjs +113 -0
  125. package/dist/core/predictive-search/server-handlers.mjs.map +1 -0
  126. package/dist/core/predictive-search/store.d.mts +44 -0
  127. package/dist/core/predictive-search/store.mjs +224 -0
  128. package/dist/core/predictive-search/store.mjs.map +1 -0
  129. package/dist/core/predictive-search/url.d.mts +41 -0
  130. package/dist/core/predictive-search/url.mjs +71 -0
  131. package/dist/core/predictive-search/url.mjs.map +1 -0
  132. package/dist/core/product/accept-variant-id.mjs +98 -0
  133. package/dist/core/product/accept-variant-id.mjs.map +1 -0
  134. package/dist/core/product/form.d.mts +63 -0
  135. package/dist/core/product/form.mjs +44 -0
  136. package/dist/core/product/form.mjs.map +1 -0
  137. package/dist/core/product/index.d.mts +5 -0
  138. package/dist/core/product/options.d.mts +43 -0
  139. package/dist/core/product/options.mjs +296 -0
  140. package/dist/core/product/options.mjs.map +1 -0
  141. package/dist/core/product/product-form.d.mts +72 -0
  142. package/dist/core/product/product-form.mjs +158 -0
  143. package/dist/core/product/product-form.mjs.map +1 -0
  144. package/dist/core/product/state.d.mts +68 -0
  145. package/dist/core/product/url.d.mts +47 -0
  146. package/dist/core/product/url.mjs +69 -0
  147. package/dist/core/product/url.mjs.map +1 -0
  148. package/dist/core/request-context.d.mts +75 -0
  149. package/dist/core/request-context.mjs +141 -0
  150. package/dist/core/request-context.mjs.map +1 -0
  151. package/dist/core/request-routing/handle-shopify-redirects.d.mts +17 -0
  152. package/dist/core/request-routing/handle-shopify-redirects.mjs +31 -0
  153. package/dist/core/request-routing/handle-shopify-redirects.mjs.map +1 -0
  154. package/dist/core/request-routing/handle-shopify-routes.d.mts +14 -0
  155. package/dist/core/request-routing/handle-shopify-routes.mjs +42 -0
  156. package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -0
  157. package/dist/core/request-routing/interceptors/admin-redirect.mjs +12 -0
  158. package/dist/core/request-routing/interceptors/admin-redirect.mjs.map +1 -0
  159. package/dist/core/request-routing/interceptors/ajax-api.mjs +16 -0
  160. package/dist/core/request-routing/interceptors/ajax-api.mjs.map +1 -0
  161. package/dist/core/request-routing/interceptors/api-proxy.mjs +20 -0
  162. package/dist/core/request-routing/interceptors/api-proxy.mjs.map +1 -0
  163. package/dist/core/request-routing/interceptors/checkout.mjs +58 -0
  164. package/dist/core/request-routing/interceptors/checkout.mjs.map +1 -0
  165. package/dist/core/request-routing/interceptors/mcp-proxy.mjs +23 -0
  166. package/dist/core/request-routing/interceptors/mcp-proxy.mjs.map +1 -0
  167. package/dist/core/request-routing/interceptors/proxy.mjs +73 -0
  168. package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -0
  169. package/dist/core/request-routing/interceptors/query-param-redirect.mjs +22 -0
  170. package/dist/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -0
  171. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs +27 -0
  172. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -0
  173. package/dist/core/request-routing/interceptors/standard-routes.mjs +19 -0
  174. package/dist/core/request-routing/interceptors/standard-routes.mjs.map +1 -0
  175. package/dist/core/request-routing/interceptors/url-redirects.mjs +27 -0
  176. package/dist/core/request-routing/interceptors/url-redirects.mjs.map +1 -0
  177. package/dist/core/request-routing/interceptors/well-known.mjs +22 -0
  178. package/dist/core/request-routing/interceptors/well-known.mjs.map +1 -0
  179. package/dist/core/request-routing/registered-routes.d.mts +7 -0
  180. package/dist/core/request-routing/registered-routes.mjs +72 -0
  181. package/dist/core/request-routing/registered-routes.mjs.map +1 -0
  182. package/dist/core/request-routing/route-types.d.mts +56 -0
  183. package/dist/core/request-routing/safe-apply-response-headers.mjs +16 -0
  184. package/dist/core/request-routing/safe-apply-response-headers.mjs.map +1 -0
  185. package/dist/core/shop-pay/shop-pay.d.mts +102 -0
  186. package/dist/core/shop-pay/shop-pay.mjs +289 -0
  187. package/dist/core/shop-pay/shop-pay.mjs.map +1 -0
  188. package/dist/core/shopify-scripts/analytics.mjs +26 -0
  189. package/dist/core/shopify-scripts/analytics.mjs.map +1 -0
  190. package/dist/core/shopify-scripts/consent.mjs +17 -0
  191. package/dist/core/shopify-scripts/consent.mjs.map +1 -0
  192. package/dist/core/shopify-scripts/constants.mjs +19 -0
  193. package/dist/core/shopify-scripts/constants.mjs.map +1 -0
  194. package/dist/core/shopify-scripts/deprecated-cookies.mjs +97 -0
  195. package/dist/core/shopify-scripts/deprecated-cookies.mjs.map +1 -0
  196. package/dist/core/shopify-scripts/global.d.mts +1 -0
  197. package/dist/core/shopify-scripts/global.mjs +86 -0
  198. package/dist/core/shopify-scripts/global.mjs.map +1 -0
  199. package/dist/core/shopify-scripts/index.d.mts +27 -0
  200. package/dist/core/shopify-scripts/index.mjs +135 -0
  201. package/dist/core/shopify-scripts/index.mjs.map +1 -0
  202. package/dist/core/shopify-scripts/initialize.d.mts +18 -0
  203. package/dist/core/shopify-scripts/initialize.mjs +27 -0
  204. package/dist/core/shopify-scripts/initialize.mjs.map +1 -0
  205. package/dist/core/shopify-scripts/page-view.mjs +49 -0
  206. package/dist/core/shopify-scripts/page-view.mjs.map +1 -0
  207. package/dist/core/shopify-scripts/perfkit.mjs +33 -0
  208. package/dist/core/shopify-scripts/perfkit.mjs.map +1 -0
  209. package/dist/core/shopify-scripts/render.d.mts +1 -0
  210. package/dist/core/shopify-scripts/render.mjs +23 -0
  211. package/dist/core/shopify-scripts/render.mjs.map +1 -0
  212. package/dist/core/shopify-scripts/types.d.mts +76 -0
  213. package/dist/core/shopify-scripts/utils/cookie-domain.mjs +43 -0
  214. package/dist/core/shopify-scripts/utils/cookie-domain.mjs.map +1 -0
  215. package/dist/core/shopify-scripts/utils/inline-script.mjs +8 -0
  216. package/dist/core/shopify-scripts/utils/inline-script.mjs.map +1 -0
  217. package/dist/core/shopify-scripts/utils/navigation.mjs +46 -0
  218. package/dist/core/shopify-scripts/utils/navigation.mjs.map +1 -0
  219. package/dist/core/shopify-scripts/utils/tracking-values.mjs +65 -0
  220. package/dist/core/shopify-scripts/utils/tracking-values.mjs.map +1 -0
  221. package/dist/core/shopify-scripts/utils/uuid.mjs +17 -0
  222. package/dist/core/shopify-scripts/utils/uuid.mjs.map +1 -0
  223. package/dist/core/shopify-scripts/webmcp.mjs +22 -0
  224. package/dist/core/shopify-scripts/webmcp.mjs.map +1 -0
  225. package/dist/core/standard-routes/build.d.mts +34 -0
  226. package/dist/core/standard-routes/build.mjs +54 -0
  227. package/dist/core/standard-routes/build.mjs.map +1 -0
  228. package/dist/core/standard-routes/defaults.mjs +31 -0
  229. package/dist/core/standard-routes/defaults.mjs.map +1 -0
  230. package/dist/core/standard-routes/match.mjs +108 -0
  231. package/dist/core/standard-routes/match.mjs.map +1 -0
  232. package/dist/core/standard-routes/path.mjs +58 -0
  233. package/dist/core/standard-routes/path.mjs.map +1 -0
  234. package/dist/core/standard-routes/redirects.mjs +51 -0
  235. package/dist/core/standard-routes/redirects.mjs.map +1 -0
  236. package/dist/core/standard-routes/types.d.mts +95 -0
  237. package/dist/core/url.mjs +42 -0
  238. package/dist/core/url.mjs.map +1 -0
  239. package/dist/core/utils/load-script.mjs +34 -0
  240. package/dist/core/utils/load-script.mjs.map +1 -0
  241. package/dist/core/utils/parse-gid.mjs +33 -0
  242. package/dist/core/utils/parse-gid.mjs.map +1 -0
  243. package/dist/core/utils/record.mjs +8 -0
  244. package/dist/core/utils/record.mjs.map +1 -0
  245. package/dist/customer-account/client.d.mts +50 -0
  246. package/dist/customer-account/client.mjs +239 -0
  247. package/dist/customer-account/client.mjs.map +1 -0
  248. package/dist/customer-account/errors.d.mts +28 -0
  249. package/dist/customer-account/errors.mjs +39 -0
  250. package/dist/customer-account/errors.mjs.map +1 -0
  251. package/dist/customer-account/graphql.d.mts +29 -0
  252. package/dist/customer-account/graphql.mjs +39 -0
  253. package/dist/customer-account/graphql.mjs.map +1 -0
  254. package/dist/customer-account/index.d.mts +7 -0
  255. package/dist/customer-account/index.mjs +7 -0
  256. package/dist/customer-account/session.d.mts +117 -0
  257. package/dist/customer-account/session.mjs +733 -0
  258. package/dist/customer-account/session.mjs.map +1 -0
  259. package/dist/customer-account/type-resolver.d.mts +11 -0
  260. package/dist/customer-account-api-types.d.ts +3269 -3270
  261. package/dist/customer-account.schema.json +50753 -1
  262. package/dist/development/_virtual/_@oxc-project_runtime@0.127.0/helpers/taggedTemplateLiteral.mjs +7 -0
  263. package/dist/development/client/client.d.mts +34 -0
  264. package/dist/development/client/client.mjs +206 -0
  265. package/dist/development/client/client.mjs.map +1 -0
  266. package/dist/development/client/errors.d.mts +48 -0
  267. package/dist/development/client/errors.mjs +57 -0
  268. package/dist/development/client/errors.mjs.map +1 -0
  269. package/dist/development/client/index.d.mts +4 -0
  270. package/dist/development/client/types.d.mts +147 -0
  271. package/dist/development/core/analytics/cart-tracker.d.mts +8 -0
  272. package/dist/development/core/analytics/cart-tracker.mjs +144 -0
  273. package/dist/development/core/analytics/cart-tracker.mjs.map +1 -0
  274. package/dist/development/core/analytics/events.d.mts +15 -0
  275. package/dist/development/core/analytics/events.mjs +15 -0
  276. package/dist/development/core/analytics/events.mjs.map +1 -0
  277. package/dist/development/core/analytics/index.d.mts +3 -0
  278. package/dist/development/core/analytics/types.d.mts +143 -0
  279. package/dist/development/core/analytics/utils/flatten-connection.d.mts +19 -0
  280. package/dist/development/core/analytics/utils/flatten-connection.mjs +25 -0
  281. package/dist/development/core/analytics/utils/flatten-connection.mjs.map +1 -0
  282. package/dist/development/core/cache/cache-status.mjs +17 -0
  283. package/dist/development/core/cache/cache-status.mjs.map +1 -0
  284. package/dist/development/core/cache/fetch-with-cache.d.mts +43 -0
  285. package/dist/development/core/cache/fetch-with-cache.mjs +188 -0
  286. package/dist/development/core/cache/fetch-with-cache.mjs.map +1 -0
  287. package/dist/development/core/cache/key.d.mts +6 -0
  288. package/dist/development/core/cache/key.mjs +27 -0
  289. package/dist/development/core/cache/key.mjs.map +1 -0
  290. package/dist/development/core/cache/run-with-cache.d.mts +36 -0
  291. package/dist/development/core/cache/run-with-cache.mjs +114 -0
  292. package/dist/development/core/cache/run-with-cache.mjs.map +1 -0
  293. package/dist/development/core/cache/store.d.mts +19 -0
  294. package/dist/development/core/cache/store.mjs +62 -0
  295. package/dist/development/core/cache/store.mjs.map +1 -0
  296. package/dist/development/core/cache/strategies.d.mts +39 -0
  297. package/dist/development/core/cache/strategies.mjs +70 -0
  298. package/dist/development/core/cache/strategies.mjs.map +1 -0
  299. package/dist/development/core/cart/actions.d.mts +50 -0
  300. package/dist/development/core/cart/actions.mjs +240 -0
  301. package/dist/development/core/cart/actions.mjs.map +1 -0
  302. package/dist/development/core/cart/attach-quantity-input.d.mts +5 -0
  303. package/dist/development/core/cart/attach-quantity-input.mjs +17 -0
  304. package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -0
  305. package/dist/development/core/cart/buyer-identity-sync.d.mts +19 -0
  306. package/dist/development/core/cart/buyer-identity-sync.mjs +6 -0
  307. package/dist/development/core/cart/buyer-identity-sync.mjs.map +1 -0
  308. package/dist/development/core/cart/cart.d.mts +37 -0
  309. package/dist/development/core/cart/cart.mjs +1707 -0
  310. package/dist/development/core/cart/cart.mjs.map +1 -0
  311. package/dist/development/core/cart/cookie.d.mts +5 -0
  312. package/dist/development/core/cart/cookie.mjs +28 -0
  313. package/dist/development/core/cart/cookie.mjs.map +1 -0
  314. package/dist/development/core/cart/form.d.mts +130 -0
  315. package/dist/development/core/cart/form.mjs +75 -0
  316. package/dist/development/core/cart/form.mjs.map +1 -0
  317. package/dist/development/core/cart/get-cart.d.mts +25 -0
  318. package/dist/development/core/cart/get-cart.mjs +32 -0
  319. package/dist/development/core/cart/get-cart.mjs.map +1 -0
  320. package/dist/development/core/cart/index.d.mts +10 -0
  321. package/dist/development/core/cart/quantity.d.mts +10 -0
  322. package/dist/development/core/cart/quantity.mjs +14 -0
  323. package/dist/development/core/cart/quantity.mjs.map +1 -0
  324. package/dist/development/core/cart/queries.d.mts +4296 -0
  325. package/dist/development/core/cart/queries.mjs +70 -0
  326. package/dist/development/core/cart/queries.mjs.map +1 -0
  327. package/dist/development/core/cart/server-handlers.d.mts +82 -0
  328. package/dist/development/core/cart/server-handlers.mjs +322 -0
  329. package/dist/development/core/cart/server-handlers.mjs.map +1 -0
  330. package/dist/development/core/cart/state.d.mts +137 -0
  331. package/dist/development/core/cart/state.mjs +76 -0
  332. package/dist/development/core/cart/state.mjs.map +1 -0
  333. package/dist/development/core/cart/sync-quantity-inputs.mjs +16 -0
  334. package/dist/development/core/cart/sync-quantity-inputs.mjs.map +1 -0
  335. package/dist/development/core/collection/collection.d.mts +134 -0
  336. package/dist/development/core/collection/collection.mjs +170 -0
  337. package/dist/development/core/collection/collection.mjs.map +1 -0
  338. package/dist/development/core/collection/index.d.mts +6 -0
  339. package/dist/development/core/collection/reconciler.d.mts +32 -0
  340. package/dist/development/core/collection/reconciler.mjs +89 -0
  341. package/dist/development/core/collection/reconciler.mjs.map +1 -0
  342. package/dist/development/core/collection/state.d.mts +66 -0
  343. package/dist/development/core/collection/state.mjs +20 -0
  344. package/dist/development/core/collection/state.mjs.map +1 -0
  345. package/dist/development/core/collection/url.d.mts +71 -0
  346. package/dist/development/core/collection/url.mjs +376 -0
  347. package/dist/development/core/collection/url.mjs.map +1 -0
  348. package/dist/development/core/constants.mjs +7 -0
  349. package/dist/development/core/constants.mjs.map +1 -0
  350. package/dist/development/core/development.d.mts +56 -0
  351. package/dist/development/core/development.mjs +45 -0
  352. package/dist/development/core/headers.mjs +43 -0
  353. package/dist/development/core/headers.mjs.map +1 -0
  354. package/dist/development/core/index.d.mts +54 -0
  355. package/dist/development/core/logging/logging.d.mts +21 -0
  356. package/dist/development/core/logging/logging.mjs +99 -0
  357. package/dist/development/core/logging/logging.mjs.map +1 -0
  358. package/dist/development/core/logging/types.d.mts +40 -0
  359. package/dist/development/core/logging/types.mjs +18 -0
  360. package/dist/development/core/logging/types.mjs.map +1 -0
  361. package/dist/development/core/money/cache.mjs +25 -0
  362. package/dist/development/core/money/cache.mjs.map +1 -0
  363. package/dist/development/core/money/format.d.mts +15 -0
  364. package/dist/development/core/money/format.mjs +243 -0
  365. package/dist/development/core/money/format.mjs.map +1 -0
  366. package/dist/development/core/money/types.d.mts +47 -0
  367. package/dist/development/core/observable.mjs +46 -0
  368. package/dist/development/core/observable.mjs.map +1 -0
  369. package/dist/development/core/predictive-search/constants.d.mts +5 -0
  370. package/dist/development/core/predictive-search/constants.mjs +6 -0
  371. package/dist/development/core/predictive-search/constants.mjs.map +1 -0
  372. package/dist/development/core/predictive-search/form.d.mts +31 -0
  373. package/dist/development/core/predictive-search/form.mjs +31 -0
  374. package/dist/development/core/predictive-search/form.mjs.map +1 -0
  375. package/dist/development/core/predictive-search/index.d.mts +6 -0
  376. package/dist/development/core/predictive-search/queries.d.mts +118 -0
  377. package/dist/development/core/predictive-search/queries.mjs +81 -0
  378. package/dist/development/core/predictive-search/queries.mjs.map +1 -0
  379. package/dist/development/core/predictive-search/search.d.mts +32 -0
  380. package/dist/development/core/predictive-search/search.mjs +74 -0
  381. package/dist/development/core/predictive-search/search.mjs.map +1 -0
  382. package/dist/development/core/predictive-search/server-handlers.d.mts +36 -0
  383. package/dist/development/core/predictive-search/server-handlers.mjs +113 -0
  384. package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -0
  385. package/dist/development/core/predictive-search/store.d.mts +44 -0
  386. package/dist/development/core/predictive-search/store.mjs +224 -0
  387. package/dist/development/core/predictive-search/store.mjs.map +1 -0
  388. package/dist/development/core/predictive-search/url.d.mts +41 -0
  389. package/dist/development/core/predictive-search/url.mjs +71 -0
  390. package/dist/development/core/predictive-search/url.mjs.map +1 -0
  391. package/dist/development/core/product/accept-variant-id.mjs +98 -0
  392. package/dist/development/core/product/accept-variant-id.mjs.map +1 -0
  393. package/dist/development/core/product/form.d.mts +63 -0
  394. package/dist/development/core/product/form.mjs +44 -0
  395. package/dist/development/core/product/form.mjs.map +1 -0
  396. package/dist/development/core/product/index.d.mts +5 -0
  397. package/dist/development/core/product/options.d.mts +43 -0
  398. package/dist/development/core/product/options.mjs +296 -0
  399. package/dist/development/core/product/options.mjs.map +1 -0
  400. package/dist/development/core/product/product-form.d.mts +72 -0
  401. package/dist/development/core/product/product-form.mjs +158 -0
  402. package/dist/development/core/product/product-form.mjs.map +1 -0
  403. package/dist/development/core/product/state.d.mts +68 -0
  404. package/dist/development/core/product/url.d.mts +47 -0
  405. package/dist/development/core/product/url.mjs +69 -0
  406. package/dist/development/core/product/url.mjs.map +1 -0
  407. package/dist/development/core/request-context.d.mts +75 -0
  408. package/dist/development/core/request-context.mjs +141 -0
  409. package/dist/development/core/request-context.mjs.map +1 -0
  410. package/dist/development/core/request-routing/handle-shopify-redirects.d.mts +17 -0
  411. package/dist/development/core/request-routing/handle-shopify-redirects.mjs +31 -0
  412. package/dist/development/core/request-routing/handle-shopify-redirects.mjs.map +1 -0
  413. package/dist/development/core/request-routing/handle-shopify-routes.d.mts +1 -0
  414. package/dist/development/core/request-routing/handle-shopify-routes.development.d.mts +11 -0
  415. package/dist/development/core/request-routing/handle-shopify-routes.development.mjs +16 -0
  416. package/dist/development/core/request-routing/handle-shopify-routes.development.mjs.map +1 -0
  417. package/dist/development/core/request-routing/handle-shopify-routes.mjs +42 -0
  418. package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -0
  419. package/dist/development/core/request-routing/interceptors/admin-redirect.mjs +12 -0
  420. package/dist/development/core/request-routing/interceptors/admin-redirect.mjs.map +1 -0
  421. package/dist/development/core/request-routing/interceptors/ajax-api.mjs +16 -0
  422. package/dist/development/core/request-routing/interceptors/ajax-api.mjs.map +1 -0
  423. package/dist/development/core/request-routing/interceptors/api-proxy.mjs +20 -0
  424. package/dist/development/core/request-routing/interceptors/api-proxy.mjs.map +1 -0
  425. package/dist/development/core/request-routing/interceptors/checkout.mjs +58 -0
  426. package/dist/development/core/request-routing/interceptors/checkout.mjs.map +1 -0
  427. package/dist/development/core/request-routing/interceptors/graphiql.mjs +46 -0
  428. package/dist/development/core/request-routing/interceptors/graphiql.mjs.map +1 -0
  429. package/dist/development/core/request-routing/interceptors/mcp-proxy.mjs +23 -0
  430. package/dist/development/core/request-routing/interceptors/mcp-proxy.mjs.map +1 -0
  431. package/dist/development/core/request-routing/interceptors/proxy.mjs +73 -0
  432. package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -0
  433. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs +22 -0
  434. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -0
  435. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs +27 -0
  436. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -0
  437. package/dist/development/core/request-routing/interceptors/standard-routes.mjs +19 -0
  438. package/dist/development/core/request-routing/interceptors/standard-routes.mjs.map +1 -0
  439. package/dist/development/core/request-routing/interceptors/url-redirects.mjs +27 -0
  440. package/dist/development/core/request-routing/interceptors/url-redirects.mjs.map +1 -0
  441. package/dist/development/core/request-routing/interceptors/well-known.mjs +22 -0
  442. package/dist/development/core/request-routing/interceptors/well-known.mjs.map +1 -0
  443. package/dist/development/core/request-routing/registered-routes.d.mts +7 -0
  444. package/dist/development/core/request-routing/registered-routes.mjs +72 -0
  445. package/dist/development/core/request-routing/registered-routes.mjs.map +1 -0
  446. package/dist/development/core/request-routing/route-types.d.mts +56 -0
  447. package/dist/development/core/request-routing/safe-apply-response-headers.mjs +16 -0
  448. package/dist/development/core/request-routing/safe-apply-response-headers.mjs.map +1 -0
  449. package/dist/development/core/shop-pay/shop-pay.d.mts +102 -0
  450. package/dist/development/core/shop-pay/shop-pay.mjs +289 -0
  451. package/dist/development/core/shop-pay/shop-pay.mjs.map +1 -0
  452. package/dist/development/core/shopify-scripts/analytics.mjs +26 -0
  453. package/dist/development/core/shopify-scripts/analytics.mjs.map +1 -0
  454. package/dist/development/core/shopify-scripts/consent.mjs +17 -0
  455. package/dist/development/core/shopify-scripts/consent.mjs.map +1 -0
  456. package/dist/development/core/shopify-scripts/constants.mjs +19 -0
  457. package/dist/development/core/shopify-scripts/constants.mjs.map +1 -0
  458. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs +97 -0
  459. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs.map +1 -0
  460. package/dist/development/core/shopify-scripts/global.d.mts +1 -0
  461. package/dist/development/core/shopify-scripts/global.mjs +86 -0
  462. package/dist/development/core/shopify-scripts/global.mjs.map +1 -0
  463. package/dist/development/core/shopify-scripts/index.d.mts +27 -0
  464. package/dist/development/core/shopify-scripts/index.mjs +145 -0
  465. package/dist/development/core/shopify-scripts/index.mjs.map +1 -0
  466. package/dist/development/core/shopify-scripts/initialize.d.mts +18 -0
  467. package/dist/development/core/shopify-scripts/initialize.mjs +27 -0
  468. package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -0
  469. package/dist/development/core/shopify-scripts/page-view.mjs +49 -0
  470. package/dist/development/core/shopify-scripts/page-view.mjs.map +1 -0
  471. package/dist/development/core/shopify-scripts/perfkit.mjs +33 -0
  472. package/dist/development/core/shopify-scripts/perfkit.mjs.map +1 -0
  473. package/dist/development/core/shopify-scripts/render.d.mts +1 -0
  474. package/dist/development/core/shopify-scripts/render.mjs +23 -0
  475. package/dist/development/core/shopify-scripts/render.mjs.map +1 -0
  476. package/dist/development/core/shopify-scripts/types.d.mts +76 -0
  477. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs +43 -0
  478. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs.map +1 -0
  479. package/dist/development/core/shopify-scripts/utils/inline-script.mjs +8 -0
  480. package/dist/development/core/shopify-scripts/utils/inline-script.mjs.map +1 -0
  481. package/dist/development/core/shopify-scripts/utils/navigation.mjs +46 -0
  482. package/dist/development/core/shopify-scripts/utils/navigation.mjs.map +1 -0
  483. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs +65 -0
  484. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs.map +1 -0
  485. package/dist/development/core/shopify-scripts/utils/uuid.mjs +17 -0
  486. package/dist/development/core/shopify-scripts/utils/uuid.mjs.map +1 -0
  487. package/dist/development/core/shopify-scripts/webmcp.mjs +22 -0
  488. package/dist/development/core/shopify-scripts/webmcp.mjs.map +1 -0
  489. package/dist/development/core/standard-routes/build.d.mts +34 -0
  490. package/dist/development/core/standard-routes/build.mjs +54 -0
  491. package/dist/development/core/standard-routes/build.mjs.map +1 -0
  492. package/dist/development/core/standard-routes/defaults.mjs +31 -0
  493. package/dist/development/core/standard-routes/defaults.mjs.map +1 -0
  494. package/dist/development/core/standard-routes/match.mjs +108 -0
  495. package/dist/development/core/standard-routes/match.mjs.map +1 -0
  496. package/dist/development/core/standard-routes/path.mjs +58 -0
  497. package/dist/development/core/standard-routes/path.mjs.map +1 -0
  498. package/dist/development/core/standard-routes/redirects.mjs +51 -0
  499. package/dist/development/core/standard-routes/redirects.mjs.map +1 -0
  500. package/dist/development/core/standard-routes/types.d.mts +95 -0
  501. package/dist/development/core/types.d.mts +11 -0
  502. package/dist/development/core/url.mjs +42 -0
  503. package/dist/development/core/url.mjs.map +1 -0
  504. package/dist/development/core/utils/load-script.mjs +34 -0
  505. package/dist/development/core/utils/load-script.mjs.map +1 -0
  506. package/dist/development/core/utils/parse-gid.mjs +33 -0
  507. package/dist/development/core/utils/parse-gid.mjs.map +1 -0
  508. package/dist/development/customer-account/session.d.mts +46 -0
  509. package/dist/development/customer-account/session.mjs +21 -0
  510. package/dist/development/customer-account/session.mjs.map +1 -0
  511. package/dist/development/globals.d.mts +64 -0
  512. package/dist/development/graphql/generated/customer-account-api-types.d.mts +13 -0
  513. package/dist/development/graphql/generated/graphql-env.d.mts +19280 -0
  514. package/dist/development/graphql/generated/storefront-api-types.d.mts +47 -0
  515. package/dist/development/graphql/graphql.d.mts +40 -0
  516. package/dist/development/graphql/graphql.mjs +16 -0
  517. package/dist/development/graphql/graphql.mjs.map +1 -0
  518. package/dist/development/graphql/index.d.mts +2 -0
  519. package/dist/development/graphql/scalars.d.mts +15 -0
  520. package/dist/development/graphql/type-resolver.d.mts +229 -0
  521. package/dist/development/react/cart.d.mts +44 -0
  522. package/dist/development/react/cart.mjs +155 -0
  523. package/dist/development/react/cart.mjs.map +1 -0
  524. package/dist/development/react/collection.d.mts +91 -0
  525. package/dist/development/react/collection.mjs +129 -0
  526. package/dist/development/react/collection.mjs.map +1 -0
  527. package/dist/development/react/index.d.mts +13 -0
  528. package/dist/development/react/index.mjs +10 -0
  529. package/dist/development/react/predictive-search.d.mts +49 -0
  530. package/dist/development/react/predictive-search.mjs +133 -0
  531. package/dist/development/react/predictive-search.mjs.map +1 -0
  532. package/dist/development/react/product.d.mts +72 -0
  533. package/dist/development/react/product.mjs +127 -0
  534. package/dist/development/react/product.mjs.map +1 -0
  535. package/dist/development/react/shop-pay.d.mts +9 -0
  536. package/dist/development/react/shop-pay.mjs +16 -0
  537. package/dist/development/react/shop-pay.mjs.map +1 -0
  538. package/dist/development/react/shopify-scripts.d.mts +20 -0
  539. package/dist/development/react/shopify-scripts.mjs +39 -0
  540. package/dist/development/react/shopify-scripts.mjs.map +1 -0
  541. package/dist/development/vendor/standard-actions.d.mts +134 -0
  542. package/dist/development/vendor/standard-events.d.mts +1107 -0
  543. package/dist/development/vue/cart.d.mts +46 -0
  544. package/dist/development/vue/cart.mjs +122 -0
  545. package/dist/development/vue/cart.mjs.map +1 -0
  546. package/dist/development/vue/collection.d.mts +46 -0
  547. package/dist/development/vue/collection.mjs +108 -0
  548. package/dist/development/vue/collection.mjs.map +1 -0
  549. package/dist/development/vue/index.d.mts +11 -0
  550. package/dist/development/vue/index.mjs +8 -0
  551. package/dist/development/vue/predictive-search.d.mts +124 -0
  552. package/dist/development/vue/predictive-search.mjs +171 -0
  553. package/dist/development/vue/predictive-search.mjs.map +1 -0
  554. package/dist/development/vue/product.d.mts +57 -0
  555. package/dist/development/vue/product.mjs +167 -0
  556. package/dist/development/vue/product.mjs.map +1 -0
  557. package/dist/development/vue/shop-pay.d.mts +25 -0
  558. package/dist/development/vue/shop-pay.mjs +35 -0
  559. package/dist/development/vue/shop-pay.mjs.map +1 -0
  560. package/dist/development/vue/shopify-scripts.d.mts +127 -0
  561. package/dist/development/vue/shopify-scripts.mjs +79 -0
  562. package/dist/development/vue/shopify-scripts.mjs.map +1 -0
  563. package/dist/globals.d.mts +64 -0
  564. package/dist/graphql/generated/customer-account-api-types.d.mts +13 -0
  565. package/dist/graphql/generated/customer-account-graphql-env.d.mts +22170 -0
  566. package/dist/graphql/generated/graphql-env.d.mts +19280 -0
  567. package/dist/graphql/generated/storefront-api-types.d.mts +47 -0
  568. package/dist/graphql/graphql.d.mts +40 -0
  569. package/dist/graphql/graphql.mjs +16 -0
  570. package/dist/graphql/graphql.mjs.map +1 -0
  571. package/dist/graphql/index.d.mts +2 -0
  572. package/dist/graphql/scalars.d.mts +26 -0
  573. package/dist/graphql/type-resolver.d.mts +229 -0
  574. package/dist/react/cart.d.mts +44 -0
  575. package/dist/react/cart.mjs +155 -0
  576. package/dist/react/cart.mjs.map +1 -0
  577. package/dist/react/collection.d.mts +91 -0
  578. package/dist/react/collection.mjs +129 -0
  579. package/dist/react/collection.mjs.map +1 -0
  580. package/dist/react/index.d.mts +13 -0
  581. package/dist/react/index.mjs +10 -0
  582. package/dist/react/predictive-search.d.mts +49 -0
  583. package/dist/react/predictive-search.mjs +133 -0
  584. package/dist/react/predictive-search.mjs.map +1 -0
  585. package/dist/react/product.d.mts +72 -0
  586. package/dist/react/product.mjs +127 -0
  587. package/dist/react/product.mjs.map +1 -0
  588. package/dist/react/shop-pay.d.mts +9 -0
  589. package/dist/react/shop-pay.mjs +16 -0
  590. package/dist/react/shop-pay.mjs.map +1 -0
  591. package/dist/react/shopify-scripts.d.mts +20 -0
  592. package/dist/react/shopify-scripts.mjs +39 -0
  593. package/dist/react/shopify-scripts.mjs.map +1 -0
  594. package/dist/storefront-api-types.d.ts +2351 -2390
  595. package/dist/storefront.schema.json +41534 -1
  596. package/dist/ts-plugin/index.cjs +68 -0
  597. package/dist/ts-plugin/index.cjs.map +1 -0
  598. package/dist/ts-plugin/index.d.cts +4 -0
  599. package/dist/vendor/standard-actions.d.mts +134 -0
  600. package/dist/vendor/standard-events.d.mts +1107 -0
  601. package/dist/vite/certificate-prompt.mjs +24 -0
  602. package/dist/vite/certificate-prompt.mjs.map +1 -0
  603. package/dist/vite/index.d.mts +2 -0
  604. package/dist/vite/index.mjs +3 -0
  605. package/dist/vite/local-https.d.mts +47 -0
  606. package/dist/vite/local-https.mjs +276 -0
  607. package/dist/vite/local-https.mjs.map +1 -0
  608. package/dist/vite/mkcert.mjs +141 -0
  609. package/dist/vite/mkcert.mjs.map +1 -0
  610. package/dist/vue/cart.d.mts +46 -0
  611. package/dist/vue/cart.mjs +122 -0
  612. package/dist/vue/cart.mjs.map +1 -0
  613. package/dist/vue/collection.d.mts +46 -0
  614. package/dist/vue/collection.mjs +108 -0
  615. package/dist/vue/collection.mjs.map +1 -0
  616. package/dist/vue/index.d.mts +11 -0
  617. package/dist/vue/index.mjs +8 -0
  618. package/dist/vue/predictive-search.d.mts +124 -0
  619. package/dist/vue/predictive-search.mjs +171 -0
  620. package/dist/vue/predictive-search.mjs.map +1 -0
  621. package/dist/vue/product.d.mts +57 -0
  622. package/dist/vue/product.mjs +167 -0
  623. package/dist/vue/product.mjs.map +1 -0
  624. package/dist/vue/shop-pay.d.mts +25 -0
  625. package/dist/vue/shop-pay.mjs +35 -0
  626. package/dist/vue/shop-pay.mjs.map +1 -0
  627. package/dist/vue/shopify-scripts.d.mts +127 -0
  628. package/dist/vue/shopify-scripts.mjs +79 -0
  629. package/dist/vue/shopify-scripts.mjs.map +1 -0
  630. package/package.json +116 -86
  631. package/skills/hydrogen-analytics/SKILL.md +80 -0
  632. package/skills/hydrogen-analytics/references/react.md +160 -0
  633. package/skills/hydrogen-analytics/references/vue.md +80 -0
  634. package/skills/hydrogen-cart-drawer/SKILL.md +248 -0
  635. package/skills/hydrogen-cart-drawer/references/accessibility.md +109 -0
  636. package/skills/hydrogen-cart-drawer/references/css.md +67 -0
  637. package/skills/hydrogen-cart-ui/SKILL.md +224 -0
  638. package/skills/hydrogen-cart-ui/references/nextjs.md +89 -0
  639. package/skills/hydrogen-cart-ui/references/react.md +285 -0
  640. package/skills/hydrogen-cart-ui/references/vue.md +228 -0
  641. package/skills/hydrogen-collection-browser/SKILL.md +83 -0
  642. package/skills/hydrogen-collection-browser/references/nextjs.md +118 -0
  643. package/skills/hydrogen-collection-browser/references/nuxt.md +108 -0
  644. package/skills/hydrogen-collection-browser/references/react.md +301 -0
  645. package/skills/hydrogen-customer-account/SKILL.md +107 -0
  646. package/skills/hydrogen-image/SKILL.md +65 -0
  647. package/skills/hydrogen-local-https/SKILL.md +142 -0
  648. package/skills/hydrogen-markets/SKILL.md +221 -0
  649. package/skills/hydrogen-markets/references/nextjs.md +157 -0
  650. package/skills/hydrogen-money/SKILL.md +67 -0
  651. package/skills/hydrogen-oxygen/SKILL.md +245 -0
  652. package/skills/hydrogen-oxygen/references/react-router.md +230 -0
  653. package/skills/hydrogen-predictive-search/SKILL.md +130 -0
  654. package/skills/hydrogen-predictive-search/references/react.md +32 -0
  655. package/skills/hydrogen-predictive-search/references/vue.md +40 -0
  656. package/skills/hydrogen-request-handlers/SKILL.md +107 -0
  657. package/skills/hydrogen-request-handlers/references/frameworks.md +179 -0
  658. package/skills/hydrogen-request-handlers/references/nextjs.md +137 -0
  659. package/skills/hydrogen-request-handlers/references/nuxt.md +215 -0
  660. package/skills/hydrogen-routing/SKILL.md +171 -0
  661. package/skills/hydrogen-setup/SKILL.md +50 -0
  662. package/skills/hydrogen-setup/references/analytics.md +624 -0
  663. package/skills/hydrogen-setup/steps/1-inspect-app.md +62 -0
  664. package/skills/hydrogen-setup/steps/10-analytics.md +10 -0
  665. package/skills/hydrogen-setup/steps/11-verify.md +31 -0
  666. package/skills/hydrogen-setup/steps/2-scaffold.md +86 -0
  667. package/skills/hydrogen-setup/steps/3-build-home-page.md +37 -0
  668. package/skills/hydrogen-setup/steps/4-collection-and-search.md +14 -0
  669. package/skills/hydrogen-setup/steps/5-cart.md +10 -0
  670. package/skills/hydrogen-setup/steps/6-product-detail-page.md +416 -0
  671. package/skills/hydrogen-setup/steps/7-shopify-runtime-scripts.md +69 -0
  672. package/skills/hydrogen-setup/steps/8-cart-drawer-and-navbar.md +35 -0
  673. package/skills/hydrogen-setup/steps/9-account-page.md +54 -0
  674. package/skills/hydrogen-shop-pay/SKILL.md +79 -0
  675. package/skills/hydrogen-shop-pay/references/core.md +66 -0
  676. package/skills/hydrogen-shop-pay/references/react.md +50 -0
  677. package/skills/hydrogen-shop-pay/references/vue.md +39 -0
  678. package/skills/hydrogen-smoke-test/SKILL.md +205 -0
  679. package/skills/hydrogen-storefront-client/SKILL.md +322 -0
  680. package/skills/hydrogen-storefront-client/references/astro.md +125 -0
  681. package/skills/hydrogen-storefront-client/references/caching.md +25 -0
  682. package/skills/hydrogen-storefront-client/references/nextjs.md +121 -0
  683. package/skills/hydrogen-storefront-client/references/nuxt.md +121 -0
  684. package/skills/hydrogen-storefront-client/references/query-validation.md +95 -0
  685. package/skills/hydrogen-storefront-client/references/react-router.md +81 -0
  686. package/skills/hydrogen-storefront-client/references/solidstart.md +131 -0
  687. package/skills/hydrogen-storefront-client/references/sveltekit.md +169 -0
  688. package/skills/hydrogen-variant-form/SKILL.md +267 -0
  689. package/skills/hydrogen-variant-form/references/nextjs.md +207 -0
  690. package/skills/hydrogen-variant-form/references/nuxt.md +95 -0
  691. package/skills/hydrogen-variant-form/references/react.md +71 -0
  692. package/dist/dev/get-virtual-routes-H72465VB.js +0 -74
  693. package/dist/dev/hydrogen-routes.d.ts +0 -5
  694. package/dist/dev/hydrogen-routes.js +0 -25
  695. package/dist/development/get-virtual-routes-ACFZHU42.js +0 -71
  696. package/dist/development/get-virtual-routes-ACFZHU42.js.map +0 -1
  697. package/dist/development/index.cjs +0 -6978
  698. package/dist/development/index.cjs.map +0 -1
  699. package/dist/development/index.js +0 -6631
  700. package/dist/development/index.js.map +0 -1
  701. package/dist/development/log-seo-tags-IG37ONQ2.js +0 -74
  702. package/dist/development/log-seo-tags-IG37ONQ2.js.map +0 -1
  703. package/dist/development/react-router-preset.d.ts +0 -45
  704. package/dist/development/react-router-preset.js +0 -49
  705. package/dist/development/react-router-preset.js.map +0 -1
  706. package/dist/index.cjs +0 -1
  707. package/dist/oxygen/index.d.ts +0 -197
  708. package/dist/oxygen/index.js +0 -107
  709. package/dist/production/get-virtual-routes-ZZEGQQDV.js +0 -3
  710. package/dist/production/get-virtual-routes-ZZEGQQDV.js.map +0 -1
  711. package/dist/production/index.cjs +0 -1019
  712. package/dist/production/index.cjs.map +0 -1
  713. package/dist/production/index.d.cts +0 -3260
  714. package/dist/production/index.d.ts +0 -3260
  715. package/dist/production/index.js +0 -1019
  716. package/dist/production/index.js.map +0 -1
  717. package/dist/production/log-seo-tags-TY72EQWZ.js +0 -2
  718. package/dist/production/log-seo-tags-TY72EQWZ.js.map +0 -1
  719. package/dist/production/react-router-preset.d.ts +0 -45
  720. package/dist/production/react-router-preset.js +0 -11
  721. package/dist/react-router.d.ts +0 -75
  722. package/dist/vite/chunk-4O5OI3OO.js +0 -127
  723. package/dist/vite/chunk-FAQ7TVHC.js +0 -9
  724. package/dist/vite/chunk-QXBQVDMY.js +0 -68
  725. package/dist/vite/compat-date.d.ts +0 -3
  726. package/dist/vite/compat-date.js +0 -6
  727. package/dist/vite/get-virtual-routes.d.ts +0 -21
  728. package/dist/vite/get-virtual-routes.js +0 -74
  729. package/dist/vite/hydrogen-middleware.d.ts +0 -9
  730. package/dist/vite/hydrogen-middleware.js +0 -7
  731. package/dist/vite/plugin.d.ts +0 -22
  732. package/dist/vite/plugin.js +0 -171
  733. package/dist/vite/request-events.d.ts +0 -144
  734. package/dist/vite/request-events.js +0 -10
  735. package/dist/vite/types.d.ts +0 -5
  736. package/dist/vite/types.js +0 -0
  737. package/dist/vite/virtual-routes/assets/debug-network.css +0 -609
  738. package/dist/vite/virtual-routes/assets/favicon-dark.svg +0 -20
  739. package/dist/vite/virtual-routes/assets/favicon.svg +0 -28
  740. package/dist/vite/virtual-routes/assets/inter-variable-font.woff2 +0 -0
  741. package/dist/vite/virtual-routes/assets/jetbrainsmono-variable-font.woff2 +0 -0
  742. package/dist/vite/virtual-routes/assets/styles.css +0 -249
  743. package/dist/vite/virtual-routes/components/FlameChartWrapper.jsx +0 -123
  744. package/dist/vite/virtual-routes/components/HydrogenLogoBaseBW.jsx +0 -32
  745. package/dist/vite/virtual-routes/components/HydrogenLogoBaseColor.jsx +0 -47
  746. package/dist/vite/virtual-routes/components/IconBanner.jsx +0 -292
  747. package/dist/vite/virtual-routes/components/IconClose.jsx +0 -38
  748. package/dist/vite/virtual-routes/components/IconDiscard.jsx +0 -44
  749. package/dist/vite/virtual-routes/components/IconError.jsx +0 -61
  750. package/dist/vite/virtual-routes/components/IconGithub.jsx +0 -23
  751. package/dist/vite/virtual-routes/components/IconTwitter.jsx +0 -21
  752. package/dist/vite/virtual-routes/components/Layout.jsx +0 -7
  753. package/dist/vite/virtual-routes/components/RequestDetails.jsx +0 -171
  754. package/dist/vite/virtual-routes/components/RequestTable.jsx +0 -91
  755. package/dist/vite/virtual-routes/components/RequestWaterfall.jsx +0 -151
  756. package/dist/vite/virtual-routes/layout.jsx +0 -33
  757. package/dist/vite/virtual-routes/lib/useDebugNetworkServer.jsx +0 -178
  758. package/dist/vite/virtual-routes/routes/[.]well-known.appspecific.com[.]chrome[.]devtools[.]json.jsx +0 -37
  759. package/dist/vite/virtual-routes/routes/[.]well-known.appspecific.com[.]chrome[.]devtools[.]json.tsx +0 -79
  760. package/dist/vite/virtual-routes/routes/graphiql.jsx +0 -5
  761. package/dist/vite/virtual-routes/routes/index.jsx +0 -265
  762. package/dist/vite/virtual-routes/routes/subrequest-profiler.jsx +0 -243
  763. package/dist/vite/virtual-routes/virtual-root-with-layout.jsx +0 -63
  764. package/dist/vite/virtual-routes/virtual-root.jsx +0 -67
@@ -0,0 +1,267 @@
1
+ ---
2
+ name: hydrogen-variant-form
3
+ description: >
4
+ Behavioral guide for building product variant selection UI with
5
+ @shopify/hydrogen. Use when writing, modifying, or
6
+ reviewing product option selectors, variant pickers, or add-to-cart forms.
7
+ Framework agnostic.
8
+ ---
9
+
10
+ # Product Form Primitive
11
+
12
+ The product form primitive is a client-side store that computes per-option-value existence, availability, and selection state from Shopify's encoded variant fields. Variant selection performs no network requests; it is derived from product data provided by the Storefront API, combined with live cart state for error surfacing and line-item matching. Form submission is delegated to the cart store. Framework-specific bindings are thin wrappers over the core `createProductFormStore`.
13
+
14
+ ## How the store works
15
+
16
+ The store holds a `ProductFormStoreState` and notifies subscribers on change. It is initialized with a product object and a `CartStore` instance. The product includes `encodedVariantExistence`, `encodedVariantAvailability`, product options (each with `firstSelectableVariant` per option value), a sparse variant cache (`adjacentVariants`), and a nullable `selectedOrFirstAvailableVariant`.
17
+
18
+ On initialization:
19
+ 1. The store uses the encoded fields, when present, to determine which option-value combinations **exist** and which are **available** (in stock). Matching is symmetric: every option value is evaluated against all selected options that belong to the current product option matrix, regardless of option order.
20
+ 2. It stitches together the sparse variant cache (`adjacentVariants` + `firstSelectableVariant` per option value + `selectedOrFirstAvailableVariant`) to resolve a concrete `variant` object per option value where possible.
21
+ 3. It sets the initial selection from `selectedOrFirstAvailableVariant` (server-resolved), falling back to explicitly provided `selectedOptions`, falling back to empty selection.
22
+ 4. It subscribes to the `CartStore` to derive `matchedLineItem` and `errors` reactively.
23
+
24
+ On `selectOption(name, value)`:
25
+ 1. The store validates that the option name and value exist on the product.
26
+ 2. It validates that the combination exists when Shopify provides an encoded existence field.
27
+ 3. It updates `selectedOptions` and recomputes the full options grid.
28
+ 4. It returns a `VariantSelectionResult`: `resolved` (full variant matched), `unresolved` (valid, but no full variant resolved locally), or `invalid` (unknown or non-existent, with a `reason` string).
29
+
30
+ The store does **not** own URL synchronization or navigation. Add-to-cart mutation logic belongs to the cart store and is reached through `handleFormSubmit(event)`.
31
+
32
+ ## State shape
33
+
34
+ ```ts
35
+ interface ProductFormStoreState<TVariant> {
36
+ options: VariantOptionState<TVariant>[];
37
+ selectedOptions: SelectedOption[];
38
+ selectedVariant: TVariant | null;
39
+ errors: ProductFormErrors;
40
+ matchedLineItem: CartLine | null;
41
+ }
42
+ ```
43
+
44
+ `options` is the computed grid — one entry per product option, each containing its values:
45
+
46
+ - `value.name` — the option value label (e.g. "Red", "Small").
47
+ - `value.selected` — whether this value is part of the current selection.
48
+ - `value.exists` — resolved from `encodedVariantExistence` when present. `false` means no variant exists for the current symmetric selection constraints — the control should be disabled and visually de-emphasized.
49
+ - `value.available` — resolved from `encodedVariantAvailability` when present. `false` means no available variant was found for the current symmetric selection constraints.
50
+ - `value.variant` — the resolved variant object, or `null` if the combination is not in the local cache.
51
+ - `value.selectedOptions` — the full option tuple that would result from selecting this value. Used for URL construction.
52
+ - `value.handle` — the product handle for this variant. Differs from the current product's handle in combined listings.
53
+
54
+ `selectedOptions` is the current selection in product-option order (e.g. `[{name: "Size", value: "Small"}, {name: "Color", value: "Red"}]`).
55
+
56
+ `selectedVariant` is the currently resolved variant, or `null` when the selection is incomplete or the variant is not in the local cache.
57
+
58
+ `errors` surfaces cart errors relevant to this product form:
59
+ - `userErrors` — merged cart-level and line-level user errors for the matched line item.
60
+ - `warnings` — merged cart-level and line-level warnings.
61
+ - `networkErrors` — cart network errors.
62
+
63
+ `matchedLineItem` is the cart line whose `merchandise.id` matches the selected variant's ID, or `null`.
64
+
65
+ ## Selection results
66
+
67
+ `selectOption` returns one of three outcomes:
68
+
69
+ - **`resolved`** — a specific variant was matched. Contains `selectedVariant` with the full variant object and `selectedOptions` with the canonical option tuple from the variant.
70
+ - **`unresolved`** — the selection is valid, but no full variant is resolved locally because the selection is incomplete or the exact variant is absent from the bounded cache. If the buyer has not selected every option yet, ask for the remaining options. If the selection is complete but missing from the local cache, fetch or otherwise resolve the exact variant before treating it as complete.
71
+ - **`invalid`** — the option name or value does not exist on the product, or the combination does not exist per the encoded existence field. State is **not** updated. Contains a `reason` string explaining why the selection was rejected.
72
+
73
+ ## Register API
74
+
75
+ The `register` function binds product form identity and activation handlers. It covers product-relevant fields. It intentionally does not emit option control UI props like `checked`, `disabled`, or `aria-pressed`; derive those from `options`, `selectedVariant`, and caller-owned state.
76
+
77
+ ### `register("merchandiseId", opts)`
78
+
79
+ Returns `{ name: "merchandiseId", value: selectedVariantId }`. The `value` is an empty string when no variant is resolved.
80
+
81
+ ### `register("quantity", opts)`
82
+
83
+ Accepts `{ value: number }` for a controlled input or `{ defaultValue: number }` for an uncontrolled input. Returns the appropriate `{ name, value }` or `{ name, defaultValue }` props, with numeric values stringified for HTML form submission.
84
+
85
+ ### `register("optionValue", opts)`
86
+
87
+ Requires `{ optionName, value }`. Returns `{ name, value, onChange, onClick }` — form identity plus activation handlers. Derive caller-owned UI props from the matching option value state, such as `value.exists`, `value.available`, and `value.selected`.
88
+
89
+ ### `register("addToCart", opts)`
90
+
91
+ Returns `{ name: "add-to-cart", type: "submit" }` for the add-to-cart submit button.
92
+
93
+ ## Form submission
94
+
95
+ `handleFormSubmit(event)` delegates to the underlying `CartStore`'s form submission. The store does not own submission logic — it passes the `SubmitEvent` through to the cart layer, plus selected-product event detail when a variant is resolved. Cart errors from the submission are surfaced reactively via the `errors` state.
96
+
97
+ ## Route Placement
98
+
99
+ When creating a product detail page, use the app's existing route convention when present; otherwise create `/products/{handle}`. Keep variant selection in query params on that product route.
100
+
101
+ ## Framework References
102
+
103
+ Before building product UI, check whether this skill has a reference file for the app's framework in `references/`. If one exists, read it and use that framework binding or route pattern first.
104
+
105
+ If there is no matching reference, use `createProductFormStore` from `@shopify/hydrogen` directly, subscribe with the framework's reactivity primitive, and own hydration, URL sync, and `destroy()` yourself. Packaged bindings are thin wrappers over this same store — apply every rule and anti-pattern below; do not invent a different contract.
106
+
107
+ ## Hydration
108
+
109
+ When the product data changes (e.g. after a URL navigation triggers a data refetch), the store must be hydrated with the new product — not recreated. `hydrate(product, opts?)` replaces the product data, clears the decoded variant cache, and recomputes state. The new product's `selectedOrFirstAvailableVariant` takes priority; if absent, falls back to explicitly provided `opts.selectedOptions`, then to the selection that was active before hydration.
110
+
111
+ Provider bindings should hydrate automatically when the product's semantic identity changes (`product.id` + `selectedOrFirstAvailableVariant.id`). They should skip hydration on mount to avoid double-initialization, and skip hydration when the product identity is unchanged — preserving user selections through unrelated re-renders. Standalone `useProductForm(store)` users manage hydration themselves.
112
+
113
+ ## Reset
114
+
115
+ `reset()` restores the store to its initial state — the product and selected options it was created with. It clears the decoded variant cache and recomputes all derived state. Useful for "reset form" interactions.
116
+
117
+ ## Extracting selected options from the URL
118
+
119
+ `getSelectedProductOptions({ searchParams, allowedOptionNames })` extracts selected options from `URLSearchParams`. Each query parameter is treated as an option name/value pair (e.g. `?Color=Red&Size=M` produces `[{name:"Color",value:"Red"},{name:"Size",value:"M"}]`).
120
+
121
+ Pass `allowedOptionNames` to filter search params to only known product option names, avoiding unrelated query parameters. Passing an empty array filters out every option.
122
+
123
+ The `variant` param is reserved for Liquid-style numeric variant ids and is never treated as an option name. Server-side, `handleShopifyRoutes({ routeTemplates })` (see the local `hydrogen-request-handlers` skill) redirects `?variant=<id>` product URLs to their canonical option-params URL before the loader runs; when both `variant` and option params are present, the variant wins.
124
+
125
+ ## Building selection URLs
126
+
127
+ `buildProductSelectionSearchParams({ style?, selectedOptions, variant?, optionNames, base? })` builds the search params for a selection link. It always removes the reserved `variant` param and every param named in `optionNames`/`selectedOptions` from `base` before writing the new selection, preserving unrelated params (`?ref=campaign`). Compose it with the app's product pathname:
128
+
129
+ ```ts
130
+ const params = buildProductSelectionSearchParams({
131
+ selectedOptions: result.selectedOptions,
132
+ optionNames: product.options.map((option) => option.name),
133
+ base: new URLSearchParams(location.search),
134
+ });
135
+ const url = `/products/${handle}${params.size ? `?${params}` : ""}`;
136
+ ```
137
+
138
+ Pass `style: "variant"` with a resolved `variant` to emit a shareable `?variant=<numeric id>` link instead of option params. When no variant is resolved (partial selection), the variant style falls back to option params — a variant link is not constructible. Prefer option-params links for in-app navigation; `?variant=` links cost a server redirect on landing.
139
+
140
+ ---
141
+
142
+ ## Rules
143
+
144
+ - **Hide the entire variant picker when no option has more than one value** (`options.every((o) => o.values.length <= 1)`). A single-variant product has nothing to choose — rendering its one option/one value is noise. This is distinct from the value-hiding rule below: it hides the *whole picker* when there's nothing to choose, not individual values based on selection.
145
+
146
+ ### Existence and availability
147
+
148
+ - **Availability and existence are symmetric.** Selecting a later option can make earlier option values unavailable or non-existent. For example, if `Medium / Olive` is sold out, selecting `Color=Olive` should mark `Size=Medium` as unavailable even though `Medium / Green` is available.
149
+ - **ALWAYS disable and visually de-emphasize option values where `exists` is `false`.** These represent combinations that do not exist in the product's variant matrix. A non-existent combination cannot be selected — showing it as interactive is misleading.
150
+ - **NEVER disable option values where `exists` is `true` but `available` is `false`.** These are sold-out variants. The control must remain interactive so the buyer can see what the variant would be. Show a "Sold out" indicator instead, such as opacity plus line-through styling.
151
+ - **NEVER hide option values based on the current selection.** All values for an option must always be visible. Hiding values based on what's currently selected creates a confusing, collapsing UI that prevents buyers from exploring the full product matrix.
152
+
153
+ ### Selection and navigation
154
+
155
+ - **With provider bindings, put URL navigation in the provider `onSelect` callback.** Do not navigate inside each same-product option button. Let `register("optionValue", ...)` call `selectOption`; the provider receives the valid selection result and owns URL sync from there.
156
+ - **ALWAYS use URL-based variant selection in URL-routing apps.** When the buyer selects an option, navigate to the URL representing that selection — replacing the history entry and without resetting scroll position. The URL is the durable source of truth — the data loader reads the selection from the URL, queries the Storefront API, and the response hydrates the store when a reload or revalidation happens.
157
+ - **Same-product option values must degrade to GET links (no JS).** In URL-routing apps, render each same-product option value as a real link (the framework's link component or `<a href>`) whose `href` is the option URL for that value, built from `value.selectedOptions` — not a bare `<button onClick>`. The `href` is the no-JS path: with scripting off, activating it issues a GET to the option URL, the loader reads the params, queries the Storefront API, and the server renders the newly selected variant. Hydration enhances the same element — the registered `register("optionValue", ...)` handler calls `selectOption` and the provider's `onSelect` replaces the URL client-side (no full reload). Because the `href` and the client navigation resolve to the same option URL, behavior is identical with or without JS. On a hydrated click the element both runs the registered handler (which calls `selectOption`) and performs its own link navigation to that same URL, so keep the provider `onSelect` idempotent — the redundant navigation is a harmless no-op. An option control that is button-/`onClick`-only renders nothing a no-JS shopper can act on, stranding them on the default variant. Non-existent combinations (`exists: false`) have no valid option URL to degrade to; render those as a disabled `<button>`, not a link. (Cross-product values are already links; this brings same-product values to parity.)
158
+ - **ALWAYS use the `selectOption` return value for navigation, not a reactive effect on state.** `selectOption` returns the result synchronously. Use the returned `selectedOptions` to construct the next URL immediately. Reacting to derived state (e.g. `selectedOptions` from the store) instead introduces an unnecessary update cycle and risks stale values. Framework bindings may wrap this as an `onSelect` callback — the principle is the same.
159
+ - **Skip data refetch only when the resolved local state is sufficient.** When `selectOption` returns `resolved`, the selected variant is already in the local cache. It is safe to skip revalidation only if the route does not need fresh loader data for other UI. When a complete selection returns `unresolved`, fetch or otherwise resolve the exact variant before treating the selection as complete.
160
+ - **NEVER call `selectOption` for combined-listing cross-product values.** When `value.handle !== product.handle`, the value belongs to a different product. Render it as a navigation element (anchor or link component) that navigates to the other product's URL — not a button that calls `selectOption`. The store only knows about the current product's variant matrix.
161
+
162
+ ### Combined listings
163
+
164
+ - **ALWAYS check `value.handle` against the current `product.handle`.** If they differ, the option value points to a different product in a combined listing. The UI must navigate to that product (full page navigation or a link component), not call `selectOption`.
165
+ - **Use the framework's client-side link component for cross-product values when one exists.** Use the app's idiomatic navigation primitive. Use a raw `<a>` only when that is the app's established routing convention.
166
+ - **Preserve non-option query params on combined-listing links.** When constructing the URL for a combined-listing link, carry forward existing search params (e.g. `?ref=campaign`) and replace only the option params. Use `buildProductSelectionSearchParams` with the current product's option names as `optionNames` — it deletes all option params (and any stale `variant` param) first, then sets the new ones, preventing stale params when combined-listing products have different option names.
167
+ - **Ignore selected options that are not in the current product option matrix.** Divergent combined-listing child products can have different option names. A stale `Color=Black` param must not constrain a child product whose options are `Size` and `Mount`.
168
+
169
+ ### Price display
170
+
171
+ - **ALWAYS display server-provided prices.** Use `selectedVariant.price` when a variant is resolved. Fall back to `product.priceRange.minVariantPrice` when no variant is selected. Never compute prices client-side.
172
+ - **Format with Hydrogen money helpers**, not string concatenation. Use the local `hydrogen-money` skill for app wrappers around `formatMoney()`.
173
+
174
+ ### Add-to-cart
175
+
176
+ - **ALWAYS use `canAddToCart(product, options)` to determine if the add-to-cart button should be enabled.** This checks three conditions: a variant is selected, it is available for sale, and the product does not require a selling plan. Checking only `selectedVariant !== null` misses the selling-plan and availability constraints.
177
+ - **The add-to-cart form is separate from the variant selector.** Variant selection uses buttons and links — not form submissions. The add-to-cart form contains `merchandiseId` (the selected variant ID) and `quantity`. Do not put variant selection controls inside the cart form.
178
+ - **Use `register` to bind form fields.** `register("merchandiseId", {})` returns the hidden input props with the current variant ID. `register("quantity", { value: 1 })` returns the quantity input props. `register("addToCart", {})` returns stable add-to-cart submit button props. These stay synchronized with store state automatically.
179
+ - **Use the local `hydrogen-shop-pay` skill** when adding accelerated checkout near the add-to-cart form.
180
+ - **Show contextual CTA text.** When `canAddToCart` is `true`: "Add to cart". When no variant is selected (`selectedVariant === null`): "Select options" unless a navigation or submission is actually pending. When a variant is selected but unavailable: "Unavailable" or "Sold out".
181
+ - **Surface cart errors from `errors` state.** After form submission, user errors, warnings, and network errors relevant to the current product form are available on `state.errors`. Display these to the buyer.
182
+
183
+ ### Store lifecycle
184
+
185
+ - **Create the store once per component mount.** Do not recreate the store when the product prop changes — use `hydrate()` instead. Recreating the store discards the decoded variant cache and any user interaction state.
186
+ - **Always call `destroy()` on unmount.** This unsubscribes from the `CartStore` and releases internal caches (the decoded variant field cache). Provider bindings handle this automatically; standalone store users must do it themselves.
187
+ - **Do not read stale state after hydration.** Hydration triggers a synchronous state update. Any code that caches the previous state reference before hydration holds a stale reference.
188
+
189
+ ### Accessibility
190
+
191
+ - **Communicate selected state with the attribute that fits the element.** Same-product and cross-product option values render as links (per the GET-links and combined-listing rules), so mark the selected one with `aria-current` — `aria-pressed` is not a valid state on a link. Reserve `aria-pressed={value.selected}` for values rendered as a `<button>`, chiefly the disabled non-existent (`exists: false`) case. Derive either from the matching option value's `selected` state.
192
+ - **Use `aria-label` on visual-only controls** (e.g. color swatches without visible text).
193
+ - **Disabled controls (`exists: false`) must use the native `disabled` attribute** — not `aria-disabled` with prevented clicks. Non-existent combinations are truly non-interactive.
194
+
195
+ ---
196
+
197
+ ## User Acceptance Tests
198
+
199
+ ### Initial state
200
+
201
+ 1. **Pre-selected variant** — When the product has a `selectedOrFirstAvailableVariant`, the corresponding option values are marked as selected on first render. The variant's price and details are displayed. `selectedVariant` on the state is non-null.
202
+ 2. **No pre-selected variant** — When `selectedOrFirstAvailableVariant` is `null`, no option values are selected. `selectedVariant` is `null`. The add-to-cart button shows "Select options" (or equivalent) and is disabled.
203
+ 3. **URL-driven selection** — When the URL encodes a variant selection (e.g. via option params like `?Color=Blue&Size=Small`), the data loader resolves the selection and passes it to the Storefront API query. The returned `selectedOrFirstAvailableVariant` reflects the URL selection, and the store initializes with those options selected.
204
+
205
+ ### Option selection
206
+
207
+ 4. **Select a value** — Click an option value button. The value becomes selected (visually indicated). If the selection resolves to a variant, the price and variant details update immediately. `selectedVariant` updates on the state. In URL-routing apps, the URL updates to reflect the new selection without a scroll reset.
208
+ 5. **Multi-option selection** — On a product with Size and Color options, select Size=Large then Color=Blue. Both selections are reflected in the state. The resolved variant matches Large/Blue.
209
+ 6. **Symmetric availability** — On a product where Medium/Olive is unavailable but Medium/Green is available, selecting Color=Olive marks Size=Medium as `available: false`; selecting Color=Green marks Size=Medium as `available: true`.
210
+ 7. **Switch within an option** — With Color=Red selected, click Color=Blue. The selection switches; Red is deselected, Blue is selected. Only one value per option is selected at a time.
211
+ 8. **Invalid selection ignored** — Calling `selectOption` with an unknown option name or value returns `invalid` with a `reason` string and does not change state. No navigation occurs.
212
+ 9. **Non-existent combination** — An option value where `exists: false` is disabled. Clicking it does nothing.
213
+ 10. **Sold-out variant** — An option value where `exists: true` and `available: false` is interactive but shows a "Sold out" indicator. Selecting it updates the state and shows the variant as unavailable.
214
+
215
+ ### Combined listings
216
+
217
+ 11. **Cross-product value** — An option value where `value.handle !== product.handle` renders as a navigation element (anchor or link component), not a button. Clicking it navigates to the other product's page with the appropriate option params.
218
+ 12. **Same-product value** — An option value where `value.handle === product.handle` renders as a GET link to its option URL and spreads `register("optionValue", ...)`. With JavaScript disabled the link navigates and the server resolves the variant; hydrated, the registered handler calls `selectOption` and the provider syncs the URL client-side.
219
+ 13. **Preserved params** — When navigating via a combined-listing link, non-option query params from the current URL are preserved in the destination URL.
220
+
221
+ ### Hydration
222
+
223
+ 14. **Product navigation** — Navigating from Product A to Product B (different `product.id`) hydrates the store with Product B's data. The selection reflects Product B's `selectedOrFirstAvailableVariant`.
224
+ 15. **Same product, different variant** — Navigating to the same product with a different URL-encoded selection (e.g. `?Color=Blue` instead of `?Color=Red`) hydrates with the new pre-selected variant without recreating the store.
225
+ 16. **Unrelated re-render** — A re-render that passes the same product identity does not hydrate. User selections made since the last hydration are preserved.
226
+ 17. **No double-init on mount** — On initial mount, the store initializes from the constructor — hydration does not fire. A user selection made immediately after mount survives a subsequent re-render with the same product.
227
+
228
+ ### Cart integration
229
+
230
+ 18. **Matched line item** — When the selected variant's ID matches a cart line's `merchandise.id`, `matchedLineItem` is non-null and contains the cart line data.
231
+ 19. **Cart error surfacing** — After a failed add-to-cart submission, `errors.userErrors` contains the relevant user errors, `errors.warnings` contains warnings, and `errors.networkErrors` contains any network failures.
232
+ 20. **Reactive cart sync** — When the cart updates externally (e.g. quantity change from a cart drawer), the `matchedLineItem` and `errors` update without any manual intervention.
233
+
234
+ ### Add-to-cart
235
+
236
+ 21. **Enabled state** — When `canAddToCart` returns `true` (variant selected, available, no selling plan required), the add-to-cart button is enabled and shows "Add to cart".
237
+ 22. **Disabled — no variant** — When no variant is selected, the button is disabled with "Select options" text unless navigation or submission is actually pending.
238
+ 23. **Disabled — sold out** — When the selected variant is not available for sale, the button is disabled with "Unavailable" or "Sold out" text.
239
+ 24. **Disabled — selling plan required** — When `product.requiresSellingPlan` is `true`, the button is disabled regardless of variant selection.
240
+ 25. **Variant ID in form** — `register("merchandiseId", {})` returns `{ name: "merchandiseId", value: selectedVariantId }`. When no variant is selected, `value` is an empty string.
241
+ 26. **Form submission** — `handleFormSubmit(event)` delegates to the cart store with the submit event and selected-product detail when a variant is resolved. Cart errors surface reactively via the `errors` state.
242
+
243
+ ### Register API
244
+
245
+ 27. **Option value registration** — `register("optionValue", { optionName: "Color", value: "Red" })` returns `{ name, value, onChange, onClick }`. Calling `onChange` or `onClick` triggers `selectOption`.
246
+ 28. **Caller-owned option attributes** — `register("optionValue", { optionName: "Color", value: "Red" })` returns only `{ name, value, onChange, onClick }`. Derive `disabled`, `aria-pressed`, and visual state from the matching `options` value.
247
+ 29. **Quantity registration** — `register("quantity", { value: 1 })` returns `{ name: "quantity", value: "1" }`. `register("quantity", { defaultValue: 1 })` returns `{ name: "quantity", defaultValue: "1" }`.
248
+ 30. **Add-to-cart registration** — `register("addToCart", {})` returns `{ name: "add-to-cart", type: "submit" }`.
249
+
250
+ ### Unresolved selection
251
+
252
+ 31. **Transient unresolved** — In URL-routing apps, when a complete selection returns `unresolved` because the exact variant is absent from the local cache, the app navigates to the new URL and re-fetches product data. The subsequent hydration resolves the variant. Incomplete selections should remain in selection UI until the buyer chooses the remaining options.
253
+
254
+ ### Reset
255
+
256
+ 32. **Reset to initial state** — Calling `reset()` restores the store to the product and selected options it was created with. All user selections are discarded.
257
+
258
+ ---
259
+
260
+ ## Anti-patterns
261
+
262
+ - **Using reactive effects on state to sync selection to URL.** The `selectOption` return value provides the selection result synchronously. Reacting to `state.selectedOptions` instead introduces an extra update cycle and can fire with stale values.
263
+ - **Navigating inside same-product option buttons.** Do not destructure `onClick` / `onChange` from `register("optionValue", ...)`, call `navigate()`, and then call the registered handler manually. This bypasses the provider `onSelect` contract and can navigate from stale or invalid data.
264
+ - **Button-/onClick-only same-product option values.** A same-product option value rendered as `<button onClick={selectOption}>` with no `href` is dead without JavaScript — a no-JS shopper cannot switch variants and is stuck on the default. Render it as a GET link to the option URL and let the registered handler enhance it; the `href` is the progressive-enhancement fallback.
265
+ - **Using raw anchors when the framework has a client-side link component.** Raw `<a>` tags lose client-router behavior such as scroll preservation, pending navigation state, prefetching, and route transitions. Use the app's established link component unless raw anchors are the framework convention.
266
+ - **Putting variant selection inside the add-to-cart form.** Variant selection is button/link interactions that update store state. The add-to-cart form submits `merchandiseId` and `quantity` to the cart. Mixing them creates ambiguous form semantics and breaks progressive enhancement.
267
+ - **Ignoring `errors` state after form submission.** Cart user errors, warnings, and network errors are surfaced reactively on the store state. Failing to display these leaves the buyer with no feedback when something goes wrong.
@@ -0,0 +1,207 @@
1
+ # Next.js App Router
2
+
3
+ ## Contents
4
+
5
+ - Server Page
6
+ - Client Details Component
7
+ - Same-Product And Cross-Product Values
8
+ - Add To Cart
9
+
10
+ Product data is fetched in the server page. Variant selection and add-to-cart live in a `"use client"` component because they use `ProductProvider`, browser routing, and cart forms.
11
+
12
+ ## Server Page
13
+
14
+ In `app/products/[handle]/page.tsx`, read selected options from URL search params and query Storefront API with Hydrogen's product fields:
15
+
16
+ ```tsx
17
+ import { getSelectedProductOptions, gql } from "@shopify/hydrogen";
18
+
19
+ export const PRODUCT_QUERY = gql(`
20
+ query Product($handle: String!, $selectedOptions: [SelectedOptionInput!]!) {
21
+ product(handle: $handle) {
22
+ id
23
+ handle
24
+ title
25
+ vendor
26
+ requiresSellingPlan
27
+ encodedVariantExistence
28
+ encodedVariantAvailability
29
+ options {
30
+ name
31
+ optionValues {
32
+ name
33
+ firstSelectableVariant {
34
+ id
35
+ title
36
+ availableForSale
37
+ selectedOptions { name value }
38
+ price { amount currencyCode }
39
+ compareAtPrice { amount currencyCode }
40
+ product { handle title }
41
+ sku
42
+ }
43
+ swatch { color image { previewImage { url } } }
44
+ }
45
+ }
46
+ selectedOrFirstAvailableVariant(
47
+ selectedOptions: $selectedOptions
48
+ ignoreUnknownOptions: true
49
+ caseInsensitiveMatch: true
50
+ ) {
51
+ id
52
+ title
53
+ availableForSale
54
+ selectedOptions { name value }
55
+ price { amount currencyCode }
56
+ compareAtPrice { amount currencyCode }
57
+ product { handle title }
58
+ sku
59
+ }
60
+ adjacentVariants(
61
+ selectedOptions: $selectedOptions
62
+ ignoreUnknownOptions: true
63
+ caseInsensitiveMatch: true
64
+ ) {
65
+ id
66
+ title
67
+ availableForSale
68
+ selectedOptions { name value }
69
+ price { amount currencyCode }
70
+ compareAtPrice { amount currencyCode }
71
+ product { handle title }
72
+ sku
73
+ }
74
+ priceRange {
75
+ minVariantPrice { amount currencyCode }
76
+ }
77
+ }
78
+ }
79
+ `);
80
+
81
+ export default async function ProductPage({ params, searchParams }: Props) {
82
+ const { handle } = await params;
83
+ const selectedOptions = getSelectedProductOptions({
84
+ searchParams: toURLSearchParams(await searchParams),
85
+ });
86
+ const storefront = await getStorefrontClient();
87
+ const { data } = await storefront.graphql(PRODUCT_QUERY, {
88
+ variables: { handle, selectedOptions },
89
+ });
90
+ if (!data?.product) notFound();
91
+ return <ProductDetails product={data.product} />;
92
+ }
93
+
94
+ function toURLSearchParams(input: Record<string, string | string[] | undefined>) {
95
+ const params = new URLSearchParams();
96
+ for (const [key, value] of Object.entries(input)) {
97
+ if (Array.isArray(value)) {
98
+ for (const item of value) params.append(key, item);
99
+ } else if (value != null) {
100
+ params.set(key, value);
101
+ }
102
+ }
103
+ return params;
104
+ }
105
+ ```
106
+
107
+ Use the route's existing search-param normalization helper if present.
108
+
109
+ ## Client Details Component
110
+
111
+ ```tsx
112
+ "use client";
113
+
114
+ import { canAddToCart, type SelectedOption, type StorefrontApi } from "@shopify/hydrogen";
115
+ import { createProductComponents } from "@shopify/hydrogen/react";
116
+ import { useRouter, useSearchParams } from "next/navigation";
117
+ import type { PRODUCT_QUERY } from "../products/[handle]/page";
118
+
119
+ type ProductQuery = StorefrontApi.ResultOf<typeof PRODUCT_QUERY>;
120
+ type ProductData = NonNullable<ProductQuery["product"]>;
121
+
122
+ const { ProductProvider, useProductForm } = createProductComponents<ProductData>();
123
+
124
+ export function ProductDetails({ product }: { product: ProductData }) {
125
+ const router = useRouter();
126
+ const searchParams = useSearchParams();
127
+
128
+ return (
129
+ <ProductProvider
130
+ product={product}
131
+ onSelect={(result) => {
132
+ router.replace(
133
+ variantUrl(product, result.selectedOptions, result.selectedVariant?.product?.handle, searchParams),
134
+ { scroll: false },
135
+ );
136
+ }}
137
+ >
138
+ <VariantSelector product={product} />
139
+ <AddToCart product={product} />
140
+ </ProductProvider>
141
+ );
142
+ }
143
+ ```
144
+
145
+ Wrap this tree in the app's `CartProvider` from `hydrogen-cart-ui`; `ProductProvider` reads the cart store for add-to-cart submission and product-scoped cart errors.
146
+
147
+ ## Same-Product And Cross-Product Values
148
+
149
+ Render same-product option values as GET links (`next/link`) so variant selection degrades without JavaScript (the skill's GET-links rule and accessibility guidance cover the `aria-current`, idempotent-`onSelect`, and no-JS rationale). The `href` is the option URL built from `value.selectedOptions`; spread `register("optionValue", ...)` to enhance the link so a hydrated click selects client-side via the provider's `onSelect`. Keep sold-out-but-existing values interactive and derive their visual treatment from `value.available`. Render non-existent combinations (`exists: false`) as a disabled `<button>` instead of a link.
150
+
151
+ ```tsx
152
+ <Link
153
+ href={variantUrl(product, value.selectedOptions, value.handle, searchParams)}
154
+ replace
155
+ scroll={false}
156
+ aria-current={value.selected ? "true" : undefined}
157
+ data-available={value.available ? "true" : "false"}
158
+ {...register("optionValue", { optionName: option.name, value: value.name })}
159
+ >
160
+ {value.name}
161
+ {!value.available ? <span className="sr-only"> (Sold out)</span> : null}
162
+ </Link>
163
+ ```
164
+
165
+ Cross-product combined-listing values point at a different `value.handle` and navigate to that product. Prefer `next/link`; if using a button for cross-product navigation, keep it clearly outside the add-to-cart form and call `router.replace(...)`. Both use the same URL helper:
166
+
167
+ ```tsx
168
+ import { buildProductSelectionSearchParams, type SelectedOption } from "@shopify/hydrogen";
169
+
170
+ function variantUrl(
171
+ product: { handle: string; options: Array<{ name: string }> },
172
+ selectedOptions: SelectedOption[],
173
+ handle = product.handle,
174
+ base: URLSearchParams | ReturnType<typeof useSearchParams> = new URLSearchParams(),
175
+ ) {
176
+ const params = buildProductSelectionSearchParams({
177
+ selectedOptions,
178
+ optionNames: product.options.map((option) => option.name),
179
+ base: new URLSearchParams(base),
180
+ });
181
+ const query = params.toString();
182
+ return `/products/${handle}${query ? `?${query}` : ""}`;
183
+ }
184
+ ```
185
+
186
+ ## Add To Cart
187
+
188
+ Use the local `hydrogen-shop-pay` skill when adding Shop Pay. Use the local `hydrogen-money` skill for prices.
189
+
190
+ Do not put option controls inside the add-to-cart form. The form submits `merchandiseId` and `quantity`; option controls are buttons/links outside it. Register the submit button with `addToCart`.
191
+
192
+ ```tsx
193
+ function AddToCart({ product }: { product: ProductData }) {
194
+ const { options, register, formProps, pending } = useProductForm();
195
+ const addable = canAddToCart(product, options);
196
+
197
+ return (
198
+ <form {...formProps({ beforeSubmit: openCartDrawer })}>
199
+ <input type="hidden" {...register("merchandiseId", {})} />
200
+ <input {...register("quantity", { value: 1 })} />
201
+ <button {...register("addToCart", {})} disabled={!addable || pending}>
202
+ Add to cart
203
+ </button>
204
+ </form>
205
+ );
206
+ }
207
+ ```
@@ -0,0 +1,95 @@
1
+ # Nuxt
2
+
3
+ Use `@shopify/hydrogen/vue` product bindings. Server page data resolves the selected variant from URL query params; client components own option interaction and add-to-cart.
4
+
5
+ ## Storefront Module
6
+
7
+ ```ts
8
+ // storefront/product.ts
9
+ import { createProductComponents } from "@shopify/hydrogen/vue";
10
+ import type { ProductData } from "./product-types";
11
+
12
+ export const { ProductProvider, useProductForm } = createProductComponents<ProductData>();
13
+ ```
14
+
15
+ `ProductData` is app-owned. It must include Hydrogen's product form fields: `id`, `handle`, `title`, `options`, `selectedOrFirstAvailableVariant`, `adjacentVariants`, `encodedVariantExistence`, `encodedVariantAvailability`, `requiresSellingPlan`, and variant `price`/`availableForSale` fields used by the UI.
16
+
17
+ Wrap this tree in the app's `CartProvider` from `hydrogen-cart-ui`; `ProductProvider` reads the cart store for add-to-cart submission and product-scoped cart errors.
18
+
19
+ ## Page
20
+
21
+ In `pages/products/[handle].vue`, pass selected options from `getSelectedProductOptions({searchParams})` into the Storefront API query. Use one reusable variant fragment for `firstSelectableVariant`, `selectedOrFirstAvailableVariant`, and `adjacentVariants`; it must include variant `price`, `availableForSale`, `selectedOptions`, and `product { handle title }` so price display and combined-listing navigation work. Use the injected server/client Storefront client from the Nuxt storefront-client recipe.
22
+
23
+ Wrap the UI:
24
+
25
+ ```vue
26
+ <template>
27
+ <ProductProvider
28
+ :product="product"
29
+ :on-select="
30
+ (result) =>
31
+ navigateTo(variantRoute(result.selectedOptions, result.selectedVariant?.product?.handle), {
32
+ replace: true,
33
+ })
34
+ "
35
+ >
36
+ <ProductVariantSelector :product="product" />
37
+ <ProductAddToCart :product="product" />
38
+ </ProductProvider>
39
+ </template>
40
+ ```
41
+
42
+ ## Variant Selector
43
+
44
+ Same-product option values are GET links (`NuxtLink`) so selection degrades without JavaScript; non-existent combinations render as a disabled `<button>`, and cross-product combined-listing values are also links. The skill's GET-links rule and accessibility guidance cover the `aria-current`, idempotent-`onSelect`, and no-JS rationale:
45
+
46
+ ```vue
47
+ <!-- cross-product: navigate to a different product -->
48
+ <NuxtLink
49
+ v-if="value.handle !== product.handle"
50
+ :to="variantRoute(value.selectedOptions, value.handle)"
51
+ replace
52
+ >
53
+ {{ value.name }}
54
+ </NuxtLink>
55
+
56
+ <!-- same-product, non-existent combination: no option URL to degrade to -->
57
+ <button
58
+ v-else-if="!value.exists"
59
+ type="button"
60
+ disabled
61
+ :aria-pressed="value.selected"
62
+ >
63
+ {{ value.name }}
64
+ </button>
65
+
66
+ <!-- same-product, existing value: GET link enhanced by the registered handler -->
67
+ <NuxtLink
68
+ v-else
69
+ :to="variantRoute(value.selectedOptions, value.handle)"
70
+ replace
71
+ :aria-current="value.selected ? 'true' : undefined"
72
+ v-bind="form.register('optionValue', { optionName: option.name, value: value.name })"
73
+ >
74
+ {{ value.name }}
75
+ <template v-if="!value.available"> - Sold out</template>
76
+ </NuxtLink>
77
+ ```
78
+
79
+ Build route query objects with `buildProductSelectionSearchParams`, passing the product option names as `optionNames`. Convert Nuxt's current route query to `URLSearchParams` for `base` by appending every scalar or array value, then convert the returned params back to a Nuxt query object while preserving repeated keys. This preserves non-option params while removing stale option params and the reserved `variant` param.
80
+
81
+ ## Add To Cart
82
+
83
+ ```vue
84
+ <form v-bind="form.formProps({ beforeSubmit: openCartDrawer })">
85
+ <input type="hidden" v-bind="form.register('merchandiseId', {})" />
86
+ <input v-bind="form.register('quantity', { value: quantity })" />
87
+ <button v-bind="form.register('addToCart', {})" :disabled="!addable || form.pending.value">
88
+ Add to cart
89
+ </button>
90
+ </form>
91
+ ```
92
+
93
+ `addable` must use `canAddToCart(product, form.options)`.
94
+
95
+ Use the local `hydrogen-shop-pay` skill for Shop Pay and `hydrogen-money` for price formatting.
@@ -0,0 +1,71 @@
1
+ # React Product Bindings
2
+
3
+ Create typed React product bindings once from `@shopify/hydrogen/react`, usually in a shared product module:
4
+
5
+ ```ts
6
+ import { createProductComponents } from "@shopify/hydrogen/react";
7
+ import type { ProductData } from "./types";
8
+
9
+ export const { ProductProvider, useProductForm } =
10
+ createProductComponents<ProductData>();
11
+ ```
12
+
13
+ Use the provider's `onSelect` callback for same-product URL sync:
14
+
15
+ ```tsx
16
+ <ProductProvider
17
+ product={product}
18
+ onSelect={(result) => {
19
+ void navigate(
20
+ toRouterLocation(
21
+ variantUrl(product, result.selectedOptions, result.selectedVariant?.product?.handle),
22
+ ),
23
+ {
24
+ replace: true,
25
+ preventScrollReset: true,
26
+ },
27
+ );
28
+ }}
29
+ >
30
+ <ProductPurchasePanel product={product} />
31
+ </ProductProvider>
32
+ ```
33
+
34
+ If a route should skip loader revalidation for locally resolved selections, use the framework's supported route-level revalidation API. Do not pass unsupported revalidation flags to `navigate()`.
35
+
36
+ Same-product option values are GET links so selection works without JavaScript (the skill's GET-links rule and accessibility guidance cover the `aria-current`, idempotent-`onSelect`, and no-JS rationale). The `to` is the option URL built from `value.selectedOptions`; spreading the registered handlers enhances the link so a hydrated click selects client-side through the provider's `onSelect`. Keep sold-out-but-existing values interactive and derive their visual treatment from `value.available`:
37
+
38
+ ```tsx
39
+ <Link
40
+ to={toRouterLocation(variantUrl(product, value.selectedOptions, value.handle))}
41
+ replace
42
+ preventScrollReset
43
+ aria-current={value.selected ? "true" : undefined}
44
+ data-available={value.available ? "true" : "false"}
45
+ {...register("optionValue", { optionName: option.name, value: value.name })}
46
+ >
47
+ {value.name}
48
+ {!value.available ? <span className="sr-only"> (Sold out)</span> : null}
49
+ </Link>
50
+ ```
51
+
52
+ Non-existent combinations (`exists: false`) render as a disabled `<button>` instead of a `<Link>`:
53
+
54
+ ```tsx
55
+ <button type="button" disabled aria-pressed={value.selected}>
56
+ {value.name}
57
+ </button>
58
+ ```
59
+
60
+ Cross-product option values are framework links that reuse the same URL helper:
61
+
62
+ ```tsx
63
+ <Link
64
+ to={toRouterLocation(variantUrl(product, value.selectedOptions, value.handle))}
65
+ preventScrollReset
66
+ data-available={value.available ? "true" : "false"}
67
+ >
68
+ {value.name}
69
+ {!value.available ? <span className="sr-only"> (Sold out)</span> : null}
70
+ </Link>
71
+ ```