@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,58 @@
1
+ //#region src/core/standard-routes/path.ts
2
+ function prependPathPrefix(pathname, pathPrefix) {
3
+ const normalizedPathPrefix = normalizePathPrefix(pathPrefix);
4
+ if (!normalizedPathPrefix) return pathname;
5
+ return `${normalizedPathPrefix}/${pathname.replace(/^\/+/, "")}`;
6
+ }
7
+ function normalizePathPrefix(pathPrefix) {
8
+ if (!pathPrefix) return "";
9
+ const trimmedPathPrefix = pathPrefix.trim().replace(/^\/+|\/+$/g, "");
10
+ return trimmedPathPrefix ? `/${trimmedPathPrefix}` : "";
11
+ }
12
+ function parseUrl(url, baseUrl) {
13
+ try {
14
+ return new URL(url, baseUrl ?? "https://shopify.local");
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+ /**
20
+ * Parses a URL and rejects absolute URLs that do not share `baseUrl`'s origin.
21
+ *
22
+ * Relative URLs are resolved against `baseUrl` when provided, or against a throwaway local origin
23
+ * when no base is available.
24
+ */
25
+ function parseSameOriginUrl(url, baseUrl) {
26
+ const parsedUrl = parseUrl(url, baseUrl);
27
+ if (!parsedUrl) return null;
28
+ const parsedBaseUrl = baseUrl ? parseUrl(baseUrl, void 0) : null;
29
+ if (parsedBaseUrl && parsedUrl.origin !== parsedBaseUrl.origin) return null;
30
+ return parsedUrl;
31
+ }
32
+ function isAbsoluteUrl(url) {
33
+ return /^[A-Za-z][A-Za-z0-9+.-]*:/.test(url);
34
+ }
35
+ function stripTrailingSlash(pathname) {
36
+ if (pathname === "/") return pathname;
37
+ return pathname.replace(/\/+$/, "");
38
+ }
39
+ /**
40
+ * Removes the locale prefix before matching route templates.
41
+ *
42
+ * `ShopifyScripts` stores `routes.root` with a trailing slash, while server i18n uses
43
+ * `pathPrefix`; both are normalized here so `/fr-ca/products/snowboard` can match the same
44
+ * template as `/products/snowboard`.
45
+ */
46
+ function stripI18nPathPrefix(pathname, pathPrefix) {
47
+ const normalizedPathPrefix = normalizePathPrefix(pathPrefix);
48
+ if (!normalizedPathPrefix) return pathname;
49
+ const lowerPathname = pathname.toLowerCase();
50
+ const lowerPathPrefix = normalizedPathPrefix.toLowerCase();
51
+ if (lowerPathname === lowerPathPrefix) return "/";
52
+ if (!lowerPathname.startsWith(`${lowerPathPrefix}/`)) return pathname;
53
+ return pathname.slice(normalizedPathPrefix.length) || "/";
54
+ }
55
+
56
+ //#endregion
57
+ export { isAbsoluteUrl, normalizePathPrefix, parseSameOriginUrl, prependPathPrefix, stripI18nPathPrefix, stripTrailingSlash };
58
+ //# sourceMappingURL=path.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.mjs","names":[],"sources":["../../../../src/core/standard-routes/path.ts"],"sourcesContent":["export function prependPathPrefix(pathname: string, pathPrefix: string | undefined): string {\n const normalizedPathPrefix = normalizePathPrefix(pathPrefix);\n if (!normalizedPathPrefix) return pathname;\n\n return `${normalizedPathPrefix}/${pathname.replace(/^\\/+/, \"\")}`;\n}\n\nexport function normalizePathPrefix(pathPrefix: string | undefined): string {\n if (!pathPrefix) return \"\";\n\n const trimmedPathPrefix = pathPrefix.trim().replace(/^\\/+|\\/+$/g, \"\");\n return trimmedPathPrefix ? `/${trimmedPathPrefix}` : \"\";\n}\n\nfunction parseUrl(url: string, baseUrl: string | undefined): URL | null {\n try {\n return new URL(url, baseUrl ?? \"https://shopify.local\");\n } catch {\n return null;\n }\n}\n\n/**\n * Parses a URL and rejects absolute URLs that do not share `baseUrl`'s origin.\n *\n * Relative URLs are resolved against `baseUrl` when provided, or against a throwaway local origin\n * when no base is available.\n */\nexport function parseSameOriginUrl(url: string, baseUrl: string | undefined): URL | null {\n const parsedUrl = parseUrl(url, baseUrl);\n if (!parsedUrl) return null;\n\n const parsedBaseUrl = baseUrl ? parseUrl(baseUrl, undefined) : null;\n if (parsedBaseUrl && parsedUrl.origin !== parsedBaseUrl.origin) return null;\n\n return parsedUrl;\n}\n\nexport function isAbsoluteUrl(url: string): boolean {\n return /^[A-Za-z][A-Za-z0-9+.-]*:/.test(url);\n}\n\nexport function stripTrailingSlash(pathname: string): string {\n if (pathname === \"/\") return pathname;\n return pathname.replace(/\\/+$/, \"\");\n}\n\n/**\n * Removes the locale prefix before matching route templates.\n *\n * `ShopifyScripts` stores `routes.root` with a trailing slash, while server i18n uses\n * `pathPrefix`; both are normalized here so `/fr-ca/products/snowboard` can match the same\n * template as `/products/snowboard`.\n */\nexport function stripI18nPathPrefix(pathname: string, pathPrefix: string | undefined): string {\n const normalizedPathPrefix = normalizePathPrefix(pathPrefix);\n if (!normalizedPathPrefix) return pathname;\n\n const lowerPathname = pathname.toLowerCase();\n const lowerPathPrefix = normalizedPathPrefix.toLowerCase();\n\n if (lowerPathname === lowerPathPrefix) return \"/\";\n if (!lowerPathname.startsWith(`${lowerPathPrefix}/`)) return pathname;\n\n return pathname.slice(normalizedPathPrefix.length) || \"/\";\n}\n"],"mappings":";AAAA,SAAgB,kBAAkB,UAAkB,YAAwC;CAC1F,MAAM,uBAAuB,oBAAoB,WAAW;AAC5D,KAAI,CAAC,qBAAsB,QAAO;AAElC,QAAO,GAAG,qBAAqB,GAAG,SAAS,QAAQ,QAAQ,GAAG;;AAGhE,SAAgB,oBAAoB,YAAwC;AAC1E,KAAI,CAAC,WAAY,QAAO;CAExB,MAAM,oBAAoB,WAAW,MAAM,CAAC,QAAQ,cAAc,GAAG;AACrE,QAAO,oBAAoB,IAAI,sBAAsB;;AAGvD,SAAS,SAAS,KAAa,SAAyC;AACtE,KAAI;AACF,SAAO,IAAI,IAAI,KAAK,WAAW,wBAAwB;SACjD;AACN,SAAO;;;;;;;;;AAUX,SAAgB,mBAAmB,KAAa,SAAyC;CACvF,MAAM,YAAY,SAAS,KAAK,QAAQ;AACxC,KAAI,CAAC,UAAW,QAAO;CAEvB,MAAM,gBAAgB,UAAU,SAAS,SAAS,OAAU,GAAG;AAC/D,KAAI,iBAAiB,UAAU,WAAW,cAAc,OAAQ,QAAO;AAEvE,QAAO;;AAGT,SAAgB,cAAc,KAAsB;AAClD,QAAO,4BAA4B,KAAK,IAAI;;AAG9C,SAAgB,mBAAmB,UAA0B;AAC3D,KAAI,aAAa,IAAK,QAAO;AAC7B,QAAO,SAAS,QAAQ,QAAQ,GAAG;;;;;;;;;AAUrC,SAAgB,oBAAoB,UAAkB,YAAwC;CAC5F,MAAM,uBAAuB,oBAAoB,WAAW;AAC5D,KAAI,CAAC,qBAAsB,QAAO;CAElC,MAAM,gBAAgB,SAAS,aAAa;CAC5C,MAAM,kBAAkB,qBAAqB,aAAa;AAE1D,KAAI,kBAAkB,gBAAiB,QAAO;AAC9C,KAAI,CAAC,cAAc,WAAW,GAAG,gBAAgB,GAAG,CAAE,QAAO;AAE7D,QAAO,SAAS,MAAM,qBAAqB,OAAO,IAAI"}
@@ -0,0 +1,51 @@
1
+ import { DEFAULT_STANDARD_ROUTES } from "./defaults.mjs";
2
+ import { isAbsoluteUrl, parseSameOriginUrl, stripTrailingSlash } from "./path.mjs";
3
+ import { buildStandardRouteTarget } from "./build.mjs";
4
+ import { matchStandardRouteTemplates } from "./match.mjs";
5
+
6
+ //#region src/core/standard-routes/redirects.ts
7
+ /**
8
+ * Returns the custom route target for a Shopify standard route pathname.
9
+ *
10
+ * This is used by server redirects: it only matches Shopify's default route shape when the app
11
+ * configured a custom template for that same resource, so standard `/products/...` requests do not
12
+ * redirect unless the app opted into a non-standard product route.
13
+ */
14
+ function getStandardRouteTarget({ pathname, pathPrefix, routeTemplates }) {
15
+ const match = matchStandardRouteTemplates(pathname, pathPrefix, (route) => routeTemplates[route] ? DEFAULT_STANDARD_ROUTES[route] : []);
16
+ if (!match) return null;
17
+ const target = routeTemplates[match.route];
18
+ if (!target) return null;
19
+ return buildStandardRouteTarget(target, match.params, pathPrefix);
20
+ }
21
+ /**
22
+ * Resolves a Shopify standard resource URL to the app's configured route template.
23
+ *
24
+ * Query strings and hashes are preserved. Absolute URLs are only resolved when they share the same
25
+ * origin as `baseUrl`; external URLs are returned unchanged.
26
+ */
27
+ function resolveStandardRouteUrl({ baseUrl, pathPrefix, routeTemplates, url }) {
28
+ const parsedUrl = parseSameOriginUrl(url, baseUrl);
29
+ if (!parsedUrl) return url;
30
+ const target = getStandardRouteTarget({
31
+ pathname: parsedUrl.pathname,
32
+ pathPrefix,
33
+ routeTemplates
34
+ });
35
+ if (!target) return url;
36
+ const resolvedUrl = `${target}${parsedUrl.search}${parsedUrl.hash}`;
37
+ return isAbsoluteUrl(url) ? `${parsedUrl.origin}${resolvedUrl}` : resolvedUrl;
38
+ }
39
+ function isStandardRouteSelfRedirect(requestUrl, location) {
40
+ try {
41
+ const sourceUrl = new URL(requestUrl);
42
+ const targetUrl = new URL(location, sourceUrl.origin);
43
+ return sourceUrl.origin === targetUrl.origin && stripTrailingSlash(sourceUrl.pathname) === stripTrailingSlash(targetUrl.pathname) && sourceUrl.search === targetUrl.search;
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+
49
+ //#endregion
50
+ export { getStandardRouteTarget, isStandardRouteSelfRedirect, resolveStandardRouteUrl };
51
+ //# sourceMappingURL=redirects.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redirects.mjs","names":[],"sources":["../../../../src/core/standard-routes/redirects.ts"],"sourcesContent":["import { buildStandardRouteTarget } from \"./build\";\nimport { DEFAULT_STANDARD_ROUTES } from \"./defaults\";\nimport { matchStandardRouteTemplates } from \"./match\";\nimport { isAbsoluteUrl, parseSameOriginUrl, stripTrailingSlash } from \"./path\";\nimport type { ShopifyRouteTemplates } from \"./types\";\n\n/**\n * Returns the custom route target for a Shopify standard route pathname.\n *\n * This is used by server redirects: it only matches Shopify's default route shape when the app\n * configured a custom template for that same resource, so standard `/products/...` requests do not\n * redirect unless the app opted into a non-standard product route.\n */\nexport function getStandardRouteTarget({\n pathname,\n pathPrefix,\n routeTemplates,\n}: {\n pathname: string;\n pathPrefix?: string;\n routeTemplates: ShopifyRouteTemplates;\n}): string | null {\n const match = matchStandardRouteTemplates(pathname, pathPrefix, (route) =>\n routeTemplates[route] ? DEFAULT_STANDARD_ROUTES[route] : [],\n );\n\n if (!match) return null;\n\n const target = routeTemplates[match.route];\n if (!target) return null;\n\n return buildStandardRouteTarget(target, match.params, pathPrefix);\n}\n\n/**\n * Resolves a Shopify standard resource URL to the app's configured route template.\n *\n * Query strings and hashes are preserved. Absolute URLs are only resolved when they share the same\n * origin as `baseUrl`; external URLs are returned unchanged.\n */\nexport function resolveStandardRouteUrl({\n baseUrl,\n pathPrefix,\n routeTemplates,\n url,\n}: {\n baseUrl?: string;\n pathPrefix?: string;\n routeTemplates: ShopifyRouteTemplates;\n url: string;\n}): string {\n const parsedUrl = parseSameOriginUrl(url, baseUrl);\n if (!parsedUrl) return url;\n\n const target = getStandardRouteTarget({\n pathname: parsedUrl.pathname,\n pathPrefix,\n routeTemplates,\n });\n\n if (!target) return url;\n\n const resolvedUrl = `${target}${parsedUrl.search}${parsedUrl.hash}`;\n\n return isAbsoluteUrl(url) ? `${parsedUrl.origin}${resolvedUrl}` : resolvedUrl;\n}\n\nexport function isStandardRouteSelfRedirect(requestUrl: string, location: string): boolean {\n try {\n const sourceUrl = new URL(requestUrl);\n const targetUrl = new URL(location, sourceUrl.origin);\n\n return (\n sourceUrl.origin === targetUrl.origin &&\n stripTrailingSlash(sourceUrl.pathname) === stripTrailingSlash(targetUrl.pathname) &&\n sourceUrl.search === targetUrl.search\n );\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,uBAAuB,EACrC,UACA,YACA,kBAKgB;CAChB,MAAM,QAAQ,4BAA4B,UAAU,aAAa,UAC/D,eAAe,SAAS,wBAAwB,SAAS,EAAE,CAC5D;AAED,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,SAAS,eAAe,MAAM;AACpC,KAAI,CAAC,OAAQ,QAAO;AAEpB,QAAO,yBAAyB,QAAQ,MAAM,QAAQ,WAAW;;;;;;;;AASnE,SAAgB,wBAAwB,EACtC,SACA,YACA,gBACA,OAMS;CACT,MAAM,YAAY,mBAAmB,KAAK,QAAQ;AAClD,KAAI,CAAC,UAAW,QAAO;CAEvB,MAAM,SAAS,uBAAuB;EACpC,UAAU,UAAU;EACpB;EACA;EACD,CAAC;AAEF,KAAI,CAAC,OAAQ,QAAO;CAEpB,MAAM,cAAc,GAAG,SAAS,UAAU,SAAS,UAAU;AAE7D,QAAO,cAAc,IAAI,GAAG,GAAG,UAAU,SAAS,gBAAgB;;AAGpE,SAAgB,4BAA4B,YAAoB,UAA2B;AACzF,KAAI;EACF,MAAM,YAAY,IAAI,IAAI,WAAW;EACrC,MAAM,YAAY,IAAI,IAAI,UAAU,UAAU,OAAO;AAErD,SACE,UAAU,WAAW,UAAU,UAC/B,mBAAmB,UAAU,SAAS,KAAK,mBAAmB,UAAU,SAAS,IACjF,UAAU,WAAW,UAAU;SAE3B;AACN,SAAO"}
@@ -0,0 +1,95 @@
1
+ //#region src/core/standard-routes/types.d.ts
2
+ type StandardRouteTemplateWithParam<Param extends string> = `/${string}:${Param}` | `/${string}:${Param}/${string}`;
3
+ type StandardRouteTemplate = `/${string}`;
4
+ type ShopifyRouteTemplates = {
5
+ /**
6
+ * Redirects Shopify article routes, for example
7
+ * `/blogs/news/snowboard-guide`, to the app's custom article route.
8
+ *
9
+ * @example "/journal/:blogHandle/:articleHandle"
10
+ */
11
+ article?: StandardRouteTemplateWithParam<"blogHandle"> & StandardRouteTemplateWithParam<"articleHandle">;
12
+ /**
13
+ * Redirects Shopify blog routes, for example `/blogs/news`, to the app's
14
+ * custom blog route.
15
+ *
16
+ * @example "/journal/:blogHandle"
17
+ */
18
+ blog?: StandardRouteTemplateWithParam<"blogHandle">;
19
+ /**
20
+ * Redirects Shopify's `/cart` route to the app's custom cart route.
21
+ *
22
+ * @example "/basket"
23
+ */
24
+ cart?: StandardRouteTemplate;
25
+ /**
26
+ * Redirects Shopify collection routes, for example `/collections/winter`,
27
+ * to the app's custom collection route.
28
+ *
29
+ * @example "/c/:collectionHandle"
30
+ */
31
+ collection?: StandardRouteTemplateWithParam<"collectionHandle">;
32
+ /**
33
+ * Redirects Shopify's collection-listing routes, `/collections` and the
34
+ * legacy `/products`, to the app's custom collection-listing route.
35
+ *
36
+ * @example "/catalog"
37
+ */
38
+ collectionList?: StandardRouteTemplate;
39
+ /**
40
+ * Redirects Shopify page routes, for example `/pages/about-us`, to the app's
41
+ * custom page route.
42
+ *
43
+ * @example "/content/:pageHandle"
44
+ */
45
+ page?: StandardRouteTemplateWithParam<"pageHandle">;
46
+ /**
47
+ * Redirects Shopify policy routes, for example `/policies/privacy-policy`,
48
+ * to the app's custom policy route.
49
+ *
50
+ * @example "/legal/:policyHandle"
51
+ */
52
+ policy?: StandardRouteTemplateWithParam<"policyHandle">;
53
+ /**
54
+ * Redirects Shopify product routes, for example `/products/snowboard`,
55
+ * to the app's custom product route.
56
+ *
57
+ * @example "/p/:productHandle"
58
+ */
59
+ product?: StandardRouteTemplateWithParam<"productHandle">;
60
+ /**
61
+ * Redirects Shopify collection-scoped product routes, for example
62
+ * `/collections/winter/products/snowboard`, to the app's custom product route.
63
+ *
64
+ * Must include `:productHandle`. May include `:collectionHandle` when the
65
+ * app's custom product route is also collection-scoped.
66
+ *
67
+ * @example "/p/:productHandle"
68
+ * @example "/c/:collectionHandle/p/:productHandle"
69
+ */
70
+ productInCollection?: StandardRouteTemplateWithParam<"productHandle">;
71
+ /**
72
+ * Redirects Shopify's `/search` route to the app's custom search route.
73
+ *
74
+ * @example "/find"
75
+ */
76
+ search?: StandardRouteTemplate;
77
+ };
78
+ type StandardRouteName = keyof ShopifyRouteTemplates;
79
+ type ShopifyStandardRouteName = StandardRouteName | "index";
80
+ type ShopifyPageTemplateName<TRoute extends ShopifyStandardRouteName = ShopifyStandardRouteName> = TRoute extends "productInCollection" ? "product" : TRoute extends "collectionList" ? "list-collections" : TRoute;
81
+ type StandardRouteParamName = "articleHandle" | "blogHandle" | "collectionHandle" | "pageHandle" | "policyHandle" | "productHandle";
82
+ type StandardRouteParams = Partial<Record<StandardRouteParamName, string>>;
83
+ type ShopifyStandardRouteMatch<TRoute extends ShopifyStandardRouteName = ShopifyStandardRouteName> = {
84
+ standardPathname: string;
85
+ params: StandardRouteParams;
86
+ route: TRoute;
87
+ pageTemplateName: ShopifyPageTemplateName<TRoute>;
88
+ templates: {
89
+ standard: string;
90
+ custom: string;
91
+ };
92
+ };
93
+ //#endregion
94
+ export { ShopifyRouteTemplates, ShopifyStandardRouteMatch };
95
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1,11 @@
1
+ //#region src/core/types.d.ts
2
+ type GraphiQLOptions = {
3
+ customerAccount?: {
4
+ apiUrl: string;
5
+ accessToken: string;
6
+ schemaUrl: string;
7
+ };
8
+ };
9
+ //#endregion
10
+ export { GraphiQLOptions };
11
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1,42 @@
1
+ //#region src/core/url.ts
2
+ const SFAPI_RE = /^\/api\/(unstable|2\d{3}-\d{2})\/graphql\.json$/;
3
+ const SHOPIFY_API_PROXY_PREFIX = "/__shopify";
4
+ const SHOPIFY_API_PROXY_RE = /^\/__shopify(?:\/|$)/;
5
+ const MCP_RE = /^\/api\/mcp$/;
6
+ const CHECKOUT_RE = /^\/checkout$/;
7
+ const CART_PERMALINK_RE = /^\/cart\/\d+:\d+(?:,\d+:\d+)*$/;
8
+ const CUSTOMER_ACCOUNT_PATHS = {
9
+ authorize: "/account/authorize",
10
+ login: "/account/login",
11
+ logout: "/account/logout",
12
+ refresh: "/account/refresh"
13
+ };
14
+ const CUSTOMER_ACCOUNT_HANDOFF_PATHS = new Set(Object.values(CUSTOMER_ACCOUNT_PATHS));
15
+ /**
16
+ * Returns whether the pathname conventionally represents a Hydrogen document-level server handoff.
17
+ *
18
+ * Keep this aligned with the routes intercepted before framework routing. API and protocol
19
+ * interceptors are intentionally excluded because they are not browser navigation destinations.
20
+ */
21
+ function isHydrogenServerHandoffPath(pathname) {
22
+ return CHECKOUT_RE.test(pathname) || CART_PERMALINK_RE.test(pathname) || CUSTOMER_ACCOUNT_HANDOFF_PATHS.has(pathname);
23
+ }
24
+ /**
25
+ * Allowlisted `.well-known` resources proxied to the Online Store origin:
26
+ * - `apple-developer-merchantid-domain-association` — Apple Pay domain verification.
27
+ * - `shopify/fec/produce` — Frontend Event Collector ingress. On the Online
28
+ * Store the myshopify.com edge forwards this first-party path to the
29
+ * collector; headless storefronts have no such edge, so Hydrogen proxies it
30
+ * to the same origin (used by WebMCP's Event Refinery client).
31
+ */
32
+ const WELL_KNOWN_RE = /^\/\.well-known\/(?:apple-developer-merchantid-domain-association|shopify\/fec\/produce)$/;
33
+ const AJAX_CART_RE = /^(?:\/[a-z]{2}(?:-[a-z]{2})?)?\/cart(?:\.(?:js|json)|\/(?:add|update|change|clear)(?:\.(?:js|json))?)$/i;
34
+ function normalizeStoreDomain(domain) {
35
+ if (!domain) throw new Error("Storefront `storeDomain` is required.");
36
+ if (domain.startsWith("http://") || domain.startsWith("https://")) return domain.replace(/\/+$/, "");
37
+ return `https://${domain}`.replace(/\/+$/, "");
38
+ }
39
+
40
+ //#endregion
41
+ export { AJAX_CART_RE, CHECKOUT_RE, CUSTOMER_ACCOUNT_PATHS, MCP_RE, SFAPI_RE, SHOPIFY_API_PROXY_PREFIX, SHOPIFY_API_PROXY_RE, WELL_KNOWN_RE, isHydrogenServerHandoffPath, normalizeStoreDomain };
42
+ //# sourceMappingURL=url.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.mjs","names":[],"sources":["../../../src/core/url.ts"],"sourcesContent":["export const SFAPI_RE = /^\\/api\\/(unstable|2\\d{3}-\\d{2})\\/graphql\\.json$/;\nexport const SHOPIFY_API_PROXY_PREFIX = \"/__shopify\";\nexport const SHOPIFY_API_PROXY_RE = /^\\/__shopify(?:\\/|$)/;\nexport const MCP_RE = /^\\/api\\/mcp$/;\nexport const CHECKOUT_RE = /^\\/checkout$/;\nexport const CART_PERMALINK_RE = /^\\/cart\\/\\d+:\\d+(?:,\\d+:\\d+)*$/;\nexport const CUSTOMER_ACCOUNT_PATHS = {\n authorize: \"/account/authorize\",\n login: \"/account/login\",\n logout: \"/account/logout\",\n refresh: \"/account/refresh\",\n} as const;\n\nconst CUSTOMER_ACCOUNT_HANDOFF_PATHS: ReadonlySet<string> = new Set(\n Object.values(CUSTOMER_ACCOUNT_PATHS),\n);\n\n/**\n * Returns whether the pathname conventionally represents a Hydrogen document-level server handoff.\n *\n * Keep this aligned with the routes intercepted before framework routing. API and protocol\n * interceptors are intentionally excluded because they are not browser navigation destinations.\n */\nexport function isHydrogenServerHandoffPath(pathname: string): boolean {\n return (\n CHECKOUT_RE.test(pathname) ||\n CART_PERMALINK_RE.test(pathname) ||\n CUSTOMER_ACCOUNT_HANDOFF_PATHS.has(pathname)\n );\n}\n\n/**\n * Allowlisted `.well-known` resources proxied to the Online Store origin:\n * - `apple-developer-merchantid-domain-association` — Apple Pay domain verification.\n * - `shopify/fec/produce` — Frontend Event Collector ingress. On the Online\n * Store the myshopify.com edge forwards this first-party path to the\n * collector; headless storefronts have no such edge, so Hydrogen proxies it\n * to the same origin (used by WebMCP's Event Refinery client).\n */\nexport const WELL_KNOWN_RE =\n /^\\/\\.well-known\\/(?:apple-developer-merchantid-domain-association|shopify\\/fec\\/produce)$/;\nexport const AJAX_CART_RE =\n /^(?:\\/[a-z]{2}(?:-[a-z]{2})?)?\\/cart(?:\\.(?:js|json)|\\/(?:add|update|change|clear)(?:\\.(?:js|json))?)$/i;\n\nexport function normalizeStoreDomain(domain: string): string {\n if (!domain) {\n throw new Error(\"Storefront `storeDomain` is required.\");\n }\n\n if (domain.startsWith(\"http://\") || domain.startsWith(\"https://\")) {\n return domain.replace(/\\/+$/, \"\");\n }\n return `https://${domain}`.replace(/\\/+$/, \"\");\n}\n"],"mappings":";AAAA,MAAa,WAAW;AACxB,MAAa,2BAA2B;AACxC,MAAa,uBAAuB;AACpC,MAAa,SAAS;AACtB,MAAa,cAAc;AAC3B,MAAa,oBAAoB;AACjC,MAAa,yBAAyB;CACpC,WAAW;CACX,OAAO;CACP,QAAQ;CACR,SAAS;CACV;AAED,MAAM,iCAAsD,IAAI,IAC9D,OAAO,OAAO,uBAAuB,CACtC;;;;;;;AAQD,SAAgB,4BAA4B,UAA2B;AACrE,QACE,YAAY,KAAK,SAAS,IAC1B,kBAAkB,KAAK,SAAS,IAChC,+BAA+B,IAAI,SAAS;;;;;;;;;;AAYhD,MAAa,gBACX;AACF,MAAa,eACX;AAEF,SAAgB,qBAAqB,QAAwB;AAC3D,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,wCAAwC;AAG1D,KAAI,OAAO,WAAW,UAAU,IAAI,OAAO,WAAW,WAAW,CAC/D,QAAO,OAAO,QAAQ,QAAQ,GAAG;AAEnC,QAAO,WAAW,SAAS,QAAQ,QAAQ,GAAG"}
@@ -0,0 +1,34 @@
1
+ //#region src/core/utils/load-script.ts
2
+ /**
3
+ * Inlined from @shopify/hydrogen-react/load-script.
4
+ * Pure DOM script loader with no framework dependency.
5
+ */
6
+ const SCRIPTS_LOADED = {};
7
+ function loadScript(src, options) {
8
+ const isScriptLoaded = SCRIPTS_LOADED[src];
9
+ if (isScriptLoaded) return isScriptLoaded;
10
+ const promise = new Promise((resolve, reject) => {
11
+ const script = document.createElement("script");
12
+ script.type = options?.module ? "module" : "text/javascript";
13
+ script.src = src;
14
+ script.addEventListener("load", () => resolve(true));
15
+ script.addEventListener("error", () => {
16
+ delete SCRIPTS_LOADED[src];
17
+ reject(false);
18
+ });
19
+ if (options?.in === "head") document.head.appendChild(script);
20
+ else document.body.appendChild(script);
21
+ if (options?.attributes) {
22
+ const { attributes } = options;
23
+ Object.keys(attributes).forEach((key) => {
24
+ script.setAttribute(key, attributes[key]);
25
+ });
26
+ }
27
+ });
28
+ SCRIPTS_LOADED[src] = promise;
29
+ return promise;
30
+ }
31
+
32
+ //#endregion
33
+ export { loadScript };
34
+ //# sourceMappingURL=load-script.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-script.mjs","names":[],"sources":["../../../../src/core/utils/load-script.ts"],"sourcesContent":["/**\n * Inlined from @shopify/hydrogen-react/load-script.\n * Pure DOM script loader with no framework dependency.\n */\n\nconst SCRIPTS_LOADED: Record<string, Promise<boolean>> = {};\n\ntype LoadScriptOptions = {\n module?: boolean;\n in?: \"head\" | \"body\";\n attributes?: Record<string, string>;\n};\n\nexport function loadScript(src: string, options?: LoadScriptOptions): Promise<boolean> {\n const isScriptLoaded = SCRIPTS_LOADED[src];\n if (isScriptLoaded) return isScriptLoaded;\n\n const promise = new Promise<boolean>((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.type = options?.module ? \"module\" : \"text/javascript\";\n script.src = src;\n script.addEventListener(\"load\", () => resolve(true));\n script.addEventListener(\"error\", () => {\n delete SCRIPTS_LOADED[src];\n reject(false);\n });\n\n if (options?.in === \"head\") {\n document.head.appendChild(script);\n } else {\n document.body.appendChild(script);\n }\n\n if (options?.attributes) {\n const { attributes } = options;\n Object.keys(attributes).forEach((key) => {\n script.setAttribute(key, attributes[key]);\n });\n }\n });\n\n SCRIPTS_LOADED[src] = promise;\n return promise;\n}\n"],"mappings":";;;;;AAKA,MAAM,iBAAmD,EAAE;AAQ3D,SAAgB,WAAW,KAAa,SAA+C;CACrF,MAAM,iBAAiB,eAAe;AACtC,KAAI,eAAgB,QAAO;CAE3B,MAAM,UAAU,IAAI,SAAkB,SAAS,WAAW;EACxD,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,SAAO,OAAO,SAAS,SAAS,WAAW;AAC3C,SAAO,MAAM;AACb,SAAO,iBAAiB,cAAc,QAAQ,KAAK,CAAC;AACpD,SAAO,iBAAiB,eAAe;AACrC,UAAO,eAAe;AACtB,UAAO,MAAM;IACb;AAEF,MAAI,SAAS,OAAO,OAClB,UAAS,KAAK,YAAY,OAAO;MAEjC,UAAS,KAAK,YAAY,OAAO;AAGnC,MAAI,SAAS,YAAY;GACvB,MAAM,EAAE,eAAe;AACvB,UAAO,KAAK,WAAW,CAAC,SAAS,QAAQ;AACvC,WAAO,aAAa,KAAK,WAAW,KAAK;KACzC;;GAEJ;AAEF,gBAAe,OAAO;AACtB,QAAO"}
@@ -0,0 +1,33 @@
1
+ //#region src/core/utils/parse-gid.ts
2
+ function parseGid(gid) {
3
+ const defaultReturn = {
4
+ id: "",
5
+ resource: null,
6
+ resourceId: null,
7
+ search: "",
8
+ searchParams: new URLSearchParams(),
9
+ hash: ""
10
+ };
11
+ if (typeof gid !== "string") return defaultReturn;
12
+ try {
13
+ const { search, searchParams, pathname, hash } = new URL(gid);
14
+ const pathnameParts = pathname.split("/");
15
+ const lastPathnamePart = pathnameParts[pathnameParts.length - 1];
16
+ const resourcePart = pathnameParts[pathnameParts.length - 2];
17
+ if (!lastPathnamePart || !resourcePart) return defaultReturn;
18
+ return {
19
+ id: `${lastPathnamePart}${search}${hash}` || "",
20
+ resource: resourcePart ?? null,
21
+ resourceId: lastPathnamePart || null,
22
+ search,
23
+ searchParams,
24
+ hash
25
+ };
26
+ } catch {
27
+ return defaultReturn;
28
+ }
29
+ }
30
+
31
+ //#endregion
32
+ export { parseGid };
33
+ //# sourceMappingURL=parse-gid.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-gid.mjs","names":[],"sources":["../../../../src/core/utils/parse-gid.ts"],"sourcesContent":["/**\n * Inlined from @shopify/hydrogen-react/analytics-utils.\n * Parses Shopify Global IDs (GIDs) into their component parts.\n */\n\ntype ShopifyGid = Pick<URL, \"search\" | \"searchParams\" | \"hash\"> & {\n id: string;\n resource: string | null;\n resourceId: string | null;\n};\n\nexport function parseGid(gid: string | undefined): ShopifyGid {\n const defaultReturn: ShopifyGid = {\n id: \"\",\n resource: null,\n resourceId: null,\n search: \"\",\n searchParams: new URLSearchParams(),\n hash: \"\",\n };\n\n if (typeof gid !== \"string\") return defaultReturn;\n\n try {\n const { search, searchParams, pathname, hash } = new URL(gid);\n const pathnameParts = pathname.split(\"/\");\n const lastPathnamePart = pathnameParts[pathnameParts.length - 1];\n const resourcePart = pathnameParts[pathnameParts.length - 2];\n\n if (!lastPathnamePart || !resourcePart) return defaultReturn;\n\n const id = `${lastPathnamePart}${search}${hash}` || \"\";\n const resourceId = lastPathnamePart || null;\n const resource = resourcePart ?? null;\n\n return { id, resource, resourceId, search, searchParams, hash };\n } catch {\n return defaultReturn;\n }\n}\n"],"mappings":";AAWA,SAAgB,SAAS,KAAqC;CAC5D,MAAM,gBAA4B;EAChC,IAAI;EACJ,UAAU;EACV,YAAY;EACZ,QAAQ;EACR,cAAc,IAAI,iBAAiB;EACnC,MAAM;EACP;AAED,KAAI,OAAO,QAAQ,SAAU,QAAO;AAEpC,KAAI;EACF,MAAM,EAAE,QAAQ,cAAc,UAAU,SAAS,IAAI,IAAI,IAAI;EAC7D,MAAM,gBAAgB,SAAS,MAAM,IAAI;EACzC,MAAM,mBAAmB,cAAc,cAAc,SAAS;EAC9D,MAAM,eAAe,cAAc,cAAc,SAAS;AAE1D,MAAI,CAAC,oBAAoB,CAAC,aAAc,QAAO;AAM/C,SAAO;GAAE,IAJE,GAAG,mBAAmB,SAAS,UAAU;GAIvC,UAFI,gBAAgB;GAEV,YAHJ,oBAAoB;GAGJ;GAAQ;GAAc;GAAM;SACzD;AACN,SAAO"}
@@ -0,0 +1,46 @@
1
+ import { ShopifyRequestContext } from "../core/request-context.mjs";
2
+ import { ShopifyRouteSessionManager } from "../core/request-routing/route-types.mjs";
3
+ //#region src/customer-account/session.d.ts
4
+ type Awaitable<T> = T | Promise<T>;
5
+ /** Read-only Customer Account session storage for UI state and strict access-token reads. */
6
+ type ReadonlyCustomerSessionManager = {
7
+ getSessionItem(key: string): Awaitable<unknown>;
8
+ };
9
+ /** Writable Customer Account session storage for OAuth, refresh, and logout response boundaries. */
10
+ type WritableCustomerSessionManager = ShopifyRouteSessionManager;
11
+ type PrepareLoginUrlOptions = {
12
+ origin?: string;
13
+ returnTo?: string;
14
+ locale?: string;
15
+ countryCode?: string;
16
+ loginHint?: string;
17
+ loginHintMode?: string;
18
+ acrValues?: string;
19
+ };
20
+ type RequestOriginOptions = {
21
+ origin?: string;
22
+ };
23
+ type LogoutOptions = RequestOriginOptions & {
24
+ postLogoutRedirectUri?: string;
25
+ };
26
+ type CustomerSession = {
27
+ /**
28
+ * Read-only signed-in check for UI state. Returns true for a usable access token
29
+ * or a refresh token that can attempt to restore one later. Not an authorization
30
+ * check for private Customer Account data.
31
+ */
32
+ isLoggedIn(sessionManager: ReadonlyCustomerSessionManager, requestContext: ShopifyRequestContext): Promise<boolean>; /** Returns only a currently usable access token. Does not refresh or mutate session state. */
33
+ getAccessToken(sessionManager: ReadonlyCustomerSessionManager, requestContext: ShopifyRequestContext): Promise<string | undefined>;
34
+ /**
35
+ * Returns a usable access token, writing updated session state through the
36
+ * supplied writable session manager when no usable access token is stored. Use
37
+ * only where the eventual response commits the session manager.
38
+ */
39
+ getOrRefreshAccessToken(sessionManager: WritableCustomerSessionManager, requestContext: ShopifyRequestContext, options?: RequestOriginOptions): Promise<string | undefined>;
40
+ prepareLoginUrl(sessionManager: WritableCustomerSessionManager, requestContext: ShopifyRequestContext, options: PrepareLoginUrlOptions): Promise<string>;
41
+ handleOAuthCallback(sessionManager: WritableCustomerSessionManager, requestContext: ShopifyRequestContext, request: Request): Promise<string>;
42
+ logout(sessionManager: WritableCustomerSessionManager, requestContext: ShopifyRequestContext, options?: LogoutOptions): Promise<string>;
43
+ };
44
+ //#endregion
45
+ export { CustomerSession, ReadonlyCustomerSessionManager, WritableCustomerSessionManager };
46
+ //# sourceMappingURL=session.d.mts.map
@@ -0,0 +1,21 @@
1
+ import { getLogger } from "../core/logging/logging.mjs";
2
+ import { CUSTOMER_ACCOUNT_PATHS } from "../core/url.mjs";
3
+
4
+ //#region src/customer-account/session.ts
5
+ const log = getLogger("customer-account");
6
+ const CUSTOMER_ACCOUNT_AUTHORIZE_PATH = CUSTOMER_ACCOUNT_PATHS.authorize;
7
+ const CUSTOMER_ACCOUNT_LOGIN_PATH = CUSTOMER_ACCOUNT_PATHS.login;
8
+ const CUSTOMER_ACCOUNT_LOGOUT_PATH = CUSTOMER_ACCOUNT_PATHS.logout;
9
+ const CUSTOMER_ACCOUNT_REFRESH_PATH = CUSTOMER_ACCOUNT_PATHS.refresh;
10
+ const PENDING_LOGIN_TTL_IN_MINUTES = 10;
11
+ const SECONDS_PER_MINUTE = 60;
12
+ const MILLISECONDS_PER_SECOND = 1e3;
13
+ const PENDING_LOGIN_TTL_IN_MS = PENDING_LOGIN_TTL_IN_MINUTES * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
14
+ const customerSessionInternals = /* @__PURE__ */ new WeakMap();
15
+ async function getCustomerSessionRefreshResult(customerSession, sessionManager, requestContext, options = {}) {
16
+ return customerSessionInternals.get(customerSession)?.getOrRefreshAccessToken(sessionManager, requestContext, options);
17
+ }
18
+
19
+ //#endregion
20
+ export { getCustomerSessionRefreshResult };
21
+ //# sourceMappingURL=session.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.mjs","names":[],"sources":["../../../src/customer-account/session.ts"],"sourcesContent":["import type { StorefrontClient } from \"../client\";\nimport {\n getCartBuyerIdentitySync,\n type CartBuyerIdentitySync,\n type CartBuyerIdentitySyncSource,\n} from \"../core/cart/buyer-identity-sync\";\nimport { DEFAULT_TIMEOUT_IN_MS } from \"../core/constants\";\nimport { getLogger } from \"../core/logging\";\nimport type { ShopifyRequestContext } from \"../core/request-context\";\nimport {\n createCallableRouteHandler,\n type CallableRouteHandler,\n type ShopifyRouteErrorResult,\n type ShopifyRouteHandlerContext,\n type ShopifyRouteRedirectResult,\n type ShopifyRouteSessionManager,\n} from \"../core/request-routing/registered-routes\";\nimport { CUSTOMER_ACCOUNT_PATHS } from \"../core/url\";\nimport { CustomerAccountApiError, CustomerAccountOAuthError } from \"./errors\";\n\nconst log = getLogger(\"customer-account\");\n\nexport const CUSTOMER_ACCOUNT_AUTHORIZE_PATH = CUSTOMER_ACCOUNT_PATHS.authorize;\nexport const CUSTOMER_ACCOUNT_LOGIN_PATH = CUSTOMER_ACCOUNT_PATHS.login;\nexport const CUSTOMER_ACCOUNT_LOGOUT_PATH = CUSTOMER_ACCOUNT_PATHS.logout;\nexport const CUSTOMER_ACCOUNT_REFRESH_PATH = CUSTOMER_ACCOUNT_PATHS.refresh;\n\nconst CUSTOMER_ACCOUNT_SESSION_KEY = \"customerAccount\";\nconst DEFAULT_LOGIN_RETURN_TO_PATH = \"/account\";\nconst DEFAULT_POST_LOGIN_REDIRECT_PATHNAME = \"/\";\nconst DEFAULT_POST_LOGOUT_REDIRECT_URI = \"/\";\nconst FAILED_LOGIN_PATH = \"/account?login=failed\";\nconst FORBIDDEN_STATUS = 403;\nconst FORBIDDEN_ERROR_CODE = \"forbidden\";\nconst FORBIDDEN_ERROR_MESSAGE = \"Forbidden\";\nconst NO_STORE_CACHE_CONTROL = \"no-store\";\nconst AUTHORIZATION_CODE_GRANT_TYPE = \"authorization_code\";\nconst REFRESH_TOKEN_GRANT_TYPE = \"refresh_token\";\nconst CUSTOMER_ACCOUNT_SCOPE = \"openid email customer-account-api:full\";\nconst CODE_CHALLENGE_METHOD = \"S256\";\nconst USER_AGENT = `Hydrogen ${__HYDROGEN_VERSION__}`;\nconst EXPIRY_BUFFER_IN_SECONDS = 120;\nconst PENDING_LOGIN_TTL_IN_MINUTES = 10;\nconst SECONDS_PER_MINUTE = 60;\nconst MILLISECONDS_PER_SECOND = 1_000;\nconst PENDING_LOGIN_TTL_IN_MS =\n PENDING_LOGIN_TTL_IN_MINUTES * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;\nconst RANDOM_BYTES_LENGTH = 32;\nconst MAX_RETURN_TO_LENGTH_IN_BYTES = 2_048;\nconst MAX_SET_TIMEOUT_IN_MS = 2_147_483_647;\nconst SHOP_ID_RE = /^\\d+$/;\nconst CUSTOMER_SESSION_ACCESS_TOKEN_PERSONALIZATION_REASON = \"customer-session-access-token\";\nconst CUSTOMER_SESSION_MUTATION_PERSONALIZATION_REASON = \"customer-session-mutation\";\nconst CUSTOMER_SESSION_INTERNAL_BRAND: unique symbol = Symbol(\"hydrogen.customerSessionInternal\");\n\nexport type Awaitable<T> = T | Promise<T>;\n\n/** Read-only Customer Account session storage for UI state and strict access-token reads. */\nexport type ReadonlyCustomerSessionManager = {\n getSessionItem(key: string): Awaitable<unknown>;\n};\n\n/** Writable Customer Account session storage for OAuth, refresh, and logout response boundaries. */\nexport type WritableCustomerSessionManager = ShopifyRouteSessionManager;\n\ntype CustomerAccountSessionData = {\n tokens?: {\n accessToken?: string;\n refreshToken?: string;\n idToken?: string;\n expiresAt?: number;\n };\n pendingLogin?: {\n state?: string;\n nonce?: string;\n codeVerifier?: string;\n returnTo?: string;\n origin?: string;\n createdAt?: number;\n };\n};\n\nexport type CreateCustomerSessionOptions = {\n shopId: string;\n customerAccountApiClientId: string;\n customerAccountApiUrl?: string;\n fetch?: typeof globalThis.fetch;\n defaultTimeoutInMs?: number;\n};\n\nexport type PrepareLoginUrlOptions = {\n origin?: string;\n returnTo?: string;\n locale?: string;\n countryCode?: string;\n loginHint?: string;\n loginHintMode?: string;\n acrValues?: string;\n};\n\nexport type RequestOriginOptions = {\n origin?: string;\n};\n\nexport type LogoutOptions = RequestOriginOptions & {\n postLogoutRedirectUri?: string;\n};\n\nexport type CustomerSession = {\n /**\n * Read-only signed-in check for UI state. Returns true for a usable access token\n * or a refresh token that can attempt to restore one later. Not an authorization\n * check for private Customer Account data.\n */\n isLoggedIn(\n sessionManager: ReadonlyCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n ): Promise<boolean>;\n /** Returns only a currently usable access token. Does not refresh or mutate session state. */\n getAccessToken(\n sessionManager: ReadonlyCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n ): Promise<string | undefined>;\n /**\n * Returns a usable access token, writing updated session state through the\n * supplied writable session manager when no usable access token is stored. Use\n * only where the eventual response commits the session manager.\n */\n getOrRefreshAccessToken(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options?: RequestOriginOptions,\n ): Promise<string | undefined>;\n prepareLoginUrl(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options: PrepareLoginUrlOptions,\n ): Promise<string>;\n handleOAuthCallback(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n request: Request,\n ): Promise<string>;\n logout(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options?: LogoutOptions,\n ): Promise<string>;\n};\n\nexport type CustomerAccountServerHandlers<\n TContext extends CustomerAccountRouteHandlerContext = CustomerAccountRouteHandlerContext,\n> = {\n authorize: CallableRouteHandler<\n TContext,\n CustomerAccountRouteResult,\n typeof CUSTOMER_ACCOUNT_AUTHORIZE_PATH,\n \"GET\"\n >;\n login: CallableRouteHandler<\n TContext,\n CustomerAccountRouteResult,\n typeof CUSTOMER_ACCOUNT_LOGIN_PATH,\n \"GET\"\n >;\n logout: CallableRouteHandler<\n TContext,\n CustomerAccountRouteResult,\n typeof CUSTOMER_ACCOUNT_LOGOUT_PATH,\n \"POST\"\n >;\n refresh: CallableRouteHandler<\n TContext,\n CustomerAccountRouteResult,\n typeof CUSTOMER_ACCOUNT_REFRESH_PATH,\n \"GET\"\n >;\n};\n\n/** Customer Account handlers that also synchronize cart buyer identity, so their context requires `storefrontClient`. */\nexport type CustomerAccountServerHandlersWithCartSync =\n CustomerAccountServerHandlers<ShopifyRouteHandlerContext>;\n\ntype CustomerSessionWithInternals = CustomerSession & {\n readonly [CUSTOMER_SESSION_INTERNAL_BRAND]: true;\n};\n\ntype CreateCustomerAccountServerHandlersBaseOptions<\n TCustomerSession extends CustomerSession = CustomerSession,\n> = {\n customerSession: TCustomerSession;\n defaultPostLoginRedirectPathname?: string;\n loginFailedRedirectPath?: string;\n origin?: string | ((request: Request) => string);\n postLogoutRedirectUri?: string;\n};\n\nexport type CreateCustomerAccountServerHandlersOptions =\n | (CreateCustomerAccountServerHandlersBaseOptions & { cartServerHandlers?: undefined })\n | (CreateCustomerAccountServerHandlersBaseOptions<CustomerSessionWithInternals> & {\n /**\n * Cart server handlers created with `createCartServerHandlers({customerSession})`.\n * When provided, the authorize, refresh, and logout routes keep the browser\n * cart's buyer identity in step with the customer session: attach on login\n * and refresh, detach on logout. Sync is best-effort — failures are logged\n * and never block the route's redirect; a failed detach during logout\n * expires the cart cookie instead.\n */\n cartServerHandlers: CartBuyerIdentitySyncSource;\n });\n\ntype CustomerAccountTokens = NonNullable<CustomerAccountSessionData[\"tokens\"]>;\ntype PendingLogin = NonNullable<CustomerAccountSessionData[\"pendingLogin\"]>;\ntype TokenEndpointResponse = {\n access_token?: unknown;\n refresh_token?: unknown;\n id_token?: unknown;\n expires_in?: unknown;\n};\ntype ParsedTokenEndpointResponse = {\n access_token: string;\n refresh_token?: string;\n id_token?: string;\n expires_in: number;\n};\ntype RefreshResult =\n | { type: \"success\"; tokens: CustomerAccountTokens }\n | { type: \"invalid\" }\n | { type: \"transient\" };\ntype TokenRefreshResult =\n | {\n /** A usable access token is available. */\n status: \"authenticated\";\n accessToken: string;\n }\n | {\n /** Refresh failed transiently, but the refreshable session remains. */\n status: \"transient\";\n accessToken: undefined;\n }\n | {\n /** No usable or refreshable customer session remains. */\n status: \"unauthenticated\";\n accessToken: undefined;\n };\ntype OAuthCallbackResult = {\n location: string;\n accessToken: string;\n};\ntype CustomerSessionInternals = {\n getOrRefreshAccessToken(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options?: RequestOriginOptions,\n ): Promise<TokenRefreshResult>;\n handleOAuthCallback(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n request: Request,\n ): Promise<OAuthCallbackResult>;\n};\nconst customerSessionInternals = new WeakMap<CustomerSession, CustomerSessionInternals>();\ntype CustomerAccountRouteResult = ShopifyRouteRedirectResult | ShopifyRouteErrorResult;\ntype CustomerAccountRouteHandlerContext = {\n request: Request;\n sessionManager: WritableCustomerSessionManager;\n requestContext: ShopifyRequestContext;\n};\ntype CustomerAccountRuntimeRouteHandlerContext = CustomerAccountRouteHandlerContext & {\n storefrontClient?: StorefrontClient;\n};\ntype CustomerAccountServerHandlersForOptions<TOptions> = TOptions extends {\n cartServerHandlers: CartBuyerIdentitySyncSource;\n}\n ? CustomerAccountServerHandlersWithCartSync\n : CustomerAccountServerHandlers;\ntype TokenRequestParams = {\n url: string;\n origin: string;\n body: URLSearchParams;\n fetch: typeof globalThis.fetch;\n signal: AbortSignal;\n};\n\nexport function createCustomerSession({\n shopId,\n customerAccountApiClientId,\n customerAccountApiUrl,\n fetch: customFetch,\n defaultTimeoutInMs = DEFAULT_TIMEOUT_IN_MS,\n}: CreateCustomerSessionOptions): CustomerSessionWithInternals {\n if (typeof document !== \"undefined\") {\n throw new Error(\n \"Customer Account OAuth sessions cannot be used in a browser context. Use this helper from server or edge routes only.\",\n );\n }\n\n validateShopId(shopId);\n validateCustomerAccountApiClientId(customerAccountApiClientId);\n validateTimeout(defaultTimeoutInMs);\n\n const fetch = customFetch ?? globalThis.fetch;\n if (typeof fetch !== \"function\") {\n throw new Error(\n \"No fetch function available. Pass a fetch option or ensure globalThis.fetch exists.\",\n );\n }\n\n const endpoints = createCustomerAccountEndpoints(shopId, customerAccountApiUrl);\n const refreshFlights = new Map<string, Promise<RefreshResult>>();\n\n async function getAccessToken(\n sessionManager: ReadonlyCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n ) {\n requestContext.markResponseAsPersonalized(CUSTOMER_SESSION_ACCESS_TOKEN_PERSONALIZATION_REASON);\n const accessToken = getUsableAccessToken(await readSessionData(sessionManager));\n return accessToken;\n }\n\n async function getOrRefreshAccessToken(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options: RequestOriginOptions = {},\n ) {\n const result = await getOrRefreshAccessTokenResult(sessionManager, requestContext, options);\n return result.accessToken;\n }\n\n async function getOrRefreshAccessTokenResult(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options: RequestOriginOptions = {},\n ): Promise<TokenRefreshResult> {\n requestContext.markResponseAsPersonalized(CUSTOMER_SESSION_ACCESS_TOKEN_PERSONALIZATION_REASON);\n const sessionData = await readSessionData(sessionManager);\n const accessToken = getUsableAccessToken(sessionData);\n if (accessToken) return { status: \"authenticated\", accessToken };\n\n const refreshToken = getRefreshToken(sessionData);\n if (!refreshToken) return { status: \"unauthenticated\", accessToken: undefined };\n\n const origin = await getResolvedOrigin(sessionManager, options.origin);\n const idToken = sessionData.tokens?.idToken;\n const refreshResult = await getRefreshResult({\n refreshFlights,\n refreshToken,\n idToken,\n origin,\n endpoints,\n customerAccountApiClientId,\n fetch,\n timeoutInMs: defaultTimeoutInMs,\n });\n\n if (refreshResult.type === \"success\") {\n const refreshedAccessToken = refreshResult.tokens.accessToken;\n if (!refreshedAccessToken) {\n return { status: \"transient\", accessToken: undefined };\n }\n await writeTokens(sessionManager, refreshResult.tokens);\n return {\n status: \"authenticated\",\n accessToken: refreshedAccessToken,\n };\n }\n\n if (refreshResult.type === \"invalid\") {\n await clearTokens(sessionManager);\n return { status: \"unauthenticated\", accessToken: undefined };\n }\n\n return { status: \"transient\", accessToken: undefined };\n }\n\n async function prepareLoginUrl(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options: PrepareLoginUrlOptions,\n ) {\n requestContext.markResponseAsPersonalized(CUSTOMER_SESSION_MUTATION_PERSONALIZATION_REASON);\n const origin = await getResolvedOrigin(sessionManager, options.origin);\n const state = generateRandomBase64Url();\n const nonce = generateRandomBase64Url();\n const codeVerifier = generateRandomBase64Url();\n const codeChallenge = await createCodeChallenge(codeVerifier);\n const returnTo = sanitizeReturnTo(options.returnTo, origin);\n const createdAt = Date.now();\n\n const sessionData = await readSessionData(sessionManager);\n await writeSessionData(sessionManager, {\n ...sessionData,\n pendingLogin: { state, nonce, codeVerifier, returnTo, origin, createdAt },\n });\n\n const loginUrl = new URL(endpoints.authorizeUrl);\n loginUrl.searchParams.set(\"client_id\", customerAccountApiClientId);\n loginUrl.searchParams.set(\"scope\", CUSTOMER_ACCOUNT_SCOPE);\n loginUrl.searchParams.set(\"response_type\", \"code\");\n loginUrl.searchParams.set(\"redirect_uri\", `${origin}${CUSTOMER_ACCOUNT_AUTHORIZE_PATH}`);\n loginUrl.searchParams.set(\"state\", state);\n loginUrl.searchParams.set(\"nonce\", nonce);\n loginUrl.searchParams.set(\"code_challenge\", codeChallenge);\n loginUrl.searchParams.set(\"code_challenge_method\", CODE_CHALLENGE_METHOD);\n setOptionalLoginParams(loginUrl, options);\n\n return loginUrl.toString();\n }\n\n async function handleOAuthCallback(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n request: Request,\n ) {\n return (await handleOAuthCallbackResult(sessionManager, requestContext, request)).location;\n }\n\n async function handleOAuthCallbackResult(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n request: Request,\n ): Promise<OAuthCallbackResult> {\n requestContext.markResponseAsPersonalized(CUSTOMER_SESSION_MUTATION_PERSONALIZATION_REASON);\n try {\n return await completeOAuthCallback({\n sessionManager,\n request,\n endpoints,\n customerAccountApiClientId,\n fetch,\n timeoutInMs: defaultTimeoutInMs,\n });\n } catch (error) {\n await clearPendingLogin(sessionManager);\n throw error;\n }\n }\n\n async function logout(\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options: LogoutOptions = {},\n ) {\n requestContext.markResponseAsPersonalized(CUSTOMER_SESSION_MUTATION_PERSONALIZATION_REASON);\n const origin = await getResolvedOrigin(sessionManager, options.origin);\n const sessionData = await readSessionData(sessionManager);\n const idToken = sessionData.tokens?.idToken;\n const postLogoutRedirectUri = absoluteSameOriginUrl(\n options.postLogoutRedirectUri ?? origin,\n origin,\n );\n\n await sessionManager.removeSessionItem(CUSTOMER_ACCOUNT_SESSION_KEY);\n\n if (!idToken) return postLogoutRedirectUri;\n\n const logoutUrl = new URL(endpoints.logoutUrl);\n logoutUrl.searchParams.set(\"id_token_hint\", idToken);\n logoutUrl.searchParams.set(\"post_logout_redirect_uri\", postLogoutRedirectUri);\n return logoutUrl.toString();\n }\n\n const customerSession: CustomerSessionWithInternals = {\n [CUSTOMER_SESSION_INTERNAL_BRAND]: true,\n isLoggedIn: async (sessionManager, requestContext) => {\n requestContext.markResponseAsPersonalized(\n CUSTOMER_SESSION_ACCESS_TOKEN_PERSONALIZATION_REASON,\n );\n return hasCustomerSession(await readSessionData(sessionManager));\n },\n getAccessToken,\n getOrRefreshAccessToken,\n prepareLoginUrl,\n handleOAuthCallback,\n logout,\n };\n customerSessionInternals.set(customerSession, {\n getOrRefreshAccessToken: getOrRefreshAccessTokenResult,\n handleOAuthCallback: handleOAuthCallbackResult,\n });\n return customerSession;\n}\n\nexport async function getCustomerSessionRefreshResult(\n customerSession: CustomerSession,\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options: RequestOriginOptions = {},\n): Promise<TokenRefreshResult | undefined> {\n return customerSessionInternals\n .get(customerSession)\n ?.getOrRefreshAccessToken(sessionManager, requestContext, options);\n}\n\n/**\n * Creates Customer Account server handlers (`/account/login`, `/account/authorize`,\n * `/account/refresh`, `/account/logout`) for `handleShopifyRoutes`. The handlers\n * return redirect results: `login` redirects to Shopify's OAuth, `logout` redirects\n * to Shopify's logout endpoint when an `id_token` exists, and `authorize`/`refresh`\n * redirect back to the app (same-origin `return_to`). Invoke these paths via\n * full-page navigation (plain `<a>`/`<form>`), not a framework client-side\n * navigation component — client-nav cannot follow these raw redirects.\n */\nexport function createCustomerAccountServerHandlers<\n const TOptions extends CreateCustomerAccountServerHandlersOptions,\n>(options: TOptions): CustomerAccountServerHandlersForOptions<TOptions>;\nexport function createCustomerAccountServerHandlers(\n options: CreateCustomerAccountServerHandlersOptions,\n): CustomerAccountServerHandlers | CustomerAccountServerHandlersWithCartSync {\n const {\n customerSession,\n defaultPostLoginRedirectPathname = DEFAULT_POST_LOGIN_REDIRECT_PATHNAME,\n loginFailedRedirectPath = FAILED_LOGIN_PATH,\n postLogoutRedirectUri = DEFAULT_POST_LOGOUT_REDIRECT_URI,\n } = options;\n const { origin: originOption } = options;\n const cartSync = resolveCartBuyerIdentitySync(options);\n\n return {\n authorize: createCallableRouteHandler(\n CUSTOMER_ACCOUNT_AUTHORIZE_PATH,\n \"GET\",\n async (context: CustomerAccountRuntimeRouteHandlerContext) => {\n return handleAuthorizeRoute(\n customerSession,\n context,\n loginFailedRedirectPath,\n originOption,\n cartSync,\n );\n },\n ),\n login: createCallableRouteHandler(\n CUSTOMER_ACCOUNT_LOGIN_PATH,\n \"GET\",\n async (context: CustomerAccountRuntimeRouteHandlerContext) => {\n const { request, sessionManager, requestContext } = context;\n return handleLoginRoute(\n customerSession,\n sessionManager,\n requestContext,\n request,\n defaultPostLoginRedirectPathname,\n originOption,\n );\n },\n ),\n logout: createCallableRouteHandler(\n CUSTOMER_ACCOUNT_LOGOUT_PATH,\n \"POST\",\n async (context: CustomerAccountRuntimeRouteHandlerContext) => {\n return handleLogoutRoute(\n customerSession,\n context,\n postLogoutRedirectUri,\n originOption,\n cartSync,\n );\n },\n ),\n refresh: createCallableRouteHandler(\n CUSTOMER_ACCOUNT_REFRESH_PATH,\n \"GET\",\n async (context: CustomerAccountRuntimeRouteHandlerContext) => {\n return handleRefreshRoute(customerSession, context, originOption, cartSync);\n },\n ),\n };\n}\n\nfunction resolveCartBuyerIdentitySync(\n options: CreateCustomerAccountServerHandlersOptions,\n): CartBuyerIdentitySync | undefined {\n if (!options.cartServerHandlers) return undefined;\n\n const cartSync = getCartBuyerIdentitySync(options.cartServerHandlers);\n if (!cartSync) {\n throw new Error(\n \"cartServerHandlers must be created by createCartServerHandlers with the customerSession option.\",\n );\n }\n if (!customerSessionInternals.has(options.customerSession)) {\n throw new Error(\n \"Cart buyer identity sync requires the customerSession returned by createCustomerSession.\",\n );\n }\n return cartSync;\n}\n\nasync function handleLoginRoute(\n customerSession: CustomerSession,\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n request: Request,\n defaultPostLoginRedirectPathname: string,\n originOption: string | ((request: Request) => string) | undefined,\n): Promise<CustomerAccountRouteResult> {\n const origin = await resolveRouteOrigin(sessionManager, request, originOption);\n const requestUrl = new URL(request.url);\n const defaultReturnTo = sanitizeReturnTo(\n defaultPostLoginRedirectPathname,\n origin,\n DEFAULT_POST_LOGIN_REDIRECT_PATHNAME,\n );\n const requestedReturnTo =\n requestUrl.searchParams.get(\"return_to\") ?? requestUrl.searchParams.get(\"returnTo\");\n const returnTo = sanitizeReturnTo(requestedReturnTo, origin, defaultReturnTo);\n const loginUrl = await customerSession.prepareLoginUrl(sessionManager, requestContext, {\n origin,\n returnTo,\n countryCode: requestContext.i18n.country,\n locale: getOptionalSearchParam(requestUrl, \"locale\"),\n acrValues: getOptionalSearchParam(requestUrl, \"acr_values\"),\n loginHint: getOptionalSearchParam(requestUrl, \"login_hint\"),\n loginHintMode: getOptionalSearchParam(requestUrl, \"login_hint_mode\"),\n });\n return redirectResult(loginUrl, await commitSession(sessionManager));\n}\n\nasync function handleLogoutRoute(\n customerSession: CustomerSession,\n context: CustomerAccountRuntimeRouteHandlerContext,\n postLogoutRedirectUri: string,\n originOption: string | ((request: Request) => string) | undefined,\n cartSync: CartBuyerIdentitySync | undefined,\n): Promise<CustomerAccountRouteResult> {\n const { request, sessionManager, requestContext } = context;\n const origin = await resolveRouteOrigin(sessionManager, request, originOption);\n if (!isSameOriginPost(request, origin)) return forbiddenResult();\n\n const requestUrl = new URL(request.url);\n const requestedReturnTo =\n requestUrl.searchParams.get(\"return_to\") ?? requestUrl.searchParams.get(\"returnTo\");\n const logoutUrl = await customerSession.logout(sessionManager, requestContext, {\n origin,\n postLogoutRedirectUri: sanitizeReturnTo(requestedReturnTo, origin, postLogoutRedirectUri),\n });\n\n // The redirect must complete even when detach fails: skipping it would leave\n // the Shopify IdP session alive after the app session is already destroyed.\n // Expiring the cart cookie is the fail-safe when the cart keeps the identity.\n let detachFailed = false;\n if (cartSync) {\n detachFailed = !(await syncCartBuyerIdentity(cartSync, context, null, \"logout\"));\n }\n const headers = new Headers(await commitSession(sessionManager));\n if (cartSync && detachFailed) headers.append(\"set-cookie\", cartSync.expiredCartCookie);\n return redirectResult(logoutUrl, headers);\n}\n\nasync function handleAuthorizeRoute(\n customerSession: CustomerSession,\n context: CustomerAccountRuntimeRouteHandlerContext,\n loginFailedRedirectPath: string,\n originOption: string | ((request: Request) => string) | undefined,\n cartSync: CartBuyerIdentitySync | undefined,\n): Promise<CustomerAccountRouteResult> {\n const { request, sessionManager, requestContext } = context;\n try {\n if (!cartSync) {\n const location = await customerSession.handleOAuthCallback(\n sessionManager,\n requestContext,\n request,\n );\n return redirectResult(location, await commitSession(sessionManager));\n }\n\n const { location, accessToken } = await handleOAuthCallbackWithAccessToken(\n customerSession,\n sessionManager,\n requestContext,\n request,\n );\n // Best-effort: the customer is authenticated at this point, so a failed\n // cart sync must not turn a successful login into an error response. The\n // next refresh retries the attach.\n await syncCartBuyerIdentity(cartSync, context, accessToken, \"authorize\");\n return redirectResult(location, await commitSession(sessionManager));\n } catch (error) {\n if (!(error instanceof CustomerAccountOAuthError)) throw error;\n const origin = await resolveRouteOrigin(sessionManager, request, originOption);\n return redirectResult(\n sanitizeReturnTo(loginFailedRedirectPath, origin),\n await commitSession(sessionManager),\n );\n }\n}\n\nasync function handleRefreshRoute(\n customerSession: CustomerSession,\n context: CustomerAccountRuntimeRouteHandlerContext,\n originOption: string | ((request: Request) => string) | undefined,\n cartSync: CartBuyerIdentitySync | undefined,\n): Promise<CustomerAccountRouteResult> {\n const { request, sessionManager, requestContext } = context;\n const origin = await resolveRouteOrigin(sessionManager, request, originOption);\n if (!cartSync) {\n await customerSession.getOrRefreshAccessToken(sessionManager, requestContext, { origin });\n return refreshRedirectResult(request, origin, await commitSession(sessionManager));\n }\n\n const refreshResult = await getOrRefreshAccessTokenWithStatus(\n customerSession,\n sessionManager,\n requestContext,\n { origin },\n );\n // Transient refresh failures keep the refreshable session, so the cart's\n // identity is left untouched; a definitive outcome attaches or detaches it.\n let detachFailed = false;\n if (refreshResult.status !== \"transient\") {\n const customerAccessToken = refreshResult.accessToken ?? null;\n const syncSucceeded = await syncCartBuyerIdentity(\n cartSync,\n context,\n customerAccessToken,\n \"refresh\",\n );\n detachFailed = customerAccessToken === null && !syncSucceeded;\n }\n\n const headers = new Headers(await commitSession(sessionManager));\n if (detachFailed) headers.append(\"set-cookie\", cartSync.expiredCartCookie);\n return refreshRedirectResult(request, origin, headers);\n}\n\nfunction refreshRedirectResult(\n request: Request,\n origin: string,\n headers?: HeadersInit,\n): ShopifyRouteRedirectResult {\n const requestUrl = new URL(request.url);\n const returnTo =\n requestUrl.searchParams.get(\"return_to\") ?? requestUrl.searchParams.get(\"returnTo\");\n return redirectResult(sanitizeReturnTo(returnTo, origin), headers);\n}\n\nasync function handleOAuthCallbackWithAccessToken(\n customerSession: CustomerSession,\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n request: Request,\n): Promise<OAuthCallbackResult> {\n const internal = customerSessionInternals.get(customerSession);\n if (!internal) {\n throw new Error(\"Customer session was not created by createCustomerSession\");\n }\n return internal.handleOAuthCallback(sessionManager, requestContext, request);\n}\n\nasync function getOrRefreshAccessTokenWithStatus(\n customerSession: CustomerSession,\n sessionManager: WritableCustomerSessionManager,\n requestContext: ShopifyRequestContext,\n options: RequestOriginOptions,\n): Promise<TokenRefreshResult> {\n const internal = customerSessionInternals.get(customerSession);\n if (!internal) {\n throw new Error(\"Customer session was not created by createCustomerSession\");\n }\n return internal.getOrRefreshAccessToken(sessionManager, requestContext, options);\n}\n\nasync function syncCartBuyerIdentity(\n cartSync: CartBuyerIdentitySync,\n context: CustomerAccountRuntimeRouteHandlerContext,\n customerAccessToken: string | null,\n route: \"authorize\" | \"refresh\" | \"logout\",\n): Promise<boolean> {\n try {\n if (!context.storefrontClient) {\n throw new Error(\n \"Customer Account handlers configured with cartServerHandlers require storefrontClient.\",\n );\n }\n await cartSync.updateBuyerIdentity(\n { request: context.request, storefrontClient: context.storefrontClient },\n customerAccessToken,\n );\n return true;\n } catch (error) {\n log.error(\"cart buyer identity sync failed\", { route, error });\n return false;\n }\n}\n\nasync function resolveRouteOrigin(\n sessionManager: WritableCustomerSessionManager,\n request: Request,\n originOption: string | ((request: Request) => string) | undefined,\n) {\n if (typeof originOption === \"function\") return normalizeOrigin(originOption(request));\n return getResolvedOrigin(sessionManager, originOption);\n}\n\nasync function getResolvedOrigin(\n sessionManager: WritableCustomerSessionManager,\n originOverride: string | undefined,\n) {\n return normalizeOrigin(originOverride ?? (await sessionManager.getSessionOrigin()));\n}\n\nfunction getOptionalSearchParam(url: URL, name: string): string | undefined {\n return url.searchParams.get(name) ?? undefined;\n}\n\nfunction redirectResult(location: string, headers?: HeadersInit): ShopifyRouteRedirectResult {\n const redirectHeaders = new Headers(headers);\n redirectHeaders.set(\"cache-control\", NO_STORE_CACHE_CONTROL);\n return { type: \"redirect\", location, headers: redirectHeaders };\n}\n\nfunction forbiddenResult(): ShopifyRouteErrorResult {\n return {\n type: \"error\",\n status: FORBIDDEN_STATUS,\n error: { code: FORBIDDEN_ERROR_CODE, message: FORBIDDEN_ERROR_MESSAGE },\n headers: { \"cache-control\": NO_STORE_CACHE_CONTROL },\n };\n}\n\nasync function commitSession(\n sessionManager: WritableCustomerSessionManager,\n): Promise<HeadersInit | undefined> {\n return (await sessionManager.commit?.()) ?? undefined;\n}\n\nasync function completeOAuthCallback({\n sessionManager,\n request,\n endpoints,\n customerAccountApiClientId,\n fetch,\n timeoutInMs,\n}: {\n sessionManager: WritableCustomerSessionManager;\n request: Request;\n endpoints: CustomerAccountEndpoints;\n customerAccountApiClientId: string;\n fetch: typeof globalThis.fetch;\n timeoutInMs: number;\n}): Promise<OAuthCallbackResult> {\n const requestUrl = new URL(request.url);\n const code = requestUrl.searchParams.get(\"code\");\n const state = requestUrl.searchParams.get(\"state\");\n const sessionData = await readSessionData(sessionManager);\n const pendingLogin = getPendingLogin(sessionData);\n\n assertOAuthCallbackParams(code, state, pendingLogin);\n\n const origin = pendingLogin.origin ?? getOriginFromRequest(request);\n const tokenResponse = await exchangeAuthorizationCode({\n endpoints,\n customerAccountApiClientId,\n origin,\n code,\n codeVerifier: pendingLogin.codeVerifier,\n fetch,\n timeoutInMs,\n });\n validateIdTokenClaims(tokenResponse.id_token, {\n audience: customerAccountApiClientId,\n issuer: endpoints.issuer,\n nonce: pendingLogin.nonce,\n });\n\n await writeSessionData(sessionManager, {\n tokens: createTokensFromResponse(tokenResponse),\n });\n\n return {\n location: pendingLogin.returnTo ?? DEFAULT_LOGIN_RETURN_TO_PATH,\n accessToken: tokenResponse.access_token,\n };\n}\n\nfunction isSameOriginPost(request: Request, trustedOrigin: string): boolean {\n const origin = request.headers.get(\"origin\");\n if (origin) {\n try {\n return normalizeOrigin(origin) === trustedOrigin;\n } catch {\n return false;\n }\n }\n\n const referer = request.headers.get(\"referer\");\n if (!referer) return false;\n\n try {\n return normalizeOrigin(new URL(referer).origin) === trustedOrigin;\n } catch {\n return false;\n }\n}\n\nfunction assertOAuthCallbackParams(\n code: string | null,\n state: string | null,\n pendingLogin: Required<PendingLogin>,\n): asserts code is string {\n if (!code || !state) {\n throw new CustomerAccountOAuthError(\n \"missing_callback_params\",\n \"OAuth callback is missing code or state\",\n );\n }\n\n if (pendingLogin.state !== state) {\n throw new CustomerAccountOAuthError(\n \"state_mismatch\",\n \"OAuth callback state does not match session state\",\n );\n }\n}\n\nasync function exchangeAuthorizationCode({\n endpoints,\n customerAccountApiClientId,\n origin,\n code,\n codeVerifier,\n fetch,\n timeoutInMs,\n}: {\n endpoints: CustomerAccountEndpoints;\n customerAccountApiClientId: string;\n origin: string;\n code: string;\n codeVerifier: string;\n fetch: typeof globalThis.fetch;\n timeoutInMs: number;\n}): Promise<Required<ParsedTokenEndpointResponse>> {\n const body = new URLSearchParams({\n grant_type: AUTHORIZATION_CODE_GRANT_TYPE,\n client_id: customerAccountApiClientId,\n redirect_uri: `${origin}${CUSTOMER_ACCOUNT_AUTHORIZE_PATH}`,\n code,\n code_verifier: codeVerifier,\n });\n return withTokenRequestTimeout(timeoutInMs, async (signal) => {\n const response = await postTokenRequest({\n url: endpoints.tokenUrl,\n origin,\n body,\n fetch,\n signal,\n });\n\n if (!response.ok) {\n cancelResponseBody(response);\n const errorCode =\n response.status === 400 || response.status === 401\n ? \"token_exchange_rejected\"\n : \"token_exchange_failed\";\n throw new CustomerAccountOAuthError(\n errorCode,\n `Customer Account OAuth token exchange failed with ${response.status}`,\n );\n }\n\n return parseRequiredTokenResponse(await readTokenResponseJson(response, signal));\n });\n}\n\nasync function getRefreshResult({\n refreshFlights,\n refreshToken,\n idToken,\n origin,\n endpoints,\n customerAccountApiClientId,\n fetch,\n timeoutInMs,\n}: {\n refreshFlights: Map<string, Promise<RefreshResult>>;\n refreshToken: string;\n idToken: string | undefined;\n origin: string;\n endpoints: CustomerAccountEndpoints;\n customerAccountApiClientId: string;\n fetch: typeof globalThis.fetch;\n timeoutInMs: number;\n}) {\n const flightKey = `${origin}\\n${refreshToken}`;\n const existingFlight = refreshFlights.get(flightKey);\n if (existingFlight) return existingFlight;\n\n const flight = refreshAccessToken({\n endpoints,\n customerAccountApiClientId,\n refreshToken,\n idToken,\n origin,\n fetch,\n timeoutInMs,\n });\n refreshFlights.set(flightKey, flight);\n\n try {\n return await flight;\n } finally {\n refreshFlights.delete(flightKey);\n }\n}\n\nasync function refreshAccessToken({\n endpoints,\n customerAccountApiClientId,\n refreshToken,\n idToken,\n origin,\n fetch,\n timeoutInMs,\n}: {\n endpoints: CustomerAccountEndpoints;\n customerAccountApiClientId: string;\n refreshToken: string;\n idToken: string | undefined;\n origin: string;\n fetch: typeof globalThis.fetch;\n timeoutInMs: number;\n}): Promise<RefreshResult> {\n const body = new URLSearchParams({\n grant_type: REFRESH_TOKEN_GRANT_TYPE,\n client_id: customerAccountApiClientId,\n refresh_token: refreshToken,\n });\n\n try {\n return await withTokenRequestTimeout(timeoutInMs, async (signal) => {\n const response = await postTokenRequest({\n url: endpoints.tokenUrl,\n origin,\n body,\n fetch,\n signal,\n });\n return readRefreshResult(response, refreshToken, idToken, signal);\n });\n } catch {\n return { type: \"transient\" };\n }\n}\n\nasync function readRefreshResult(\n response: Response,\n currentRefreshToken: string,\n currentIdToken: string | undefined,\n signal: AbortSignal,\n): Promise<RefreshResult> {\n if (response.status === 400 || response.status === 401) {\n cancelResponseBody(response);\n return { type: \"invalid\" };\n }\n\n if (!response.ok) {\n cancelResponseBody(response);\n return { type: \"transient\" };\n }\n\n try {\n const tokenResponse = await readTokenResponseJson(response, signal);\n const parsedTokenResponse = parseRefreshTokenResponse(tokenResponse);\n return {\n type: \"success\",\n tokens: createTokensFromResponse({\n ...parsedTokenResponse,\n refresh_token: parsedTokenResponse.refresh_token ?? currentRefreshToken,\n id_token: parsedTokenResponse.id_token ?? currentIdToken,\n }),\n };\n } catch {\n return { type: \"transient\" };\n }\n}\n\nasync function postTokenRequest({\n url,\n origin,\n body,\n fetch,\n signal,\n}: TokenRequestParams): Promise<Response> {\n return await withAbort(\n fetch(url, {\n method: \"POST\",\n headers: new Headers({\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"User-Agent\": USER_AGENT,\n Origin: origin,\n }),\n body,\n cache: \"no-store\",\n redirect: \"manual\",\n signal,\n }),\n signal,\n );\n}\n\nasync function withTokenRequestTimeout<T>(\n timeoutInMs: number,\n operation: (signal: AbortSignal) => Promise<T>,\n): Promise<T> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort(\n new CustomerAccountApiError(\n `Customer Account OAuth request timed out after ${timeoutInMs}ms`,\n ),\n );\n }, timeoutInMs);\n\n try {\n return await operation(controller.signal);\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\nfunction withAbort<T>(promise: T | Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(getAbortReason(signal));\n\n return new Promise<T>((resolve, reject) => {\n const abort = () => {\n signal.removeEventListener(\"abort\", abort);\n reject(getAbortReason(signal));\n };\n signal.addEventListener(\"abort\", abort, { once: true });\n Promise.resolve(promise).then(\n (value) => {\n signal.removeEventListener(\"abort\", abort);\n resolve(value);\n },\n (error) => {\n signal.removeEventListener(\"abort\", abort);\n reject(error);\n },\n );\n });\n}\n\nfunction getAbortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new DOMException(\"signal is aborted without reason\", \"AbortError\");\n}\n\nasync function readTokenResponseJson(\n response: Response,\n signal: AbortSignal,\n): Promise<TokenEndpointResponse> {\n const json = await withAbort(response.json(), signal);\n if (!isObjectRecord(json)) {\n throw new CustomerAccountOAuthError(\n \"invalid_token_response\",\n \"Customer Account OAuth response must be an object\",\n );\n }\n return {\n access_token: json.access_token,\n refresh_token: json.refresh_token,\n id_token: json.id_token,\n expires_in: json.expires_in,\n };\n}\n\nfunction parseRequiredTokenResponse(\n response: TokenEndpointResponse,\n): Required<ParsedTokenEndpointResponse> {\n const parsed = parseRefreshTokenResponse(response);\n if (typeof response.refresh_token !== \"string\" || response.refresh_token === \"\") {\n throw new CustomerAccountOAuthError(\n \"invalid_token_response\",\n \"Customer Account OAuth response is missing refresh_token\",\n );\n }\n\n if (typeof response.id_token !== \"string\" || response.id_token === \"\") {\n throw new CustomerAccountOAuthError(\n \"invalid_token_response\",\n \"Customer Account OAuth response is missing id_token\",\n );\n }\n return { ...parsed, refresh_token: response.refresh_token, id_token: response.id_token };\n}\n\nfunction parseRefreshTokenResponse(response: TokenEndpointResponse): ParsedTokenEndpointResponse {\n if (typeof response.access_token !== \"string\" || response.access_token === \"\") {\n throw new CustomerAccountOAuthError(\n \"invalid_token_response\",\n \"Customer Account OAuth response is missing access_token\",\n );\n }\n\n if (\n typeof response.expires_in !== \"number\" ||\n !Number.isFinite(response.expires_in) ||\n response.expires_in <= 0\n ) {\n throw new CustomerAccountOAuthError(\n \"invalid_token_response\",\n \"Customer Account OAuth response is missing expires_in\",\n );\n }\n\n return {\n access_token: response.access_token,\n refresh_token:\n typeof response.refresh_token === \"string\" && response.refresh_token !== \"\"\n ? response.refresh_token\n : undefined,\n id_token:\n typeof response.id_token === \"string\" && response.id_token !== \"\"\n ? response.id_token\n : undefined,\n expires_in: response.expires_in,\n };\n}\n\nfunction createTokensFromResponse(response: ParsedTokenEndpointResponse): CustomerAccountTokens {\n return {\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n idToken: response.id_token,\n expiresAt:\n Date.now() + (response.expires_in - EXPIRY_BUFFER_IN_SECONDS) * MILLISECONDS_PER_SECOND,\n };\n}\n\nfunction validateIdTokenClaims(\n idToken: string,\n expected: { audience: string; issuer: string; nonce: string },\n): void {\n const claims = parseJwtPayload(idToken);\n if (claims.nonce !== expected.nonce) {\n throw new CustomerAccountOAuthError(\n \"nonce_mismatch\",\n \"OAuth id_token nonce does not match session nonce\",\n );\n }\n\n if (claims.iss !== expected.issuer) {\n throw new CustomerAccountOAuthError(\n \"issuer_mismatch\",\n \"OAuth id_token issuer does not match Customer Account issuer\",\n );\n }\n\n if (!hasAudienceClaim(claims.aud, expected.audience)) {\n throw new CustomerAccountOAuthError(\n \"audience_mismatch\",\n \"OAuth id_token audience does not match Customer Account client ID\",\n );\n }\n\n if (\n typeof claims.exp !== \"number\" ||\n claims.exp <= Math.floor(Date.now() / MILLISECONDS_PER_SECOND)\n ) {\n throw new CustomerAccountOAuthError(\"expired_id_token\", \"OAuth id_token is expired\");\n }\n}\n\nfunction parseJwtPayload(idToken: string): Record<string, unknown> {\n try {\n const [, payload] = idToken.split(\".\");\n if (!payload) throw new Error(\"Missing JWT payload\");\n\n const decodedPayload = JSON.parse(base64UrlDecode(payload));\n if (!isObjectRecord(decodedPayload)) throw new Error(\"JWT payload must be an object\");\n\n return decodedPayload;\n } catch (cause) {\n throw new CustomerAccountOAuthError(\n \"invalid_id_token\",\n \"Customer Account OAuth id_token is invalid\",\n { cause },\n );\n }\n}\n\nfunction hasAudienceClaim(audience: unknown, expectedAudience: string): boolean {\n if (audience === expectedAudience) return true;\n if (!Array.isArray(audience)) return false;\n return audience.includes(expectedAudience);\n}\n\nasync function readSessionData(\n sessionManager: ReadonlyCustomerSessionManager,\n): Promise<CustomerAccountSessionData> {\n const value = await sessionManager.getSessionItem(CUSTOMER_ACCOUNT_SESSION_KEY);\n return isCustomerAccountSessionData(value) ? value : {};\n}\n\nasync function writeSessionData(\n sessionManager: WritableCustomerSessionManager,\n sessionData: CustomerAccountSessionData,\n): Promise<void> {\n await sessionManager.setSessionItem(CUSTOMER_ACCOUNT_SESSION_KEY, sessionData);\n}\n\nasync function writeTokens(\n sessionManager: WritableCustomerSessionManager,\n tokens: CustomerAccountTokens,\n): Promise<void> {\n const sessionData = await readSessionData(sessionManager);\n await writeSessionData(sessionManager, { ...sessionData, tokens });\n}\n\nasync function clearTokens(sessionManager: WritableCustomerSessionManager): Promise<void> {\n const sessionData = await readSessionData(sessionManager);\n const { pendingLogin } = sessionData;\n if (pendingLogin) {\n await writeSessionData(sessionManager, { pendingLogin });\n return;\n }\n\n await sessionManager.removeSessionItem(CUSTOMER_ACCOUNT_SESSION_KEY);\n}\n\nasync function clearPendingLogin(sessionManager: WritableCustomerSessionManager): Promise<void> {\n const sessionData = await readSessionData(sessionManager);\n const { tokens } = sessionData;\n if (tokens) {\n await writeSessionData(sessionManager, { tokens });\n return;\n }\n\n await sessionManager.removeSessionItem(CUSTOMER_ACCOUNT_SESSION_KEY);\n}\n\nfunction getUsableAccessToken(sessionData: CustomerAccountSessionData): string | undefined {\n const tokens = sessionData.tokens;\n if (!isTokenUsable(tokens)) return undefined;\n return tokens.accessToken;\n}\n\nfunction hasCustomerSession(sessionData: CustomerAccountSessionData): boolean {\n return (\n getUsableAccessToken(sessionData) !== undefined || getRefreshToken(sessionData) !== undefined\n );\n}\n\nfunction getRefreshToken(sessionData: CustomerAccountSessionData): string | undefined {\n const refreshToken = sessionData.tokens?.refreshToken;\n return typeof refreshToken === \"string\" && refreshToken !== \"\" ? refreshToken : undefined;\n}\n\nfunction getPendingLogin(sessionData: CustomerAccountSessionData): Required<PendingLogin> {\n const pendingLogin = sessionData.pendingLogin;\n if (isPendingLoginReady(pendingLogin)) return pendingLogin;\n throw new CustomerAccountOAuthError(\n \"missing_pending_login\",\n \"OAuth callback has no pending login state\",\n );\n}\n\nfunction isTokenUsable(\n tokens: CustomerAccountSessionData[\"tokens\"],\n): tokens is Required<CustomerAccountTokens> {\n if (!tokens || typeof tokens.accessToken !== \"string\" || tokens.accessToken === \"\") return false;\n if (typeof tokens.expiresAt !== \"number\") return false;\n return tokens.expiresAt > Date.now();\n}\n\nfunction isPendingLoginReady(\n pendingLogin: PendingLogin | undefined,\n): pendingLogin is Required<PendingLogin> {\n if (!pendingLogin) return false;\n if (!isNonEmptyString(pendingLogin.state)) return false;\n if (!isNonEmptyString(pendingLogin.nonce)) return false;\n if (!isNonEmptyString(pendingLogin.codeVerifier)) return false;\n if (!isNonEmptyString(pendingLogin.returnTo)) return false;\n if (!isNonEmptyString(pendingLogin.origin)) return false;\n if (typeof pendingLogin.createdAt !== \"number\") return false;\n return Date.now() - pendingLogin.createdAt <= PENDING_LOGIN_TTL_IN_MS;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value !== \"\";\n}\n\nfunction isCustomerAccountSessionData(value: unknown): value is CustomerAccountSessionData {\n if (!isObjectRecord(value) || Array.isArray(value)) return false;\n const tokens = value.tokens;\n const pendingLogin = value.pendingLogin;\n if (tokens !== undefined && (!isObjectRecord(tokens) || Array.isArray(tokens))) return false;\n return (\n pendingLogin === undefined || (isObjectRecord(pendingLogin) && !Array.isArray(pendingLogin))\n );\n}\n\nfunction setOptionalLoginParams(loginUrl: URL, options: PrepareLoginUrlOptions): void {\n if (options.locale) loginUrl.searchParams.set(\"locale\", options.locale);\n if (options.countryCode) loginUrl.searchParams.set(\"region_country\", options.countryCode);\n if (options.acrValues) loginUrl.searchParams.set(\"acr_values\", options.acrValues);\n if (!options.loginHint) return;\n\n loginUrl.searchParams.set(\"login_hint\", options.loginHint);\n if (options.loginHintMode) loginUrl.searchParams.set(\"login_hint_mode\", options.loginHintMode);\n}\n\nfunction sanitizeReturnTo(\n returnTo: string | null | undefined,\n origin: string,\n fallbackReturnTo = DEFAULT_LOGIN_RETURN_TO_PATH,\n): string {\n if (!returnTo) return fallbackReturnTo;\n\n try {\n const url = new URL(returnTo, origin);\n if (url.origin !== origin) return fallbackReturnTo;\n const sanitizedReturnTo = `${url.pathname}${url.search}${url.hash}`;\n if (new TextEncoder().encode(sanitizedReturnTo).byteLength > MAX_RETURN_TO_LENGTH_IN_BYTES) {\n return fallbackReturnTo;\n }\n return sanitizedReturnTo;\n } catch {\n return fallbackReturnTo;\n }\n}\n\nfunction absoluteSameOriginUrl(url: string, origin: string): string {\n const parsedUrl = new URL(url, origin);\n if (parsedUrl.origin !== origin) return origin;\n return parsedUrl.toString();\n}\n\nasync function createCodeChallenge(codeVerifier: string): Promise<string> {\n const digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(codeVerifier));\n return base64UrlEncode(String.fromCharCode(...new Uint8Array(digest)));\n}\n\nfunction generateRandomBase64Url(): string {\n const bytes = new Uint8Array(RANDOM_BYTES_LENGTH);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(String.fromCharCode(...bytes));\n}\n\nfunction base64UrlEncode(value: string): string {\n return btoa(value).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\n\nfunction base64UrlDecode(value: string): string {\n const base64 = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const paddingLength = (4 - (base64.length % 4)) % 4;\n return atob(base64.padEnd(base64.length + paddingLength, \"=\"));\n}\n\ntype CustomerAccountEndpoints = {\n authorizeUrl: string;\n tokenUrl: string;\n logoutUrl: string;\n issuer: string;\n};\n\nfunction createCustomerAccountEndpoints(\n shopId: string,\n customerAccountApiUrl: string | undefined,\n): CustomerAccountEndpoints {\n const authBaseUrl = normalizeAuthBaseUrl(\n customerAccountApiUrl ?? `https://shopify.com/authentication/${shopId}`,\n );\n return {\n authorizeUrl: `${authBaseUrl}/oauth/authorize`,\n tokenUrl: `${authBaseUrl}/oauth/token`,\n logoutUrl: `${authBaseUrl}/logout`,\n issuer: authBaseUrl,\n };\n}\n\nfunction normalizeAuthBaseUrl(customerAccountApiUrl: string): string {\n const url = new URL(customerAccountApiUrl);\n if (url.protocol !== \"https:\") {\n throw new Error(\"customerAccountApiUrl must use HTTPS\");\n }\n return url.toString().replace(/\\/$/, \"\");\n}\n\nfunction getOriginFromRequest(request: Request): string {\n return normalizeOrigin(new URL(request.url).origin);\n}\n\nfunction normalizeOrigin(origin: string): string {\n const url = new URL(origin);\n if (url.protocol === \"https:\") return url.origin;\n throw new Error(\n \"Customer Account OAuth origin must use HTTPS. Use a public HTTPS tunnel for local Customer Account login.\",\n );\n}\n\nfunction cancelResponseBody(response: Response): void {\n try {\n void response.body?.cancel().catch(() => undefined);\n } catch {}\n}\n\nfunction validateShopId(shopId: string): void {\n if (!SHOP_ID_RE.test(shopId)) {\n throw new Error(\"shopId must be a numeric Shopify shop ID string\");\n }\n}\n\nfunction validateCustomerAccountApiClientId(customerAccountApiClientId: string): void {\n if (typeof customerAccountApiClientId !== \"string\" || customerAccountApiClientId.trim() === \"\") {\n throw new Error(\"customerAccountApiClientId is required\");\n }\n}\n\nfunction validateTimeout(timeoutInMs: number): void {\n if (\n !Number.isSafeInteger(timeoutInMs) ||\n timeoutInMs <= 0 ||\n timeoutInMs > MAX_SET_TIMEOUT_IN_MS\n ) {\n throw new Error(\n `defaultTimeoutInMs must be a positive safe integer no greater than ${MAX_SET_TIMEOUT_IN_MS}`,\n );\n }\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":";;;;AAoBA,MAAM,MAAM,UAAU,mBAAmB;AAEzC,MAAa,kCAAkC,uBAAuB;AACtE,MAAa,8BAA8B,uBAAuB;AAClE,MAAa,+BAA+B,uBAAuB;AACnE,MAAa,gCAAgC,uBAAuB;AAiBpE,MAAM,+BAA+B;AACrC,MAAM,qBAAqB;AAC3B,MAAM,0BAA0B;AAChC,MAAM,0BACJ,+BAA+B,qBAAqB;AAuNtD,MAAM,2CAA2B,IAAI,SAAoD;AA8NzF,eAAsB,gCACpB,iBACA,gBACA,gBACA,UAAgC,EAAE,EACO;AACzC,QAAO,yBACJ,IAAI,gBAAgB,EACnB,wBAAwB,gBAAgB,gBAAgB,QAAQ"}
@@ -0,0 +1,64 @@
1
+ import { I18nConfig } from "./core/request-context.mjs";
2
+ import { ShopifyStandardRouteMatch } from "./core/standard-routes/types.mjs";
3
+ import { ShopifyStandardActions } from "./vendor/standard-actions.mjs";
4
+ import { StorefrontAnalytics } from "./core/analytics/types.mjs";
5
+
6
+ //#region src/globals.d.ts
7
+ type ShopifyGlobal = {
8
+ actions: ShopifyStandardActions;
9
+ analytics?: StorefrontAnalytics;
10
+ components: {
11
+ config: {
12
+ storeDomain: string;
13
+ publicAccessToken?: string;
14
+ apiVersion?: string;
15
+ country?: string;
16
+ language?: string;
17
+ };
18
+ };
19
+ country: I18nConfig["country"] | string;
20
+ currency?: {
21
+ active: string;
22
+ };
23
+ customerPrivacy: {
24
+ config?: {
25
+ isHeadless?: boolean;
26
+ consentDomain?: string;
27
+ };
28
+ consentStatus?: "pending" | "loaded";
29
+ currentVisitorConsent: () => Record<string, unknown>;
30
+ preferencesProcessingAllowed: () => boolean;
31
+ saleOfDataAllowed: () => boolean;
32
+ marketingAllowed: () => boolean;
33
+ analyticsProcessingAllowed: () => boolean;
34
+ setTrackingConsent: (consent: Record<string, unknown>, callback: (data: {
35
+ error: string;
36
+ } | undefined) => void) => void | Promise<unknown>;
37
+ shouldShowBanner: () => boolean;
38
+ };
39
+ locale: Lowercase<I18nConfig["language"]> | string; /** @deprecated Use `Shopify.routes.navigate` instead. */
40
+ navigate?: (url: string) => void | Promise<void>;
41
+ routes: {
42
+ root: string; /** @internal */
43
+ apiProxyPrefix?: string; /** @internal */
44
+ match?: (url: string) => ShopifyStandardRouteMatch | null; /** @internal */
45
+ resolve?: (url: string) => string; /** @internal */
46
+ navigate?: (url: string) => void | Promise<void>;
47
+ [key: string]: unknown;
48
+ }; /** The shop's permanent `*.myshopify.com` domain. */
49
+ shop: string;
50
+ [key: string]: unknown;
51
+ };
52
+ type ShopifyPrivacyBanner = {
53
+ showPreferences: () => Promise<void>;
54
+ showBanner: () => Promise<void>;
55
+ };
56
+ declare global {
57
+ interface Window {
58
+ privacyBanner?: ShopifyPrivacyBanner;
59
+ Shopify?: ShopifyGlobal;
60
+ }
61
+ }
62
+ //#endregion
63
+ export { ShopifyGlobal };
64
+ //# sourceMappingURL=globals.d.mts.map