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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) hide show
  1. package/README.md +34 -3
  2. package/dist/cli/index.mjs +578 -85
  3. package/dist/client/client.mjs +2 -2
  4. package/dist/client/errors.d.mts +19 -19
  5. package/dist/client/errors.mjs +19 -16
  6. package/dist/client/errors.mjs.map +1 -1
  7. package/dist/client/types.d.mts +91 -10
  8. package/dist/core/analytics/cart-tracker.d.mts +14 -0
  9. package/dist/core/analytics/cart-tracker.mjs +15 -1
  10. package/dist/core/analytics/cart-tracker.mjs.map +1 -1
  11. package/dist/core/analytics/custom-consent.mjs +12 -0
  12. package/dist/core/analytics/custom-consent.mjs.map +1 -0
  13. package/dist/core/analytics/events.d.mts +7 -0
  14. package/dist/core/analytics/events.mjs +7 -0
  15. package/dist/core/analytics/events.mjs.map +1 -1
  16. package/dist/core/analytics/index.d.mts +1 -1
  17. package/dist/core/analytics/types.d.mts +152 -16
  18. package/dist/core/cart/actions.d.mts +47 -3
  19. package/dist/core/cart/actions.mjs +29 -0
  20. package/dist/core/cart/actions.mjs.map +1 -1
  21. package/dist/core/cart/attach-quantity-input.d.mts +26 -0
  22. package/dist/core/cart/attach-quantity-input.mjs +26 -0
  23. package/dist/core/cart/attach-quantity-input.mjs.map +1 -1
  24. package/dist/core/cart/cart.d.mts +106 -6
  25. package/dist/core/cart/cart.mjs +57 -6
  26. package/dist/core/cart/cart.mjs.map +1 -1
  27. package/dist/core/cart/cookie.d.mts +15 -0
  28. package/dist/core/cart/cookie.mjs +15 -0
  29. package/dist/core/cart/cookie.mjs.map +1 -1
  30. package/dist/core/cart/form.d.mts +61 -0
  31. package/dist/core/cart/form.mjs +18 -0
  32. package/dist/core/cart/form.mjs.map +1 -1
  33. package/dist/core/cart/get-cart.d.mts +43 -2
  34. package/dist/core/cart/get-cart.mjs +33 -0
  35. package/dist/core/cart/get-cart.mjs.map +1 -1
  36. package/dist/core/cart/index.d.mts +1 -1
  37. package/dist/core/cart/quantity.d.mts +17 -0
  38. package/dist/core/cart/quantity.mjs +17 -0
  39. package/dist/core/cart/quantity.mjs.map +1 -1
  40. package/dist/core/cart/queries.d.mts +402 -369
  41. package/dist/core/cart/queries.mjs +2 -1
  42. package/dist/core/cart/queries.mjs.map +1 -1
  43. package/dist/core/cart/server-handlers.d.mts +69 -0
  44. package/dist/core/cart/server-handlers.mjs +2 -10
  45. package/dist/core/cart/server-handlers.mjs.map +1 -1
  46. package/dist/core/cart/state.d.mts +133 -0
  47. package/dist/core/cart/state.mjs +8 -0
  48. package/dist/core/cart/state.mjs.map +1 -1
  49. package/dist/core/collection/collection.d.mts +18 -2
  50. package/dist/core/collection/collection.mjs +11 -0
  51. package/dist/core/collection/collection.mjs.map +1 -1
  52. package/dist/core/collection/index.d.mts +1 -1
  53. package/dist/core/collection/reconciler.d.mts +13 -4
  54. package/dist/core/collection/reconciler.mjs.map +1 -1
  55. package/dist/core/collection/state.d.mts +7 -2
  56. package/dist/core/collection/state.mjs.map +1 -1
  57. package/dist/core/collection/url.d.mts +3 -0
  58. package/dist/core/collection/url.mjs.map +1 -1
  59. package/dist/core/constants.d.mts +1 -1
  60. package/dist/core/constants.mjs +2 -2
  61. package/dist/core/constants.mjs.map +1 -1
  62. package/dist/core/headers.mjs +2 -2
  63. package/dist/core/headers.mjs.map +1 -1
  64. package/dist/core/index.d.mts +5 -5
  65. package/dist/core/logging/logging.mjs.map +1 -1
  66. package/dist/core/money/format.mjs +6 -3
  67. package/dist/core/money/format.mjs.map +1 -1
  68. package/dist/core/observable.mjs +0 -3
  69. package/dist/core/observable.mjs.map +1 -1
  70. package/dist/core/predictive-search/form.d.mts +1 -0
  71. package/dist/core/predictive-search/form.mjs +1 -0
  72. package/dist/core/predictive-search/form.mjs.map +1 -1
  73. package/dist/core/predictive-search/index.d.mts +1 -1
  74. package/dist/core/predictive-search/queries.d.mts +40 -4
  75. package/dist/core/predictive-search/queries.mjs +7 -1
  76. package/dist/core/predictive-search/queries.mjs.map +1 -1
  77. package/dist/core/predictive-search/search.d.mts +43 -10
  78. package/dist/core/predictive-search/search.mjs +34 -0
  79. package/dist/core/predictive-search/search.mjs.map +1 -1
  80. package/dist/core/predictive-search/server-handlers.d.mts +31 -5
  81. package/dist/core/predictive-search/server-handlers.mjs +5 -3
  82. package/dist/core/predictive-search/server-handlers.mjs.map +1 -1
  83. package/dist/core/predictive-search/store.d.mts +3 -1
  84. package/dist/core/predictive-search/store.mjs.map +1 -1
  85. package/dist/core/predictive-search/url.d.mts +50 -8
  86. package/dist/core/predictive-search/url.mjs +9 -0
  87. package/dist/core/predictive-search/url.mjs.map +1 -1
  88. package/dist/core/product/form.d.mts +21 -2
  89. package/dist/core/product/form.mjs +13 -0
  90. package/dist/core/product/form.mjs.map +1 -1
  91. package/dist/core/product/index.d.mts +1 -1
  92. package/dist/core/product/options.d.mts +2 -2
  93. package/dist/core/product/options.mjs +2 -2
  94. package/dist/core/product/options.mjs.map +1 -1
  95. package/dist/core/product/product-form.d.mts +33 -1
  96. package/dist/core/product/product-form.mjs +30 -0
  97. package/dist/core/product/product-form.mjs.map +1 -1
  98. package/dist/core/product/state.d.mts +41 -0
  99. package/dist/core/product/url.d.mts +6 -0
  100. package/dist/core/product/url.mjs.map +1 -1
  101. package/dist/core/request-context.d.mts +8 -5
  102. package/dist/core/request-context.mjs +17 -39
  103. package/dist/core/request-context.mjs.map +1 -1
  104. package/dist/core/request-routing/handle-shopify-routes.mjs +4 -1
  105. package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  106. package/dist/core/request-routing/interceptors/ajax-api.mjs +1 -1
  107. package/dist/core/request-routing/interceptors/api-proxy.mjs +1 -1
  108. package/dist/core/request-routing/interceptors/checkout.mjs +22 -6
  109. package/dist/core/request-routing/interceptors/checkout.mjs.map +1 -1
  110. package/dist/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
  111. package/dist/core/request-routing/interceptors/proxy.mjs +36 -10
  112. package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
  113. package/dist/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
  114. package/dist/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
  115. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
  116. package/dist/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
  117. package/dist/core/request-routing/interceptors/ucp-mcp-proxy.mjs +35 -0
  118. package/dist/core/request-routing/interceptors/ucp-mcp-proxy.mjs.map +1 -0
  119. package/dist/core/request-routing/interceptors/well-known.mjs +1 -1
  120. package/dist/core/shopify-scripts/analytics.mjs +2 -2
  121. package/dist/core/shopify-scripts/analytics.mjs.map +1 -1
  122. package/dist/core/shopify-scripts/constants.mjs +4 -2
  123. package/dist/core/shopify-scripts/constants.mjs.map +1 -1
  124. package/dist/core/shopify-scripts/global.mjs +9 -4
  125. package/dist/core/shopify-scripts/global.mjs.map +1 -1
  126. package/dist/core/shopify-scripts/index.d.mts +1 -0
  127. package/dist/core/shopify-scripts/index.mjs +14 -12
  128. package/dist/core/shopify-scripts/index.mjs.map +1 -1
  129. package/dist/core/shopify-scripts/initialize.d.mts +1 -0
  130. package/dist/core/shopify-scripts/initialize.mjs +10 -3
  131. package/dist/core/shopify-scripts/initialize.mjs.map +1 -1
  132. package/dist/core/shopify-scripts/types.d.mts +6 -0
  133. package/dist/core/url.mjs +21 -2
  134. package/dist/core/url.mjs.map +1 -1
  135. package/dist/customer-account/client.d.mts +128 -4
  136. package/dist/customer-account/client.mjs +27 -1
  137. package/dist/customer-account/client.mjs.map +1 -1
  138. package/dist/customer-account/errors.d.mts +46 -0
  139. package/dist/customer-account/errors.mjs +46 -0
  140. package/dist/customer-account/errors.mjs.map +1 -1
  141. package/dist/customer-account/graphql.d.mts +68 -0
  142. package/dist/customer-account/graphql.mjs +47 -0
  143. package/dist/customer-account/graphql.mjs.map +1 -1
  144. package/dist/customer-account/session.d.mts +114 -23
  145. package/dist/customer-account/session.mjs +27 -12
  146. package/dist/customer-account/session.mjs.map +1 -1
  147. package/dist/customer-account/type-resolver.d.mts +23 -0
  148. package/dist/customer-account-api-types.d.ts +570 -374
  149. package/dist/customer-account.schema.json +2981 -1527
  150. package/dist/development/client/client.mjs +2 -2
  151. package/dist/development/client/errors.d.mts +19 -19
  152. package/dist/development/client/errors.mjs +20 -20
  153. package/dist/development/client/errors.mjs.map +1 -1
  154. package/dist/development/client/types.d.mts +91 -10
  155. package/dist/development/core/analytics/cart-tracker.d.mts +14 -0
  156. package/dist/development/core/analytics/cart-tracker.mjs +15 -1
  157. package/dist/development/core/analytics/cart-tracker.mjs.map +1 -1
  158. package/dist/development/core/analytics/custom-consent.mjs +12 -0
  159. package/dist/development/core/analytics/custom-consent.mjs.map +1 -0
  160. package/dist/development/core/analytics/events.d.mts +7 -0
  161. package/dist/development/core/analytics/events.mjs +7 -0
  162. package/dist/development/core/analytics/events.mjs.map +1 -1
  163. package/dist/development/core/analytics/index.d.mts +1 -1
  164. package/dist/development/core/analytics/types.d.mts +152 -16
  165. package/dist/development/core/cart/actions.d.mts +47 -3
  166. package/dist/development/core/cart/actions.mjs +29 -0
  167. package/dist/development/core/cart/actions.mjs.map +1 -1
  168. package/dist/development/core/cart/attach-quantity-input.d.mts +26 -0
  169. package/dist/development/core/cart/attach-quantity-input.mjs +26 -0
  170. package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -1
  171. package/dist/development/core/cart/cart.d.mts +106 -6
  172. package/dist/development/core/cart/cart.mjs +57 -6
  173. package/dist/development/core/cart/cart.mjs.map +1 -1
  174. package/dist/development/core/cart/cookie.d.mts +15 -0
  175. package/dist/development/core/cart/cookie.mjs +15 -0
  176. package/dist/development/core/cart/cookie.mjs.map +1 -1
  177. package/dist/development/core/cart/form.d.mts +61 -0
  178. package/dist/development/core/cart/form.mjs +18 -0
  179. package/dist/development/core/cart/form.mjs.map +1 -1
  180. package/dist/development/core/cart/get-cart.d.mts +43 -2
  181. package/dist/development/core/cart/get-cart.mjs +33 -0
  182. package/dist/development/core/cart/get-cart.mjs.map +1 -1
  183. package/dist/development/core/cart/index.d.mts +1 -1
  184. package/dist/development/core/cart/quantity.d.mts +17 -0
  185. package/dist/development/core/cart/quantity.mjs +17 -0
  186. package/dist/development/core/cart/quantity.mjs.map +1 -1
  187. package/dist/development/core/cart/queries.d.mts +345 -312
  188. package/dist/development/core/cart/queries.mjs +2 -1
  189. package/dist/development/core/cart/queries.mjs.map +1 -1
  190. package/dist/development/core/cart/server-handlers.d.mts +69 -0
  191. package/dist/development/core/cart/server-handlers.mjs +2 -10
  192. package/dist/development/core/cart/server-handlers.mjs.map +1 -1
  193. package/dist/development/core/cart/state.d.mts +133 -0
  194. package/dist/development/core/cart/state.mjs +8 -0
  195. package/dist/development/core/cart/state.mjs.map +1 -1
  196. package/dist/development/core/collection/collection.d.mts +18 -2
  197. package/dist/development/core/collection/collection.mjs +11 -0
  198. package/dist/development/core/collection/collection.mjs.map +1 -1
  199. package/dist/development/core/collection/index.d.mts +1 -1
  200. package/dist/development/core/collection/reconciler.d.mts +13 -4
  201. package/dist/development/core/collection/reconciler.mjs.map +1 -1
  202. package/dist/development/core/collection/state.d.mts +7 -2
  203. package/dist/development/core/collection/state.mjs.map +1 -1
  204. package/dist/development/core/collection/url.d.mts +3 -0
  205. package/dist/development/core/collection/url.mjs.map +1 -1
  206. package/dist/development/core/constants.mjs +1 -1
  207. package/dist/development/core/constants.mjs.map +1 -1
  208. package/dist/development/core/development.d.mts +5 -5
  209. package/dist/development/core/headers.mjs +2 -2
  210. package/dist/development/core/headers.mjs.map +1 -1
  211. package/dist/development/core/index.d.mts +4 -4
  212. package/dist/development/core/logging/logging.mjs.map +1 -1
  213. package/dist/development/core/money/format.mjs +6 -3
  214. package/dist/development/core/money/format.mjs.map +1 -1
  215. package/dist/development/core/observable.mjs +0 -3
  216. package/dist/development/core/observable.mjs.map +1 -1
  217. package/dist/development/core/predictive-search/form.d.mts +1 -0
  218. package/dist/development/core/predictive-search/form.mjs +1 -0
  219. package/dist/development/core/predictive-search/form.mjs.map +1 -1
  220. package/dist/development/core/predictive-search/index.d.mts +1 -1
  221. package/dist/development/core/predictive-search/queries.d.mts +38 -2
  222. package/dist/development/core/predictive-search/queries.mjs +7 -1
  223. package/dist/development/core/predictive-search/queries.mjs.map +1 -1
  224. package/dist/development/core/predictive-search/search.d.mts +43 -10
  225. package/dist/development/core/predictive-search/search.mjs +34 -0
  226. package/dist/development/core/predictive-search/search.mjs.map +1 -1
  227. package/dist/development/core/predictive-search/server-handlers.d.mts +31 -5
  228. package/dist/development/core/predictive-search/server-handlers.mjs +5 -3
  229. package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -1
  230. package/dist/development/core/predictive-search/store.d.mts +3 -1
  231. package/dist/development/core/predictive-search/store.mjs.map +1 -1
  232. package/dist/development/core/predictive-search/url.d.mts +50 -8
  233. package/dist/development/core/predictive-search/url.mjs +9 -0
  234. package/dist/development/core/predictive-search/url.mjs.map +1 -1
  235. package/dist/development/core/product/form.d.mts +21 -2
  236. package/dist/development/core/product/form.mjs +13 -0
  237. package/dist/development/core/product/form.mjs.map +1 -1
  238. package/dist/development/core/product/index.d.mts +1 -1
  239. package/dist/development/core/product/options.d.mts +2 -2
  240. package/dist/development/core/product/options.mjs +2 -2
  241. package/dist/development/core/product/options.mjs.map +1 -1
  242. package/dist/development/core/product/product-form.d.mts +33 -1
  243. package/dist/development/core/product/product-form.mjs +30 -0
  244. package/dist/development/core/product/product-form.mjs.map +1 -1
  245. package/dist/development/core/product/state.d.mts +41 -0
  246. package/dist/development/core/product/url.d.mts +6 -0
  247. package/dist/development/core/product/url.mjs.map +1 -1
  248. package/dist/development/core/request-context.d.mts +8 -5
  249. package/dist/development/core/request-context.mjs +17 -39
  250. package/dist/development/core/request-context.mjs.map +1 -1
  251. package/dist/development/core/request-routing/handle-shopify-routes.mjs +4 -1
  252. package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -1
  253. package/dist/development/core/request-routing/interceptors/ajax-api.mjs +1 -1
  254. package/dist/development/core/request-routing/interceptors/api-proxy.mjs +1 -1
  255. package/dist/development/core/request-routing/interceptors/checkout.mjs +22 -6
  256. package/dist/development/core/request-routing/interceptors/checkout.mjs.map +1 -1
  257. package/dist/development/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
  258. package/dist/development/core/request-routing/interceptors/proxy.mjs +36 -10
  259. package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
  260. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
  261. package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
  262. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
  263. package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
  264. package/dist/development/core/request-routing/interceptors/ucp-mcp-proxy.mjs +35 -0
  265. package/dist/development/core/request-routing/interceptors/ucp-mcp-proxy.mjs.map +1 -0
  266. package/dist/development/core/request-routing/interceptors/well-known.mjs +1 -1
  267. package/dist/development/core/shopify-scripts/analytics.mjs +2 -2
  268. package/dist/development/core/shopify-scripts/analytics.mjs.map +1 -1
  269. package/dist/development/core/shopify-scripts/constants.mjs +4 -2
  270. package/dist/development/core/shopify-scripts/constants.mjs.map +1 -1
  271. package/dist/development/core/shopify-scripts/global.mjs +9 -4
  272. package/dist/development/core/shopify-scripts/global.mjs.map +1 -1
  273. package/dist/development/core/shopify-scripts/index.d.mts +1 -0
  274. package/dist/development/core/shopify-scripts/index.mjs +14 -12
  275. package/dist/development/core/shopify-scripts/index.mjs.map +1 -1
  276. package/dist/development/core/shopify-scripts/initialize.d.mts +1 -0
  277. package/dist/development/core/shopify-scripts/initialize.mjs +10 -3
  278. package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -1
  279. package/dist/development/core/shopify-scripts/types.d.mts +6 -0
  280. package/dist/development/core/url.mjs +21 -2
  281. package/dist/development/core/url.mjs.map +1 -1
  282. package/dist/development/customer-account/session.d.mts +69 -13
  283. package/dist/development/customer-account/session.mjs +4 -0
  284. package/dist/development/customer-account/session.mjs.map +1 -1
  285. package/dist/development/globals.d.mts +6 -5
  286. package/dist/development/graphql/generated/graphql-env.d.mts +459 -16
  287. package/dist/development/graphql/generated/storefront-api-types.d.mts +1 -1
  288. package/dist/development/graphql/graphql.d.mts +33 -0
  289. package/dist/development/graphql/graphql.mjs +30 -0
  290. package/dist/development/graphql/graphql.mjs.map +1 -1
  291. package/dist/development/graphql/type-resolver.d.mts +14 -0
  292. package/dist/development/react/cart.d.mts +85 -14
  293. package/dist/development/react/cart.mjs +123 -3
  294. package/dist/development/react/cart.mjs.map +1 -1
  295. package/dist/development/react/collection.d.mts +2 -4
  296. package/dist/development/react/collection.mjs.map +1 -1
  297. package/dist/development/react/index.d.mts +8 -6
  298. package/dist/development/react/index.mjs +2 -2
  299. package/dist/development/react/predictive-search.d.mts +68 -7
  300. package/dist/development/react/predictive-search.mjs +54 -12
  301. package/dist/development/react/predictive-search.mjs.map +1 -1
  302. package/dist/development/react/product.d.mts +1 -0
  303. package/dist/development/react/product.mjs +5 -2
  304. package/dist/development/react/product.mjs.map +1 -1
  305. package/dist/development/react/shopify-scripts.mjs +1 -0
  306. package/dist/development/react/shopify-scripts.mjs.map +1 -1
  307. package/dist/development/vue/cart.d.mts +85 -6
  308. package/dist/development/vue/cart.mjs +95 -0
  309. package/dist/development/vue/cart.mjs.map +1 -1
  310. package/dist/development/vue/collection.d.mts +83 -5
  311. package/dist/development/vue/collection.mjs +52 -0
  312. package/dist/development/vue/collection.mjs.map +1 -1
  313. package/dist/development/vue/index.d.mts +6 -4
  314. package/dist/development/vue/predictive-search.d.mts +53 -6
  315. package/dist/development/vue/predictive-search.mjs +33 -0
  316. package/dist/development/vue/predictive-search.mjs.map +1 -1
  317. package/dist/development/vue/product.d.mts +6 -2
  318. package/dist/development/vue/product.mjs.map +1 -1
  319. package/dist/development/vue/shopify-scripts.d.mts +30 -3
  320. package/dist/development/vue/shopify-scripts.mjs +6 -0
  321. package/dist/development/vue/shopify-scripts.mjs.map +1 -1
  322. package/dist/globals.d.mts +6 -5
  323. package/dist/graphql/generated/customer-account-graphql-env.d.mts +1717 -1090
  324. package/dist/graphql/generated/graphql-env.d.mts +459 -16
  325. package/dist/graphql/generated/storefront-api-types.d.mts +1 -1
  326. package/dist/graphql/graphql.d.mts +33 -0
  327. package/dist/graphql/graphql.mjs +30 -0
  328. package/dist/graphql/graphql.mjs.map +1 -1
  329. package/dist/graphql/type-resolver.d.mts +14 -0
  330. package/dist/react/cart.d.mts +85 -14
  331. package/dist/react/cart.mjs +123 -3
  332. package/dist/react/cart.mjs.map +1 -1
  333. package/dist/react/collection.d.mts +2 -4
  334. package/dist/react/collection.mjs.map +1 -1
  335. package/dist/react/index.d.mts +8 -6
  336. package/dist/react/index.mjs +2 -2
  337. package/dist/react/predictive-search.d.mts +68 -7
  338. package/dist/react/predictive-search.mjs +54 -12
  339. package/dist/react/predictive-search.mjs.map +1 -1
  340. package/dist/react/product.d.mts +1 -0
  341. package/dist/react/product.mjs +5 -2
  342. package/dist/react/product.mjs.map +1 -1
  343. package/dist/react/shopify-scripts.mjs +1 -0
  344. package/dist/react/shopify-scripts.mjs.map +1 -1
  345. package/dist/storefront-api-types.d.ts +165 -20
  346. package/dist/storefront.schema.json +782 -57
  347. package/dist/vite/customer-account.mjs +191 -0
  348. package/dist/vite/customer-account.mjs.map +1 -0
  349. package/dist/vite/local-https.mjs +13 -29
  350. package/dist/vite/local-https.mjs.map +1 -1
  351. package/dist/vite/mkcert.mjs +1 -1
  352. package/dist/vue/cart.d.mts +85 -6
  353. package/dist/vue/cart.mjs +95 -0
  354. package/dist/vue/cart.mjs.map +1 -1
  355. package/dist/vue/collection.d.mts +83 -5
  356. package/dist/vue/collection.mjs +52 -0
  357. package/dist/vue/collection.mjs.map +1 -1
  358. package/dist/vue/index.d.mts +6 -4
  359. package/dist/vue/predictive-search.d.mts +53 -6
  360. package/dist/vue/predictive-search.mjs +33 -0
  361. package/dist/vue/predictive-search.mjs.map +1 -1
  362. package/dist/vue/product.d.mts +6 -2
  363. package/dist/vue/product.mjs.map +1 -1
  364. package/dist/vue/shopify-scripts.d.mts +31 -4
  365. package/dist/vue/shopify-scripts.mjs +6 -0
  366. package/dist/vue/shopify-scripts.mjs.map +1 -1
  367. package/package.json +5 -2
  368. package/skills/hydrogen-analytics/SKILL.md +6 -5
  369. package/skills/hydrogen-analytics/references/custom-consent.md +52 -0
  370. package/skills/hydrogen-analytics/references/react.md +1 -1
  371. package/skills/hydrogen-cart-metafields/SKILL.md +133 -0
  372. package/skills/hydrogen-cart-ui/SKILL.md +2 -1
  373. package/skills/hydrogen-customer-account/SKILL.md +3 -3
  374. package/skills/hydrogen-local-https/SKILL.md +22 -7
  375. package/skills/hydrogen-request-handlers/SKILL.md +5 -5
  376. package/skills/hydrogen-request-handlers/references/nuxt.md +0 -6
  377. package/skills/hydrogen-setup/references/analytics.md +24 -23
  378. package/skills/hydrogen-setup/steps/1-inspect-app.md +1 -1
  379. package/skills/hydrogen-setup/steps/11-verify.md +1 -0
  380. package/skills/hydrogen-setup/steps/2-scaffold.md +22 -1
  381. package/skills/hydrogen-smoke-test/SKILL.md +2 -2
  382. package/skills/hydrogen-storefront-client/SKILL.md +4 -2
  383. package/skills/hydrogen-variant-form/SKILL.md +10 -4
  384. package/ts-plugin/package.json +5 -0
  385. package/dist/core/shopify-scripts/consent.mjs +0 -17
  386. package/dist/core/shopify-scripts/consent.mjs.map +0 -1
  387. package/dist/core/shopify-scripts/deprecated-cookies.mjs +0 -97
  388. package/dist/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
  389. package/dist/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
  390. package/dist/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
  391. package/dist/core/shopify-scripts/utils/tracking-values.mjs +0 -65
  392. package/dist/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
  393. package/dist/core/shopify-scripts/utils/uuid.mjs +0 -17
  394. package/dist/core/shopify-scripts/utils/uuid.mjs.map +0 -1
  395. package/dist/development/core/shopify-scripts/consent.mjs +0 -17
  396. package/dist/development/core/shopify-scripts/consent.mjs.map +0 -1
  397. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs +0 -97
  398. package/dist/development/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
  399. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
  400. package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
  401. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs +0 -65
  402. package/dist/development/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
  403. package/dist/development/core/shopify-scripts/utils/uuid.mjs +0 -17
  404. package/dist/development/core/shopify-scripts/utils/uuid.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/core/shopify-scripts/index.ts"],"sourcesContent":["import { getShopifyAnalyticsBusScript, getShopifyAnalyticsConfig } from \"./analytics\";\nimport { getShopifyConsentTrackingScript } from \"./consent\";\nimport {\n SHOPIFY_CONSENT_API_SCRIPT,\n SHOPIFY_CDN_ORIGIN,\n SHOPIFY_CONSENT_SCRIPT_ID,\n SHOPIFY_PRIVACY_BANNER_SCRIPT,\n SHOPIFY_SHOP_APP_ORIGIN,\n SHOPIFY_INBOX_SCRIPT,\n SHOPIFY_STOREFRONT_ANALYTICS_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_ACTIONS_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_EVENTS_INSPECTOR_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_EVENTS_SCRIPT,\n} from \"./constants\";\nimport { getShopifyGlobalBootstrapScript } from \"./global\";\nimport { getPerfKitScript } from \"./perfkit\";\nimport { renderShopifyScriptTag } from \"./render\";\nimport type {\n ShopifyLinkDescriptor,\n ShopifyScriptDescriptor,\n ShopifyScriptTagDescriptors,\n ShopifyScriptTagsOptions,\n} from \"./types\";\n\nexport {\n SHOPIFY_CDN_ORIGIN,\n SHOPIFY_CONSENT_API_SCRIPT,\n SHOPIFY_PERF_KIT_SCRIPT,\n SHOPIFY_PRIVACY_BANNER_SCRIPT,\n SHOPIFY_SHOP_APP_ORIGIN,\n SHOPIFY_INBOX_SCRIPT,\n SHOPIFY_STOREFRONT_ANALYTICS_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_ACTIONS_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_EVENTS_SCRIPT,\n SHOPIFY_STOREFRONT_WEBMCP_SCRIPT,\n VISITOR_CONSENT_COLLECTED_EVENT,\n} from \"./constants\";\nexport { getShopifyGlobal, getShopifyGlobalBootstrapScript } from \"./global\";\nexport { initializeShopifyScripts } from \"./initialize\";\nexport { renderShopifyScriptTag } from \"./render\";\nexport type {\n ShopifyScriptsAnalyticsConfig,\n ShopifyRoutesOptions,\n ShopifyScriptTagDescriptor,\n ShopifyScriptTagDescriptors,\n ShopifyScriptTagsOptions,\n ShopifyScriptsOptions,\n ShopifyScriptsI18n,\n ShopifyScriptsShop,\n} from \"./types\";\n\n/**\n * Returns grouped Shopify storefront script/link descriptors for SSR frameworks and bindings.\n *\n * Framework bindings wrap this with `initializeShopifyScripts()` to form their `ShopifyScripts`\n * component. Frameworks without a binding can render these descriptors during SSR and call\n * `initializeShopifyScripts()` during browser hydration.\n */\nexport function getShopifyScriptTags({\n analytics,\n consent,\n debug,\n i18n,\n nonce,\n shop,\n shopifyAnalytics = true,\n inbox = false,\n}: ShopifyScriptTagsOptions): ShopifyScriptTagDescriptors {\n const nonceAttributes = nonce !== undefined ? { nonce } : undefined;\n const analyticsConfig = getShopifyAnalyticsConfig({ analytics, consent, shop });\n\n const links: ShopifyLinkDescriptor[] = [\n {\n tagName: \"link\",\n attributes: {\n rel: \"preconnect\",\n href: SHOPIFY_CDN_ORIGIN,\n },\n },\n {\n tagName: \"link\",\n attributes: {\n rel: \"preconnect\",\n href: SHOPIFY_SHOP_APP_ORIGIN,\n },\n },\n {\n tagName: \"link\",\n attributes: {\n rel: \"prefetch\",\n as: \"script\",\n href: SHOPIFY_STOREFRONT_STANDARD_EVENTS_SCRIPT,\n crossorigin: \"anonymous\",\n },\n },\n ];\n const scripts: ShopifyScriptDescriptor[] = [\n {\n tagName: \"script\",\n attributes: { id: \"shopify-global-bootstrap\", ...nonceAttributes },\n innerHTML: getShopifyGlobalBootstrapScript({ i18n, shop }),\n },\n {\n tagName: \"script\",\n attributes: {\n id: \"shopify-standard-actions\",\n type: \"module\",\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src: SHOPIFY_STOREFRONT_STANDARD_ACTIONS_SCRIPT,\n },\n },\n ];\n\n if (__DEV__ && debug?.standardEventsInspector) {\n scripts.push({\n tagName: \"script\",\n attributes: {\n id: \"shopify-standard-events-inspector\",\n defer: true,\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src: SHOPIFY_STOREFRONT_STANDARD_EVENTS_INSPECTOR_SCRIPT,\n },\n });\n }\n\n if (inbox) {\n scripts.push({\n tagName: \"script\",\n attributes: {\n id: \"shopify-inbox\",\n type: \"module\",\n async: true,\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src: SHOPIFY_INBOX_SCRIPT,\n },\n });\n }\n\n // Keep this async consent library immediately before the inline consent bootstrap.\n // Parser-inserted async scripts execute in a later task, so the following inline\n // script can attach a load listener before the library runs.\n scripts.push({\n tagName: \"script\",\n attributes: {\n id: SHOPIFY_CONSENT_SCRIPT_ID,\n async: true,\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src:\n consent?.mode === \"default-banner\"\n ? SHOPIFY_PRIVACY_BANNER_SCRIPT\n : SHOPIFY_CONSENT_API_SCRIPT,\n },\n });\n // This must run immediately after the consent library tag so it can find that\n // tag and attach its load listener before consent-tracking-api/privacy-banner executes.\n scripts.push({\n tagName: \"script\",\n attributes: { id: \"shopify-consent-bootstrap\", ...nonceAttributes },\n innerHTML: getShopifyConsentTrackingScript(consent),\n });\n\n // This must run after getShopifyConsentTrackingScript because that script\n // temporarily annotates visitorConsentCollected events for the analytics bus.\n scripts.push({\n tagName: \"script\",\n attributes: { id: \"shopify-analytics-bus\", ...nonceAttributes },\n innerHTML: getShopifyAnalyticsBusScript(analyticsConfig),\n });\n\n if (shopifyAnalytics) {\n scripts.push({\n tagName: \"script\",\n attributes: {\n id: \"shopify-storefront-analytics\",\n async: true,\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src: SHOPIFY_STOREFRONT_ANALYTICS_SCRIPT,\n },\n });\n }\n\n const perfKitScript = getPerfKitScript(shop, nonceAttributes);\n if (perfKitScript) {\n scripts.push(perfKitScript);\n }\n\n return {\n links,\n scripts,\n get tags() {\n return [...links, ...scripts];\n },\n };\n}\n\n/**\n * Renders all Shopify storefront script/link descriptors to HTML strings.\n */\nexport function renderShopifyScriptTags(options: ShopifyScriptTagsOptions): string[] {\n return getShopifyScriptTags(options).tags.map(renderShopifyScriptTag);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA0DA,SAAgB,qBAAqB,EACnC,WACA,SACA,OACA,MACA,OACA,MACA,mBAAmB,MACnB,QAAQ,SACgD;CACxD,MAAM,kBAAkB,UAAU,SAAY,EAAE,OAAO,GAAG;CAC1D,MAAM,kBAAkB,0BAA0B;EAAE;EAAW;EAAS;EAAM,CAAC;CAE/E,MAAM,QAAiC;EACrC;GACE,SAAS;GACT,YAAY;IACV,KAAK;IACL,MAAM;IACP;GACF;EACD;GACE,SAAS;GACT,YAAY;IACV,KAAK;IACL,MAAM;IACP;GACF;EACD;GACE,SAAS;GACT,YAAY;IACV,KAAK;IACL,IAAI;IACJ,MAAM;IACN,aAAa;IACd;GACF;EACF;CACD,MAAM,UAAqC,CACzC;EACE,SAAS;EACT,YAAY;GAAE,IAAI;GAA4B,GAAG;GAAiB;EAClE,WAAW,gCAAgC;GAAE;GAAM;GAAM,CAAC;EAC3D,EACD;EACE,SAAS;EACT,YAAY;GACV,IAAI;GACJ,MAAM;GACN,aAAa;GACb,GAAG;GACH,KAAK;GACN;EACF,CACF;AAeD,KAAI,MACF,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GACV,IAAI;GACJ,MAAM;GACN,OAAO;GACP,aAAa;GACb,GAAG;GACH,KAAK;GACN;EACF,CAAC;AAMJ,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GACV,IAAI;GACJ,OAAO;GACP,aAAa;GACb,GAAG;GACH,KACE,SAAS,SAAS,mBACd,gCACA;GACP;EACF,CAAC;AAGF,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GAAE,IAAI;GAA6B,GAAG;GAAiB;EACnE,WAAW,gCAAgC,QAAQ;EACpD,CAAC;AAIF,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GAAE,IAAI;GAAyB,GAAG;GAAiB;EAC/D,WAAW,6BAA6B,gBAAgB;EACzD,CAAC;AAEF,KAAI,iBACF,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GACV,IAAI;GACJ,OAAO;GACP,aAAa;GACb,GAAG;GACH,KAAK;GACN;EACF,CAAC;CAGJ,MAAM,gBAAgB,iBAAiB,MAAM,gBAAgB;AAC7D,KAAI,cACF,SAAQ,KAAK,cAAc;AAG7B,QAAO;EACL;EACA;EACA,IAAI,OAAO;AACT,UAAO,CAAC,GAAG,OAAO,GAAG,QAAQ;;EAEhC;;;;;AAMH,SAAgB,wBAAwB,SAA6C;AACnF,QAAO,qBAAqB,QAAQ,CAAC,KAAK,IAAI,uBAAuB"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/core/shopify-scripts/index.ts"],"sourcesContent":["import { getShopifyAnalyticsBusScript, getShopifyAnalyticsConfig } from \"./analytics\";\nimport {\n SHOPIFY_ACCOUNT_SCRIPT,\n SHOPIFY_CONSENT_API_SCRIPT,\n SHOPIFY_CDN_ORIGIN,\n SHOPIFY_CONSENT_SCRIPT_ID,\n SHOPIFY_PRIVACY_BANNER_SCRIPT,\n SHOPIFY_SHOP_APP_ORIGIN,\n SHOPIFY_INBOX_SCRIPT,\n SHOPIFY_STOREFRONT_ANALYTICS_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_ACTIONS_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_EVENTS_INSPECTOR_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_EVENTS_SCRIPT,\n} from \"./constants\";\nimport { getShopifyGlobalBootstrapScript } from \"./global\";\nimport { getPerfKitScript } from \"./perfkit\";\nimport { renderShopifyScriptTag } from \"./render\";\nimport type {\n ShopifyLinkDescriptor,\n ShopifyScriptDescriptor,\n ShopifyScriptTagDescriptors,\n ShopifyScriptTagsOptions,\n} from \"./types\";\n\nexport {\n CONSENT_TRACKING_API_LOADED_EVENT,\n SHOPIFY_ACCOUNT_SCRIPT,\n SHOPIFY_CDN_ORIGIN,\n SHOPIFY_CONSENT_API_SCRIPT,\n SHOPIFY_PERF_KIT_SCRIPT,\n SHOPIFY_PRIVACY_BANNER_SCRIPT,\n SHOPIFY_SHOP_APP_ORIGIN,\n SHOPIFY_INBOX_SCRIPT,\n SHOPIFY_STOREFRONT_ANALYTICS_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_ACTIONS_SCRIPT,\n SHOPIFY_STOREFRONT_STANDARD_EVENTS_SCRIPT,\n SHOPIFY_STOREFRONT_WEBMCP_SCRIPT,\n VISITOR_CONSENT_COLLECTED_EVENT,\n} from \"./constants\";\nexport { getShopifyGlobal, getShopifyGlobalBootstrapScript } from \"./global\";\nexport { initializeShopifyScripts } from \"./initialize\";\nexport { renderShopifyScriptTag } from \"./render\";\nexport type {\n ShopifyScriptsAnalyticsConfig,\n ShopifyRoutesOptions,\n ShopifyScriptTagDescriptor,\n ShopifyScriptTagDescriptors,\n ShopifyScriptTagsOptions,\n ShopifyScriptsOptions,\n ShopifyScriptsI18n,\n ShopifyScriptsShop,\n} from \"./types\";\n\n/**\n * Returns grouped Shopify storefront script/link descriptors for SSR frameworks and bindings.\n *\n * Framework bindings wrap this with `initializeShopifyScripts()` to form their `ShopifyScripts`\n * component. Frameworks without a binding can render these descriptors during SSR and call\n * `initializeShopifyScripts()` during browser hydration.\n */\n// oxlint-disable-next-line complexity -- ordered assembly of optional Shopify script tags; each flag adds one branch and splitting would obscure the required load order\nexport function getShopifyScriptTags({\n account = false,\n analytics,\n consent,\n debug,\n i18n,\n nonce,\n shop,\n shopifyAnalytics = true,\n inbox = false,\n}: ShopifyScriptTagsOptions): ShopifyScriptTagDescriptors {\n const nonceAttributes = nonce !== undefined ? { nonce } : undefined;\n const analyticsConfig = getShopifyAnalyticsConfig({ analytics, consent, shop });\n\n const links: ShopifyLinkDescriptor[] = [\n {\n tagName: \"link\",\n attributes: {\n rel: \"preconnect\",\n href: SHOPIFY_CDN_ORIGIN,\n },\n },\n {\n tagName: \"link\",\n attributes: {\n rel: \"preconnect\",\n href: SHOPIFY_SHOP_APP_ORIGIN,\n },\n },\n {\n tagName: \"link\",\n attributes: {\n rel: \"prefetch\",\n as: \"script\",\n href: SHOPIFY_STOREFRONT_STANDARD_EVENTS_SCRIPT,\n crossorigin: \"anonymous\",\n },\n },\n ];\n const scripts: ShopifyScriptDescriptor[] = [\n {\n tagName: \"script\",\n attributes: { id: \"shopify-global-bootstrap\", ...nonceAttributes },\n innerHTML: getShopifyGlobalBootstrapScript({ i18n, shop }),\n },\n {\n tagName: \"script\",\n attributes: {\n id: \"shopify-standard-actions\",\n type: \"module\",\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src: SHOPIFY_STOREFRONT_STANDARD_ACTIONS_SCRIPT,\n },\n },\n ];\n\n if (__DEV__ && debug?.standardEventsInspector) {\n scripts.push({\n tagName: \"script\",\n attributes: {\n id: \"shopify-standard-events-inspector\",\n defer: true,\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src: SHOPIFY_STOREFRONT_STANDARD_EVENTS_INSPECTOR_SCRIPT,\n },\n });\n }\n\n if (inbox) {\n scripts.push({\n tagName: \"script\",\n attributes: {\n id: \"shopify-inbox\",\n type: \"module\",\n async: true,\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src: SHOPIFY_INBOX_SCRIPT,\n },\n });\n }\n\n if (account) {\n scripts.push({\n tagName: \"script\",\n attributes: {\n id: \"shopify-account\",\n type: \"module\",\n async: true,\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src: SHOPIFY_ACCOUNT_SCRIPT,\n },\n });\n }\n\n // Install readiness listeners before loading the async consent library so a\n // cached script cannot finish initialization before Hydrogen starts listening.\n scripts.push({\n tagName: \"script\",\n attributes: { id: \"shopify-analytics-bus\", ...nonceAttributes },\n innerHTML: getShopifyAnalyticsBusScript(analyticsConfig),\n });\n\n scripts.push({\n tagName: \"script\",\n attributes: {\n id: SHOPIFY_CONSENT_SCRIPT_ID,\n async: true,\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src:\n consent?.mode === \"default-banner\"\n ? SHOPIFY_PRIVACY_BANNER_SCRIPT\n : SHOPIFY_CONSENT_API_SCRIPT,\n },\n });\n\n if (shopifyAnalytics) {\n scripts.push({\n tagName: \"script\",\n attributes: {\n id: \"shopify-storefront-analytics\",\n async: true,\n crossorigin: \"anonymous\",\n ...nonceAttributes,\n src: SHOPIFY_STOREFRONT_ANALYTICS_SCRIPT,\n },\n });\n }\n\n const perfKitScript = getPerfKitScript(shop, nonceAttributes);\n if (perfKitScript) {\n scripts.push(perfKitScript);\n }\n\n return {\n links,\n scripts,\n get tags() {\n return [...links, ...scripts];\n },\n };\n}\n\n/**\n * Renders all Shopify storefront script/link descriptors to HTML strings.\n */\nexport function renderShopifyScriptTags(options: ShopifyScriptTagsOptions): string[] {\n return getShopifyScriptTags(options).tags.map(renderShopifyScriptTag);\n}\n"],"mappings":";;;;;;;;;;;;;;;AA6DA,SAAgB,qBAAqB,EACnC,UAAU,OACV,WACA,SACA,OACA,MACA,OACA,MACA,mBAAmB,MACnB,QAAQ,SACgD;CACxD,MAAM,kBAAkB,UAAU,SAAY,EAAE,OAAO,GAAG;CAC1D,MAAM,kBAAkB,0BAA0B;EAAE;EAAW;EAAS;EAAM,CAAC;CAE/E,MAAM,QAAiC;EACrC;GACE,SAAS;GACT,YAAY;IACV,KAAK;IACL,MAAM;IACP;GACF;EACD;GACE,SAAS;GACT,YAAY;IACV,KAAK;IACL,MAAM;IACP;GACF;EACD;GACE,SAAS;GACT,YAAY;IACV,KAAK;IACL,IAAI;IACJ,MAAM;IACN,aAAa;IACd;GACF;EACF;CACD,MAAM,UAAqC,CACzC;EACE,SAAS;EACT,YAAY;GAAE,IAAI;GAA4B,GAAG;GAAiB;EAClE,WAAW,gCAAgC;GAAE;GAAM;GAAM,CAAC;EAC3D,EACD;EACE,SAAS;EACT,YAAY;GACV,IAAI;GACJ,MAAM;GACN,aAAa;GACb,GAAG;GACH,KAAK;GACN;EACF,CACF;AAeD,KAAI,MACF,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GACV,IAAI;GACJ,MAAM;GACN,OAAO;GACP,aAAa;GACb,GAAG;GACH,KAAK;GACN;EACF,CAAC;AAGJ,KAAI,QACF,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GACV,IAAI;GACJ,MAAM;GACN,OAAO;GACP,aAAa;GACb,GAAG;GACH,KAAK;GACN;EACF,CAAC;AAKJ,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GAAE,IAAI;GAAyB,GAAG;GAAiB;EAC/D,WAAW,6BAA6B,gBAAgB;EACzD,CAAC;AAEF,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GACV,IAAI;GACJ,OAAO;GACP,aAAa;GACb,GAAG;GACH,KACE,SAAS,SAAS,mBACd,gCACA;GACP;EACF,CAAC;AAEF,KAAI,iBACF,SAAQ,KAAK;EACX,SAAS;EACT,YAAY;GACV,IAAI;GACJ,OAAO;GACP,aAAa;GACb,GAAG;GACH,KAAK;GACN;EACF,CAAC;CAGJ,MAAM,gBAAgB,iBAAiB,MAAM,gBAAgB;AAC7D,KAAI,cACF,SAAQ,KAAK,cAAc;AAG7B,QAAO;EACL;EACA;EACA,IAAI,OAAO;AACT,UAAO,CAAC,GAAG,OAAO,GAAG,QAAQ;;EAEhC;;;;;AAMH,SAAgB,wBAAwB,SAA6C;AACnF,QAAO,qBAAqB,QAAQ,CAAC,KAAK,IAAI,uBAAuB"}
@@ -9,6 +9,7 @@ import { InitializeShopifyScriptsOptions } from "./types.mjs";
9
9
  * and call this helper from their browser lifecycle.
10
10
  */
11
11
  declare function initializeShopifyScripts({
12
+ consent,
12
13
  navigate,
13
14
  routes,
14
15
  webMcp
@@ -1,9 +1,11 @@
1
+ import { getLogger } from "../logging/logging.mjs";
1
2
  import { configureShopifyRouting } from "./global.mjs";
2
- import { initializeDeprecatedCookies } from "./deprecated-cookies.mjs";
3
+ import { initializeCustomConsent } from "../analytics/custom-consent.mjs";
3
4
  import { initializeShopifyPageViewEvents } from "./page-view.mjs";
4
5
  import { loadShopifyWebMcpTools } from "./webmcp.mjs";
5
6
 
6
7
  //#region src/core/shopify-scripts/initialize.ts
8
+ const log = getLogger("consent");
7
9
  /**
8
10
  * Initializes Shopify browser script behavior for frameworks without a Hydrogen binding.
9
11
  *
@@ -11,13 +13,18 @@ import { loadShopifyWebMcpTools } from "./webmcp.mjs";
11
13
  * into a `ShopifyScripts` component, while custom integrations can render script tags during SSR
12
14
  * and call this helper from their browser lifecycle.
13
15
  */
14
- function initializeShopifyScripts({ navigate, routes, webMcp = true }) {
16
+ function initializeShopifyScripts({ consent, navigate, routes, webMcp = true }) {
15
17
  configureShopifyRouting({
16
18
  navigate,
17
19
  routes
18
20
  });
19
- initializeDeprecatedCookies();
20
21
  initializeShopifyPageViewEvents();
22
+ if (consent?.mode === "custom-banner") if (typeof consent.setup !== "function") log.warn("custom-banner requires a consent.setup callback; analytics delivery remains blocked until setup completes");
23
+ else try {
24
+ initializeCustomConsent(consent.setup);
25
+ } catch (error) {
26
+ log.error("custom consent initialization failed", { error });
27
+ }
21
28
  if (!webMcp) return Promise.resolve();
22
29
  return loadShopifyWebMcpTools();
23
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"initialize.mjs","names":[],"sources":["../../../src/core/shopify-scripts/initialize.ts"],"sourcesContent":["import { initializeDeprecatedCookies } from \"./deprecated-cookies\";\nimport { configureShopifyRouting } from \"./global\";\nimport { initializeShopifyPageViewEvents } from \"./page-view\";\nimport type { InitializeShopifyScriptsOptions } from \"./types\";\nimport { loadShopifyWebMcpTools } from \"./webmcp\";\n\n/**\n * Initializes Shopify browser script behavior for frameworks without a Hydrogen binding.\n *\n * This is the browser hydration half of `getShopifyScriptTags()`: framework bindings combine both\n * into a `ShopifyScripts` component, while custom integrations can render script tags during SSR\n * and call this helper from their browser lifecycle.\n */\nexport function initializeShopifyScripts({\n navigate,\n routes,\n webMcp = true,\n}: InitializeShopifyScriptsOptions): Promise<boolean | void> {\n configureShopifyRouting({ navigate, routes });\n initializeDeprecatedCookies();\n initializeShopifyPageViewEvents();\n\n if (!webMcp) return Promise.resolve();\n\n return loadShopifyWebMcpTools();\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,yBAAyB,EACvC,UACA,QACA,SAAS,QACkD;AAC3D,yBAAwB;EAAE;EAAU;EAAQ,CAAC;AAC7C,8BAA6B;AAC7B,kCAAiC;AAEjC,KAAI,CAAC,OAAQ,QAAO,QAAQ,SAAS;AAErC,QAAO,wBAAwB"}
1
+ {"version":3,"file":"initialize.mjs","names":[],"sources":["../../../src/core/shopify-scripts/initialize.ts"],"sourcesContent":["import { initializeCustomConsent } from \"../analytics/custom-consent\";\nimport { getLogger } from \"../logging\";\nimport { configureShopifyRouting } from \"./global\";\nimport { initializeShopifyPageViewEvents } from \"./page-view\";\nimport type { InitializeShopifyScriptsOptions } from \"./types\";\nimport { loadShopifyWebMcpTools } from \"./webmcp\";\n\nconst log = getLogger(\"consent\");\n\n/**\n * Initializes Shopify browser script behavior for frameworks without a Hydrogen binding.\n *\n * This is the browser hydration half of `getShopifyScriptTags()`: framework bindings combine both\n * into a `ShopifyScripts` component, while custom integrations can render script tags during SSR\n * and call this helper from their browser lifecycle.\n */\nexport function initializeShopifyScripts({\n consent,\n navigate,\n routes,\n webMcp = true,\n}: InitializeShopifyScriptsOptions): Promise<boolean | void> {\n configureShopifyRouting({ navigate, routes });\n initializeShopifyPageViewEvents();\n\n if (consent?.mode === \"custom-banner\") {\n if (typeof consent.setup !== \"function\") {\n log.warn(\n \"custom-banner requires a consent.setup callback; analytics delivery remains blocked until setup completes\",\n );\n } else {\n try {\n initializeCustomConsent(consent.setup);\n } catch (error) {\n log.error(\"custom consent initialization failed\", { error });\n }\n }\n }\n\n if (!webMcp) return Promise.resolve();\n\n return loadShopifyWebMcpTools();\n}\n"],"mappings":";;;;;;;AAOA,MAAM,MAAM,UAAU,UAAU;;;;;;;;AAShC,SAAgB,yBAAyB,EACvC,SACA,UACA,QACA,SAAS,QACkD;AAC3D,yBAAwB;EAAE;EAAU;EAAQ,CAAC;AAC7C,kCAAiC;AAEjC,KAAI,SAAS,SAAS,gBACpB,KAAI,OAAO,QAAQ,UAAU,WAC3B,KAAI,KACF,4GACD;KAED,KAAI;AACF,0BAAwB,QAAQ,MAAM;UAC/B,OAAO;AACd,MAAI,MAAM,wCAAwC,EAAE,OAAO,CAAC;;AAKlE,KAAI,CAAC,OAAQ,QAAO,QAAQ,SAAS;AAErC,QAAO,wBAAwB"}
@@ -52,6 +52,11 @@ type ShopifyScriptsShop = {
52
52
  myshopifyDomain: string;
53
53
  };
54
54
  type ShopifyScriptTagsOptions = {
55
+ /**
56
+ * Loads the customer account component. Render `<shopify-account>` where you want the account UI to appear.
57
+ * @see {@link https://shopify.dev/docs/api/storefront-web-components/components/shopify-account | shopify-account}
58
+ */
59
+ account?: boolean;
55
60
  analytics?: ShopifyScriptsAnalyticsConfig;
56
61
  consent?: ConsentConfig;
57
62
  debug?: {
@@ -68,6 +73,7 @@ type ShopifyRoutesOptions = {
68
73
  routes?: ShopifyRouteTemplates;
69
74
  };
70
75
  type InitializeShopifyScriptsOptions = ShopifyRoutesOptions & {
76
+ /** Pass the same consent configuration used to render the script tags. Setup runs once per bus. */consent?: ConsentConfig;
71
77
  webMcp?: boolean;
72
78
  };
73
79
  type ShopifyScriptsOptions = ShopifyScriptTagsOptions & InitializeShopifyScriptsOptions;
package/dist/core/url.mjs CHANGED
@@ -3,8 +3,11 @@ const SFAPI_RE = /^\/api\/(unstable|2\d{3}-\d{2})\/graphql\.json$/;
3
3
  const SHOPIFY_API_PROXY_PREFIX = "/__shopify";
4
4
  const SHOPIFY_API_PROXY_RE = /^\/__shopify(?:\/|$)/;
5
5
  const MCP_RE = /^\/api\/mcp$/;
6
+ const UCP_MCP_RE = /^\/api\/ucp\/mcp$/;
6
7
  const CHECKOUT_RE = /^\/checkout$/;
7
8
  const CART_PERMALINK_RE = /^\/cart\/\d+:\d+(?:,\d+:\d+)*$/;
9
+ const BUY_ITEM_PAIR = String.raw`(?:[A-Za-z0-9._-]+|~[A-Za-z0-9_-]+):[1-9]\d*`;
10
+ const BUY_PERMALINK_RE = new RegExp(`^/buy/${BUY_ITEM_PAIR}(?:,${BUY_ITEM_PAIR})*$`);
8
11
  const CUSTOMER_ACCOUNT_PATHS = {
9
12
  authorize: "/account/authorize",
10
13
  login: "/account/login",
@@ -19,7 +22,7 @@ const CUSTOMER_ACCOUNT_HANDOFF_PATHS = new Set(Object.values(CUSTOMER_ACCOUNT_PA
19
22
  * interceptors are intentionally excluded because they are not browser navigation destinations.
20
23
  */
21
24
  function isHydrogenServerHandoffPath(pathname) {
22
- return CHECKOUT_RE.test(pathname) || CART_PERMALINK_RE.test(pathname) || CUSTOMER_ACCOUNT_HANDOFF_PATHS.has(pathname);
25
+ return CHECKOUT_RE.test(pathname) || CART_PERMALINK_RE.test(pathname) || BUY_PERMALINK_RE.test(pathname) || CUSTOMER_ACCOUNT_HANDOFF_PATHS.has(pathname);
23
26
  }
24
27
  /**
25
28
  * Allowlisted `.well-known` resources proxied to the Online Store origin:
@@ -31,6 +34,22 @@ function isHydrogenServerHandoffPath(pathname) {
31
34
  */
32
35
  const WELL_KNOWN_RE = /^\/\.well-known\/(?:apple-developer-merchantid-domain-association|shopify\/fec\/produce)$/;
33
36
  const AJAX_CART_RE = /^(?:\/[a-z]{2}(?:-[a-z]{2})?)?\/cart(?:\.(?:js|json)|\/(?:add|update|change|clear)(?:\.(?:js|json))?)$/i;
37
+ /**
38
+ * Normalizes `target` to a path on `origin` that is safe to use as a redirect location.
39
+ * Returns `undefined` unless `target` is a path starting with `/` or an absolute URL,
40
+ * stays on `origin`, and its normalized pathname does not start with `//`.
41
+ */
42
+ function getSameOriginPath(target, origin) {
43
+ if (!target) return void 0;
44
+ try {
45
+ const url = target.startsWith("/") ? new URL(target, origin) : new URL(target);
46
+ if (url.origin !== origin) return void 0;
47
+ const path = `${url.pathname}${url.search}${url.hash}`;
48
+ return path.startsWith("//") ? void 0 : path;
49
+ } catch {
50
+ return;
51
+ }
52
+ }
34
53
  function normalizeStoreDomain(domain) {
35
54
  if (!domain) throw new Error("Storefront `storeDomain` is required.");
36
55
  if (domain.startsWith("http://") || domain.startsWith("https://")) return domain.replace(/\/+$/, "");
@@ -38,5 +57,5 @@ function normalizeStoreDomain(domain) {
38
57
  }
39
58
 
40
59
  //#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 };
60
+ export { AJAX_CART_RE, BUY_PERMALINK_RE, CHECKOUT_RE, CUSTOMER_ACCOUNT_PATHS, MCP_RE, SFAPI_RE, SHOPIFY_API_PROXY_PREFIX, SHOPIFY_API_PROXY_RE, UCP_MCP_RE, WELL_KNOWN_RE, getSameOriginPath, isHydrogenServerHandoffPath, normalizeStoreDomain };
42
61
  //# sourceMappingURL=url.mjs.map
@@ -1 +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"}
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 UCP_MCP_RE = /^\\/api\\/ucp\\/mcp$/;\nexport const CHECKOUT_RE = /^\\/checkout$/;\nexport const CART_PERMALINK_RE = /^\\/cart\\/\\d+:\\d+(?:,\\d+:\\d+)*$/;\nconst BUY_ITEM_PAIR = String.raw`(?:[A-Za-z0-9._-]+|~[A-Za-z0-9_-]+):[1-9]\\d*`;\n// Items are required so a storefront's own /buy pages keep routing to the app.\nexport const BUY_PERMALINK_RE = new RegExp(`^/buy/${BUY_ITEM_PAIR}(?:,${BUY_ITEM_PAIR})*$`);\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 BUY_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\n/**\n * Normalizes `target` to a path on `origin` that is safe to use as a redirect location.\n * Returns `undefined` unless `target` is a path starting with `/` or an absolute URL,\n * stays on `origin`, and its normalized pathname does not start with `//`.\n */\nexport function getSameOriginPath(\n target: string | null | undefined,\n origin: string,\n): string | undefined {\n if (!target) return undefined;\n\n try {\n // Parse absolute URLs without a base so scheme-relative forms like `https:evil.example`\n // resolve to their own host, as a browser would, instead of onto `origin`.\n const url = target.startsWith(\"/\") ? new URL(target, origin) : new URL(target);\n if (url.origin !== origin) return undefined;\n const path = `${url.pathname}${url.search}${url.hash}`;\n // The parsed pathname can itself start with `//` (e.g. from `/x/..//evil`),\n // which a redirect would resolve as a protocol-relative URL to another host.\n return path.startsWith(\"//\") ? undefined : path;\n } catch {\n return undefined;\n }\n}\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,aAAa;AAC1B,MAAa,cAAc;AAC3B,MAAa,oBAAoB;AACjC,MAAM,gBAAgB,OAAO,GAAG;AAEhC,MAAa,mBAAmB,IAAI,OAAO,SAAS,cAAc,MAAM,cAAc,KAAK;AAC3F,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,iBAAiB,KAAK,SAAS,IAC/B,+BAA+B,IAAI,SAAS;;;;;;;;;;AAYhD,MAAa,gBACX;AACF,MAAa,eACX;;;;;;AAOF,SAAgB,kBACd,QACA,QACoB;AACpB,KAAI,CAAC,OAAQ,QAAO;AAEpB,KAAI;EAGF,MAAM,MAAM,OAAO,WAAW,IAAI,GAAG,IAAI,IAAI,QAAQ,OAAO,GAAG,IAAI,IAAI,OAAO;AAC9E,MAAI,IAAI,WAAW,OAAQ,QAAO;EAClC,MAAM,OAAO,GAAG,IAAI,WAAW,IAAI,SAAS,IAAI;AAGhD,SAAO,KAAK,WAAW,KAAK,GAAG,SAAY;SACrC;AACN;;;AAIJ,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"}
@@ -3,16 +3,53 @@ import { GraphQLFormattedError } from "../client/types.mjs";
3
3
  import { AnyCustomerAccountDocument, CustomerAccountDocument } from "./graphql.mjs";
4
4
 
5
5
  //#region src/customer-account/client.d.ts
6
+ /**
7
+ * Options for {@link createCustomerAccountClient}.
8
+ *
9
+ * The client validates `shopId`, `customerApiVersion`, and `defaultTimeoutInMs`
10
+ * at construction time and throws synchronously on invalid values.
11
+ */
6
12
  type CreateCustomerAccountClientOptions = {
7
- shopId: string;
13
+ /** Numeric Shopify shop ID as a string (e.g. `"12345"`). Validated against `/^\d+$/`. */shopId: string;
14
+ /**
15
+ * Customer Account API version in `YYYY-MM` format.
16
+ * Defaults to {@link CUSTOMER_ACCOUNT_API_VERSION}.
17
+ */
8
18
  customerApiVersion?: string;
9
- requestContext: ShopifyRequestContext;
19
+ /**
20
+ * Request-scoped context created by `createShopifyRequestContext`. Must
21
+ * have a `url` property (throws at construction when missing or non-HTTPS;
22
+ * `http://localhost`, `127.0.0.1` and `::1` are accepted and sent as
23
+ * HTTPS). Supplies the i18n language for automatic `$language` variable
24
+ * injection, an abort signal that propagates request cancellation, and a
25
+ * personalization marker (sets `private, no-store` and removes CDN cache
26
+ * headers).
27
+ */
28
+ requestContext: ShopifyRequestContext; /** Custom `fetch` implementation. Falls back to `globalThis.fetch`. */
10
29
  fetch?: typeof globalThis.fetch;
30
+ /**
31
+ * Per-request timeout in milliseconds. Must be a positive safe integer no
32
+ * greater than 2,147,483,647. Defaults to 30,000 ms.
33
+ */
11
34
  defaultTimeoutInMs?: number;
12
35
  };
36
+ /**
37
+ * Per-call options passed to {@link CustomerAccountClient.graphql}.
38
+ * See `variables` for automatic `$language` injection.
39
+ */
13
40
  type CustomerAccountGraphqlOptions<Variables = Record<string, unknown>> = {
14
- accessToken: string;
41
+ /** Customer Account API access token, e.g. from `CustomerSession.getAccessToken()` or `getOrRefreshAccessToken()`. */accessToken: string;
42
+ /**
43
+ * Query variables. When the GraphQL document declares a `$language` variable
44
+ * and you omit it here, the client injects it automatically from
45
+ * `requestContext.i18n.language`.
46
+ */
15
47
  variables?: Variables;
48
+ /**
49
+ * Optional abort signal. Combined with the request context signal and the
50
+ * per-request timeout signal via `AbortSignal.any`, so cancellation from any
51
+ * source stops the request.
52
+ */
16
53
  signal?: AbortSignal;
17
54
  };
18
55
  type ResultOfDoc<Doc> = Doc extends CustomerAccountDocument<infer Result, never, string> ? Result : never;
@@ -22,6 +59,18 @@ type OptionalAutoVariables<Variables> = "language" extends keyof Variables ? {
22
59
  } : {};
23
60
  type UserVariables<Doc> = Omit<VariablesOfDoc<Doc>, "language"> & OptionalAutoVariables<VariablesOfDoc<Doc>>;
24
61
  type HasNoRequiredKeys<T> = Record<string, never> extends T ? true : false;
62
+ /**
63
+ * Discriminated union returned by {@link CustomerAccountClient.graphql}.
64
+ *
65
+ * Discriminate on the presence of `errors`, not via `Partial`:
66
+ *
67
+ * - **Success:** `data` is always present, `errors` is `undefined`.
68
+ * - **Partial or error:** `errors` is a non-empty array of
69
+ * {@link GraphQLFormattedError}, and `data` may be `null`.
70
+ *
71
+ * Both arms expose the raw response `headers` for inspecting
72
+ * `x-request-id` or rate-limit metadata.
73
+ */
25
74
  type CustomerAccountGraphqlResult<Result = unknown> = {
26
75
  data: Result;
27
76
  errors?: undefined;
@@ -31,13 +80,88 @@ type CustomerAccountGraphqlResult<Result = unknown> = {
31
80
  errors: GraphQLFormattedError[];
32
81
  headers: Headers;
33
82
  };
83
+ /**
84
+ * Conditional rest parameter for {@link CustomerAccountClient.graphql}.
85
+ *
86
+ * When the document has no required user variables (after `language` is
87
+ * excluded from the requirement), `options.variables` is optional. When the
88
+ * document declares required variables, the type forces the caller to supply
89
+ * them, turning a missing-variables bug into a compile error.
90
+ */
34
91
  type CustomerAccountGqlRestParam<Doc extends AnyCustomerAccountDocument> = HasNoRequiredKeys<UserVariables<Doc>> extends true ? [options: CustomerAccountGraphqlOptions<UserVariables<Doc>>] : [options: CustomerAccountGraphqlOptions<UserVariables<Doc>> & {
35
92
  variables: UserVariables<Doc>;
36
93
  }];
94
+ /**
95
+ * A server-side GraphQL client for the Shopify Customer Account API.
96
+ *
97
+ * Created by {@link createCustomerAccountClient}. Each call to `graphql()`
98
+ * validates that the document was produced by `gql()` (runtime branded with a
99
+ * private Symbol), validates the access token, auto-injects the `language`
100
+ * variable from i18n context when applicable, and marks the response as
101
+ * personalized so the app response is sent with private, no-store cache headers.
102
+ */
37
103
  type CustomerAccountClient = {
38
- readonly apiUrl: string;
104
+ /** Constructed endpoint: `https://shopify.com/{shopId}/account/customer/api/{version}/graphql`. */readonly apiUrl: string;
105
+ /**
106
+ * Send a GraphQL request to the Customer Account API.
107
+ *
108
+ * The document must be created by `gql()`. At call time the client:
109
+ * 1. Validates the document brand and options object.
110
+ * 2. Marks the response as personalized (sets `private, no-store`).
111
+ * 3. Validates the access token.
112
+ * 4. Throws if a signal is already aborted.
113
+ * 5. Combines the request context signal, the caller's signal, and a timeout
114
+ * signal via `AbortSignal.any`.
115
+ * 6. Auto-injects the `language` variable from `requestContext.i18n.language`
116
+ * when the document declares `$language` and the caller omits it.
117
+ *
118
+ * @throws {TypeError} When the document is not a branded `gql()` result.
119
+ * @throws {CustomerAccountAuthenticationError} When the access token fails pre-flight validation.
120
+ * @throws {CustomerAccountTimeoutError} When the request exceeds `defaultTimeoutInMs`.
121
+ * @throws {CustomerAccountApiError} On non-OK HTTP status, unparseable JSON, or missing `data`.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const result = await client.graphql(
126
+ * gql(`query { customer { firstName lastName } }`),
127
+ * { accessToken },
128
+ * );
129
+ *
130
+ * if (result.errors) {
131
+ * console.error(result.errors);
132
+ * } else {
133
+ * console.log(result.data.customer.firstName);
134
+ * }
135
+ * ```
136
+ */
39
137
  graphql: <const Doc extends AnyCustomerAccountDocument>(document: Doc, ...options: CustomerAccountGqlRestParam<Doc>) => Promise<CustomerAccountGraphqlResult<ResultOfDoc<Doc>>>;
40
138
  };
139
+ /**
140
+ * Creates a server-only {@link CustomerAccountClient} for the Shopify Customer
141
+ * Account API.
142
+ *
143
+ * Validates `shopId`, `customerApiVersion`, and `defaultTimeoutInMs` at
144
+ * construction time. The returned client auto-injects the `language` variable
145
+ * from `requestContext.i18n.language` when applicable, composes abort signals,
146
+ * and marks responses as personalized (sets `private, no-store` cache headers).
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * import { createCustomerAccountClient, gql } from "@shopify/hydrogen/customer-account";
151
+ *
152
+ * const customerAccount = createCustomerAccountClient({
153
+ * shopId: "12345",
154
+ * requestContext,
155
+ * });
156
+ *
157
+ * const { data } = await customerAccount.graphql(
158
+ * gql(`query { customer { firstName } }`),
159
+ * { accessToken },
160
+ * );
161
+ * ```
162
+ *
163
+ * @throws {Error} When called in a browser context, when `shopId`, `customerApiVersion`, or `defaultTimeoutInMs` is invalid, when no `fetch` is available, or when `requestContext.url` is missing or not HTTPS.
164
+ */
41
165
  declare function createCustomerAccountClient({
42
166
  shopId,
43
167
  customerApiVersion,
@@ -15,8 +15,34 @@ const LOCAL_ORIGIN_HOSTS = new Set([
15
15
  "::1",
16
16
  "[::1]"
17
17
  ]);
18
- const USER_AGENT = `Hydrogen 2026.10.0-preview.2`;
18
+ const USER_AGENT = `Hydrogen 2026.10.0-preview.4`;
19
19
  const CUSTOMER_ACCOUNT_GRAPHQL_PERSONALIZATION_REASON = "customer-account-graphql";
20
+ /**
21
+ * Creates a server-only {@link CustomerAccountClient} for the Shopify Customer
22
+ * Account API.
23
+ *
24
+ * Validates `shopId`, `customerApiVersion`, and `defaultTimeoutInMs` at
25
+ * construction time. The returned client auto-injects the `language` variable
26
+ * from `requestContext.i18n.language` when applicable, composes abort signals,
27
+ * and marks responses as personalized (sets `private, no-store` cache headers).
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { createCustomerAccountClient, gql } from "@shopify/hydrogen/customer-account";
32
+ *
33
+ * const customerAccount = createCustomerAccountClient({
34
+ * shopId: "12345",
35
+ * requestContext,
36
+ * });
37
+ *
38
+ * const { data } = await customerAccount.graphql(
39
+ * gql(`query { customer { firstName } }`),
40
+ * { accessToken },
41
+ * );
42
+ * ```
43
+ *
44
+ * @throws {Error} When called in a browser context, when `shopId`, `customerApiVersion`, or `defaultTimeoutInMs` is invalid, when no `fetch` is available, or when `requestContext.url` is missing or not HTTPS.
45
+ */
20
46
  function createCustomerAccountClient({ shopId, customerApiVersion = CUSTOMER_ACCOUNT_API_VERSION, requestContext, fetch: customFetch, defaultTimeoutInMs = DEFAULT_TIMEOUT_IN_MS }) {
21
47
  if (typeof document !== "undefined") throw new Error("Customer Account API tokens cannot be used in a browser context. Use this client from server or edge routes only.");
22
48
  validateShopId(shopId);
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../../src/customer-account/client.ts"],"sourcesContent":["import type { GraphQLFormattedError } from \"../client/types\";\nimport { CUSTOMER_ACCOUNT_API_VERSION, DEFAULT_TIMEOUT_IN_MS } from \"../core/constants\";\nimport type { ShopifyRequestContext } from \"../core/request-context\";\nimport { isObjectRecord } from \"../core/utils/record\";\nimport {\n CustomerAccountApiError,\n CustomerAccountAuthenticationError,\n CustomerAccountTimeoutError,\n} from \"./errors\";\nimport {\n assertCustomerAccountDocument,\n type AnyCustomerAccountDocument,\n type CustomerAccountDocument,\n} from \"./graphql\";\n\nconst SHOP_ID_RE = /^\\d+$/;\nconst CUSTOMER_API_VERSION_RE = /^\\d{4}-\\d{2}$/;\nconst MAX_ASCII_CONTROL_CODE_POINT = 31;\nconst ASCII_DELETE_CODE_POINT = 127;\nconst MAX_SET_TIMEOUT_IN_MS = 2_147_483_647;\nconst LOCAL_ORIGIN_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\"]);\nconst USER_AGENT = `Hydrogen ${__HYDROGEN_VERSION__}`;\nconst CUSTOMER_ACCOUNT_GRAPHQL_PERSONALIZATION_REASON = \"customer-account-graphql\";\n\ntype AccessToken = string | null | undefined;\ntype FetchCustomerAccountGraphqlParams = {\n fetch: typeof globalThis.fetch;\n apiUrl: string;\n accessToken: string;\n origin: string;\n query: string;\n variables: Record<string, unknown>;\n signal: AbortSignal;\n timeoutSignal: AbortSignal;\n timeoutInMs: number;\n};\n\nexport type CreateCustomerAccountClientOptions = {\n shopId: string;\n customerApiVersion?: string;\n requestContext: ShopifyRequestContext;\n fetch?: typeof globalThis.fetch;\n defaultTimeoutInMs?: number;\n};\n\nexport type CustomerAccountGraphqlOptions<Variables = Record<string, unknown>> = {\n accessToken: string;\n variables?: Variables;\n signal?: AbortSignal;\n};\n\ntype ResultOfDoc<Doc> =\n Doc extends CustomerAccountDocument<infer Result, never, string> ? Result : never;\ntype VariablesOfDoc<Doc> =\n Doc extends CustomerAccountDocument<unknown, infer Variables, string> ? Variables : never;\ntype OptionalAutoVariables<Variables> = \"language\" extends keyof Variables\n ? { language?: Variables[\"language\"] }\n : {};\ntype UserVariables<Doc> = Omit<VariablesOfDoc<Doc>, \"language\"> &\n OptionalAutoVariables<VariablesOfDoc<Doc>>;\ntype HasNoRequiredKeys<T> = Record<string, never> extends T ? true : false;\n\nexport type CustomerAccountGraphqlResult<Result = unknown> =\n | { data: Result; errors?: undefined; headers: Headers }\n | { data: Result | null; errors: GraphQLFormattedError[]; headers: Headers };\n\nexport type CustomerAccountGqlRestParam<Doc extends AnyCustomerAccountDocument> =\n HasNoRequiredKeys<UserVariables<Doc>> extends true\n ? [options: CustomerAccountGraphqlOptions<UserVariables<Doc>>]\n : [\n options: CustomerAccountGraphqlOptions<UserVariables<Doc>> & {\n variables: UserVariables<Doc>;\n },\n ];\n\nexport type CustomerAccountClient = {\n readonly apiUrl: string;\n graphql: <const Doc extends AnyCustomerAccountDocument>(\n document: Doc,\n ...options: CustomerAccountGqlRestParam<Doc>\n ) => Promise<CustomerAccountGraphqlResult<ResultOfDoc<Doc>>>;\n};\n\nexport function createCustomerAccountClient({\n shopId,\n customerApiVersion = CUSTOMER_ACCOUNT_API_VERSION,\n requestContext,\n fetch: customFetch,\n defaultTimeoutInMs = DEFAULT_TIMEOUT_IN_MS,\n}: CreateCustomerAccountClientOptions): CustomerAccountClient {\n if (typeof document !== \"undefined\") {\n throw new Error(\n \"Customer Account API tokens cannot be used in a browser context. Use this client from server or edge routes only.\",\n );\n }\n\n validateShopId(shopId);\n validateCustomerApiVersion(customerApiVersion);\n validateTimeout(defaultTimeoutInMs);\n\n const resolvedFetch = customFetch ?? globalThis.fetch;\n if (typeof resolvedFetch !== \"function\") {\n throw new Error(\n \"No fetch function available. Pass a fetch option or ensure globalThis.fetch exists.\",\n );\n }\n\n const origin = getOrigin(requestContext.url);\n const apiUrl = `https://shopify.com/${shopId}/account/customer/api/${customerApiVersion}/graphql`;\n\n async function graphql<const Doc extends AnyCustomerAccountDocument>(\n document: Doc,\n ...rest: CustomerAccountGqlRestParam<Doc>\n ): Promise<CustomerAccountGraphqlResult<ResultOfDoc<Doc>>>;\n async function graphql(\n document: AnyCustomerAccountDocument,\n ...rest: [options?: CustomerAccountGraphqlOptions<unknown>]\n ): Promise<CustomerAccountGraphqlResult<unknown>> {\n assertCustomerAccountDocument(document);\n const options = validateGraphqlOptions(rest[0]);\n requestContext.markResponseAsPersonalized(CUSTOMER_ACCOUNT_GRAPHQL_PERSONALIZATION_REASON);\n const accessToken = validateAccessToken(options.accessToken);\n const externalSignals = [requestContext.signal, options.signal].filter(\n (signal): signal is AbortSignal => Boolean(signal),\n );\n throwIfAlreadyAborted(externalSignals);\n\n const { signal: timeoutSignal, cleanup: cleanupTimeout } =\n createCustomerAccountTimeoutSignal(defaultTimeoutInMs);\n externalSignals.push(timeoutSignal);\n const signal = AbortSignal.any(externalSignals);\n\n try {\n const variables = getVariables(\n document,\n getOptionalVariables(options.variables),\n requestContext.i18n.language,\n );\n const response = await fetchCustomerAccountGraphql({\n fetch: resolvedFetch,\n apiUrl,\n accessToken,\n origin,\n query: document.source,\n variables,\n signal,\n timeoutSignal,\n timeoutInMs: defaultTimeoutInMs,\n });\n\n return await readCustomerAccountGraphqlResponse(\n response,\n signal,\n timeoutSignal,\n defaultTimeoutInMs,\n );\n } finally {\n cleanupTimeout();\n }\n }\n\n return { apiUrl, graphql };\n}\n\nasync function fetchCustomerAccountGraphql({\n fetch,\n apiUrl,\n accessToken,\n origin,\n query,\n variables,\n signal,\n timeoutSignal,\n timeoutInMs,\n}: FetchCustomerAccountGraphqlParams): Promise<Response> {\n try {\n return await withAbort(\n fetch(apiUrl, {\n method: \"POST\",\n headers: new Headers({\n Authorization: accessToken,\n \"Content-Type\": \"application/json\",\n Origin: origin,\n \"User-Agent\": USER_AGENT,\n }),\n body: JSON.stringify({ query, variables }),\n cache: \"no-store\",\n redirect: \"manual\",\n signal,\n }),\n signal,\n );\n } catch (cause) {\n throwIfCustomerAccountSignalAborted(signal, timeoutSignal, timeoutInMs);\n if (isAbortError(cause)) throw cause;\n throw new CustomerAccountApiError(\"Customer Account API request failed\", { cause });\n }\n}\n\nasync function readCustomerAccountGraphqlResponse(\n response: Response,\n signal: AbortSignal,\n timeoutSignal: AbortSignal,\n timeoutInMs: number,\n): Promise<CustomerAccountGraphqlResult<unknown>> {\n const requestId = response.headers.get(\"x-request-id\") ?? undefined;\n if (!response.ok) {\n cancelResponseBody(response);\n throw new CustomerAccountApiError(`Customer Account API responded with ${response.status}`, {\n status: response.status,\n requestId,\n retryAfter: response.headers.get(\"retry-after\") ?? undefined,\n });\n }\n\n let body: { data?: unknown; errors?: GraphQLFormattedError[] };\n try {\n body = parseGraphqlResponse(await withAbort(response.json(), signal));\n } catch (cause) {\n cancelResponseBody(response);\n\n throwIfCustomerAccountSignalAborted(signal, timeoutSignal, timeoutInMs);\n if (isAbortError(cause)) throw cause;\n throw new CustomerAccountApiError(\"Failed to parse Customer Account API response as JSON\", {\n status: response.status,\n requestId,\n cause,\n });\n }\n\n if (body.errors) {\n return { data: body.data ?? null, errors: body.errors, headers: response.headers };\n }\n\n if (body.data == null) {\n throw new CustomerAccountApiError(\"Customer Account API response did not include data\", {\n status: response.status,\n requestId,\n });\n }\n\n return { data: body.data, headers: response.headers };\n}\n\nfunction cancelResponseBody(response: Response): void {\n try {\n void response.body?.cancel().catch(() => undefined);\n } catch {}\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 createCustomerAccountTimeoutSignal(timeoutInMs: number): {\n signal: AbortSignal;\n cleanup: () => void;\n} {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort(new CustomerAccountTimeoutError(timeoutInMs));\n }, timeoutInMs);\n\n return { signal: controller.signal, cleanup: () => clearTimeout(timeoutId) };\n}\n\nfunction withAbort<T>(promise: T | Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return Promise.resolve(promise);\n if (signal.aborted) {\n return Promise.reject(\n signal.reason ?? new DOMException(\"signal is aborted without reason\", \"AbortError\"),\n );\n }\n\n return new Promise<T>((resolve, reject) => {\n const abort = () => {\n signal.removeEventListener(\"abort\", abort);\n const reason =\n signal.reason ?? new DOMException(\"signal is aborted without reason\", \"AbortError\");\n reject(reason);\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 getVariables(\n document: { variableNames: ReadonlySet<string> },\n variables: Record<string, unknown>,\n language: ShopifyRequestContext[\"i18n\"][\"language\"],\n): Record<string, unknown> {\n if (!document.variableNames.has(\"language\")) return variables;\n if (\"language\" in variables) return variables;\n return { ...variables, language };\n}\n\nfunction throwIfAlreadyAborted(signals: AbortSignal[]): void {\n const abortedSignal = signals.find((signal) => signal.aborted);\n if (!abortedSignal) return;\n throw abortedSignal.reason ?? new DOMException(\"signal is aborted without reason\", \"AbortError\");\n}\n\nfunction throwIfCustomerAccountSignalAborted(\n signal: AbortSignal,\n timeoutSignal: AbortSignal,\n timeoutInMs: number,\n): void {\n if (!signal.aborted) return;\n if (timeoutSignal.aborted && signal.reason === timeoutSignal.reason) {\n const reason = timeoutSignal.reason;\n throw reason instanceof CustomerAccountTimeoutError\n ? reason\n : new CustomerAccountTimeoutError(timeoutInMs);\n }\n throw signal.reason ?? new DOMException(\"signal is aborted without reason\", \"AbortError\");\n}\n\nfunction isAbortError(cause: unknown): cause is DOMException {\n return cause instanceof DOMException && cause.name === \"AbortError\";\n}\n\nfunction parseGraphqlResponse(json: unknown): {\n data?: unknown;\n errors?: GraphQLFormattedError[];\n} {\n if (!isObjectRecord(json)) {\n throw new Error(\"Customer Account API returned unexpected JSON type\");\n }\n\n if (!(\"data\" in json) && !(\"errors\" in json)) {\n throw new Error(\"Customer Account API response must include data or errors\");\n }\n const errors = json.errors;\n if (errors !== undefined && !isGraphqlErrorArray(errors)) {\n throw new Error(\"Customer Account API returned invalid GraphQL errors\");\n }\n return { data: json.data, errors };\n}\n\nfunction isGraphqlErrorArray(value: unknown): value is GraphQLFormattedError[] {\n if (!Array.isArray(value)) return false;\n return value.every((error) => {\n return typeof error === \"object\" && error !== null && typeof error.message === \"string\";\n });\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 validateCustomerApiVersion(customerApiVersion: string): void {\n if (!CUSTOMER_API_VERSION_RE.test(customerApiVersion)) {\n throw new Error(\"customerApiVersion must use YYYY-MM format\");\n }\n}\n\nfunction validateAccessToken(accessToken: AccessToken): string {\n if (\n typeof accessToken !== \"string\" ||\n accessToken === \"\" ||\n accessToken.trim() !== accessToken ||\n hasAsciiControlCharacter(accessToken)\n ) {\n throw new CustomerAccountAuthenticationError();\n }\n return accessToken;\n}\n\nfunction validateGraphqlOptions(\n options: CustomerAccountGraphqlOptions<unknown> | undefined,\n): CustomerAccountGraphqlOptions<unknown> {\n if (typeof options !== \"object\" || options === null) {\n throw new CustomerAccountAuthenticationError();\n }\n return options;\n}\n\nfunction getOptionalVariables(variables: unknown): Record<string, unknown> {\n if (variables === undefined) return {};\n if (!isObjectRecord(variables)) {\n throw new TypeError(\"Customer Account API variables must be an object\");\n }\n return variables;\n}\n\nfunction hasAsciiControlCharacter(value: string): boolean {\n for (let index = 0; index < value.length; index++) {\n const codePoint = value.charCodeAt(index);\n if (codePoint <= MAX_ASCII_CONTROL_CODE_POINT || codePoint === ASCII_DELETE_CODE_POINT) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Customer Account API requires an HTTPS Origin, but local dev frameworks often\n * receive `http://localhost` requests. Keep that local-only exception without\n * weakening origin validation for non-local requests.\n */\nfunction getOrigin(requestUrl: string | undefined): string {\n if (!requestUrl) {\n throw new Error(\"requestContext.url is required for Customer Account API requests\");\n }\n\n const url = new URL(requestUrl);\n if (url.protocol === \"https:\") return url.origin;\n if (url.protocol === \"http:\" && LOCAL_ORIGIN_HOSTS.has(url.hostname)) {\n return `https://${url.host}`;\n }\n throw new Error(`Customer Account API origin must use HTTPS. Received: ${requestUrl}`);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../../src/customer-account/client.ts"],"sourcesContent":["import type { GraphQLFormattedError } from \"../client/types\";\nimport { CUSTOMER_ACCOUNT_API_VERSION, DEFAULT_TIMEOUT_IN_MS } from \"../core/constants\";\nimport type { ShopifyRequestContext } from \"../core/request-context\";\nimport { isObjectRecord } from \"../core/utils/record\";\nimport {\n CustomerAccountApiError,\n CustomerAccountAuthenticationError,\n CustomerAccountTimeoutError,\n} from \"./errors\";\nimport {\n assertCustomerAccountDocument,\n type AnyCustomerAccountDocument,\n type CustomerAccountDocument,\n} from \"./graphql\";\n\nconst SHOP_ID_RE = /^\\d+$/;\nconst CUSTOMER_API_VERSION_RE = /^\\d{4}-\\d{2}$/;\nconst MAX_ASCII_CONTROL_CODE_POINT = 31;\nconst ASCII_DELETE_CODE_POINT = 127;\nconst MAX_SET_TIMEOUT_IN_MS = 2_147_483_647;\nconst LOCAL_ORIGIN_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\"]);\nconst USER_AGENT = `Hydrogen ${__HYDROGEN_VERSION__}`;\nconst CUSTOMER_ACCOUNT_GRAPHQL_PERSONALIZATION_REASON = \"customer-account-graphql\";\n\ntype AccessToken = string | null | undefined;\ntype FetchCustomerAccountGraphqlParams = {\n fetch: typeof globalThis.fetch;\n apiUrl: string;\n accessToken: string;\n origin: string;\n query: string;\n variables: Record<string, unknown>;\n signal: AbortSignal;\n timeoutSignal: AbortSignal;\n timeoutInMs: number;\n};\n\n/**\n * Options for {@link createCustomerAccountClient}.\n *\n * The client validates `shopId`, `customerApiVersion`, and `defaultTimeoutInMs`\n * at construction time and throws synchronously on invalid values.\n */\nexport type CreateCustomerAccountClientOptions = {\n /** Numeric Shopify shop ID as a string (e.g. `\"12345\"`). Validated against `/^\\d+$/`. */\n shopId: string;\n /**\n * Customer Account API version in `YYYY-MM` format.\n * Defaults to {@link CUSTOMER_ACCOUNT_API_VERSION}.\n */\n customerApiVersion?: string;\n /**\n * Request-scoped context created by `createShopifyRequestContext`. Must\n * have a `url` property (throws at construction when missing or non-HTTPS;\n * `http://localhost`, `127.0.0.1` and `::1` are accepted and sent as\n * HTTPS). Supplies the i18n language for automatic `$language` variable\n * injection, an abort signal that propagates request cancellation, and a\n * personalization marker (sets `private, no-store` and removes CDN cache\n * headers).\n */\n requestContext: ShopifyRequestContext;\n /** Custom `fetch` implementation. Falls back to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch;\n /**\n * Per-request timeout in milliseconds. Must be a positive safe integer no\n * greater than 2,147,483,647. Defaults to 30,000 ms.\n */\n defaultTimeoutInMs?: number;\n};\n\n/**\n * Per-call options passed to {@link CustomerAccountClient.graphql}.\n * See `variables` for automatic `$language` injection.\n */\nexport type CustomerAccountGraphqlOptions<Variables = Record<string, unknown>> = {\n /** Customer Account API access token, e.g. from `CustomerSession.getAccessToken()` or `getOrRefreshAccessToken()`. */\n accessToken: string;\n /**\n * Query variables. When the GraphQL document declares a `$language` variable\n * and you omit it here, the client injects it automatically from\n * `requestContext.i18n.language`.\n */\n variables?: Variables;\n /**\n * Optional abort signal. Combined with the request context signal and the\n * per-request timeout signal via `AbortSignal.any`, so cancellation from any\n * source stops the request.\n */\n signal?: AbortSignal;\n};\n\ntype ResultOfDoc<Doc> =\n Doc extends CustomerAccountDocument<infer Result, never, string> ? Result : never;\ntype VariablesOfDoc<Doc> =\n Doc extends CustomerAccountDocument<unknown, infer Variables, string> ? Variables : never;\ntype OptionalAutoVariables<Variables> = \"language\" extends keyof Variables\n ? { language?: Variables[\"language\"] }\n : {};\ntype UserVariables<Doc> = Omit<VariablesOfDoc<Doc>, \"language\"> &\n OptionalAutoVariables<VariablesOfDoc<Doc>>;\ntype HasNoRequiredKeys<T> = Record<string, never> extends T ? true : false;\n\n/**\n * Discriminated union returned by {@link CustomerAccountClient.graphql}.\n *\n * Discriminate on the presence of `errors`, not via `Partial`:\n *\n * - **Success:** `data` is always present, `errors` is `undefined`.\n * - **Partial or error:** `errors` is a non-empty array of\n * {@link GraphQLFormattedError}, and `data` may be `null`.\n *\n * Both arms expose the raw response `headers` for inspecting\n * `x-request-id` or rate-limit metadata.\n */\nexport type CustomerAccountGraphqlResult<Result = unknown> =\n | { data: Result; errors?: undefined; headers: Headers }\n | { data: Result | null; errors: GraphQLFormattedError[]; headers: Headers };\n\n/**\n * Conditional rest parameter for {@link CustomerAccountClient.graphql}.\n *\n * When the document has no required user variables (after `language` is\n * excluded from the requirement), `options.variables` is optional. When the\n * document declares required variables, the type forces the caller to supply\n * them, turning a missing-variables bug into a compile error.\n */\nexport type CustomerAccountGqlRestParam<Doc extends AnyCustomerAccountDocument> =\n HasNoRequiredKeys<UserVariables<Doc>> extends true\n ? [options: CustomerAccountGraphqlOptions<UserVariables<Doc>>]\n : [\n options: CustomerAccountGraphqlOptions<UserVariables<Doc>> & {\n variables: UserVariables<Doc>;\n },\n ];\n\n/**\n * A server-side GraphQL client for the Shopify Customer Account API.\n *\n * Created by {@link createCustomerAccountClient}. Each call to `graphql()`\n * validates that the document was produced by `gql()` (runtime branded with a\n * private Symbol), validates the access token, auto-injects the `language`\n * variable from i18n context when applicable, and marks the response as\n * personalized so the app response is sent with private, no-store cache headers.\n */\nexport type CustomerAccountClient = {\n /** Constructed endpoint: `https://shopify.com/{shopId}/account/customer/api/{version}/graphql`. */\n readonly apiUrl: string;\n /**\n * Send a GraphQL request to the Customer Account API.\n *\n * The document must be created by `gql()`. At call time the client:\n * 1. Validates the document brand and options object.\n * 2. Marks the response as personalized (sets `private, no-store`).\n * 3. Validates the access token.\n * 4. Throws if a signal is already aborted.\n * 5. Combines the request context signal, the caller's signal, and a timeout\n * signal via `AbortSignal.any`.\n * 6. Auto-injects the `language` variable from `requestContext.i18n.language`\n * when the document declares `$language` and the caller omits it.\n *\n * @throws {TypeError} When the document is not a branded `gql()` result.\n * @throws {CustomerAccountAuthenticationError} When the access token fails pre-flight validation.\n * @throws {CustomerAccountTimeoutError} When the request exceeds `defaultTimeoutInMs`.\n * @throws {CustomerAccountApiError} On non-OK HTTP status, unparseable JSON, or missing `data`.\n *\n * @example\n * ```ts\n * const result = await client.graphql(\n * gql(`query { customer { firstName lastName } }`),\n * { accessToken },\n * );\n *\n * if (result.errors) {\n * console.error(result.errors);\n * } else {\n * console.log(result.data.customer.firstName);\n * }\n * ```\n */\n graphql: <const Doc extends AnyCustomerAccountDocument>(\n document: Doc,\n ...options: CustomerAccountGqlRestParam<Doc>\n ) => Promise<CustomerAccountGraphqlResult<ResultOfDoc<Doc>>>;\n};\n\n/**\n * Creates a server-only {@link CustomerAccountClient} for the Shopify Customer\n * Account API.\n *\n * Validates `shopId`, `customerApiVersion`, and `defaultTimeoutInMs` at\n * construction time. The returned client auto-injects the `language` variable\n * from `requestContext.i18n.language` when applicable, composes abort signals,\n * and marks responses as personalized (sets `private, no-store` cache headers).\n *\n * @example\n * ```ts\n * import { createCustomerAccountClient, gql } from \"@shopify/hydrogen/customer-account\";\n *\n * const customerAccount = createCustomerAccountClient({\n * shopId: \"12345\",\n * requestContext,\n * });\n *\n * const { data } = await customerAccount.graphql(\n * gql(`query { customer { firstName } }`),\n * { accessToken },\n * );\n * ```\n *\n * @throws {Error} When called in a browser context, when `shopId`, `customerApiVersion`, or `defaultTimeoutInMs` is invalid, when no `fetch` is available, or when `requestContext.url` is missing or not HTTPS.\n */\nexport function createCustomerAccountClient({\n shopId,\n customerApiVersion = CUSTOMER_ACCOUNT_API_VERSION,\n requestContext,\n fetch: customFetch,\n defaultTimeoutInMs = DEFAULT_TIMEOUT_IN_MS,\n}: CreateCustomerAccountClientOptions): CustomerAccountClient {\n if (typeof document !== \"undefined\") {\n throw new Error(\n \"Customer Account API tokens cannot be used in a browser context. Use this client from server or edge routes only.\",\n );\n }\n\n validateShopId(shopId);\n validateCustomerApiVersion(customerApiVersion);\n validateTimeout(defaultTimeoutInMs);\n\n const resolvedFetch = customFetch ?? globalThis.fetch;\n if (typeof resolvedFetch !== \"function\") {\n throw new Error(\n \"No fetch function available. Pass a fetch option or ensure globalThis.fetch exists.\",\n );\n }\n\n const origin = getOrigin(requestContext.url);\n const apiUrl = `https://shopify.com/${shopId}/account/customer/api/${customerApiVersion}/graphql`;\n\n async function graphql<const Doc extends AnyCustomerAccountDocument>(\n document: Doc,\n ...rest: CustomerAccountGqlRestParam<Doc>\n ): Promise<CustomerAccountGraphqlResult<ResultOfDoc<Doc>>>;\n async function graphql(\n document: AnyCustomerAccountDocument,\n ...rest: [options?: CustomerAccountGraphqlOptions<unknown>]\n ): Promise<CustomerAccountGraphqlResult<unknown>> {\n assertCustomerAccountDocument(document);\n const options = validateGraphqlOptions(rest[0]);\n requestContext.markResponseAsPersonalized(CUSTOMER_ACCOUNT_GRAPHQL_PERSONALIZATION_REASON);\n const accessToken = validateAccessToken(options.accessToken);\n const externalSignals = [requestContext.signal, options.signal].filter(\n (signal): signal is AbortSignal => Boolean(signal),\n );\n throwIfAlreadyAborted(externalSignals);\n\n const { signal: timeoutSignal, cleanup: cleanupTimeout } =\n createCustomerAccountTimeoutSignal(defaultTimeoutInMs);\n externalSignals.push(timeoutSignal);\n const signal = AbortSignal.any(externalSignals);\n\n try {\n const variables = getVariables(\n document,\n getOptionalVariables(options.variables),\n requestContext.i18n.language,\n );\n const response = await fetchCustomerAccountGraphql({\n fetch: resolvedFetch,\n apiUrl,\n accessToken,\n origin,\n query: document.source,\n variables,\n signal,\n timeoutSignal,\n timeoutInMs: defaultTimeoutInMs,\n });\n\n return await readCustomerAccountGraphqlResponse(\n response,\n signal,\n timeoutSignal,\n defaultTimeoutInMs,\n );\n } finally {\n cleanupTimeout();\n }\n }\n\n return { apiUrl, graphql };\n}\n\nasync function fetchCustomerAccountGraphql({\n fetch,\n apiUrl,\n accessToken,\n origin,\n query,\n variables,\n signal,\n timeoutSignal,\n timeoutInMs,\n}: FetchCustomerAccountGraphqlParams): Promise<Response> {\n try {\n return await withAbort(\n fetch(apiUrl, {\n method: \"POST\",\n headers: new Headers({\n Authorization: accessToken,\n \"Content-Type\": \"application/json\",\n Origin: origin,\n \"User-Agent\": USER_AGENT,\n }),\n body: JSON.stringify({ query, variables }),\n cache: \"no-store\",\n redirect: \"manual\",\n signal,\n }),\n signal,\n );\n } catch (cause) {\n throwIfCustomerAccountSignalAborted(signal, timeoutSignal, timeoutInMs);\n if (isAbortError(cause)) throw cause;\n throw new CustomerAccountApiError(\"Customer Account API request failed\", { cause });\n }\n}\n\nasync function readCustomerAccountGraphqlResponse(\n response: Response,\n signal: AbortSignal,\n timeoutSignal: AbortSignal,\n timeoutInMs: number,\n): Promise<CustomerAccountGraphqlResult<unknown>> {\n const requestId = response.headers.get(\"x-request-id\") ?? undefined;\n if (!response.ok) {\n cancelResponseBody(response);\n throw new CustomerAccountApiError(`Customer Account API responded with ${response.status}`, {\n status: response.status,\n requestId,\n retryAfter: response.headers.get(\"retry-after\") ?? undefined,\n });\n }\n\n let body: { data?: unknown; errors?: GraphQLFormattedError[] };\n try {\n body = parseGraphqlResponse(await withAbort(response.json(), signal));\n } catch (cause) {\n cancelResponseBody(response);\n\n throwIfCustomerAccountSignalAborted(signal, timeoutSignal, timeoutInMs);\n if (isAbortError(cause)) throw cause;\n throw new CustomerAccountApiError(\"Failed to parse Customer Account API response as JSON\", {\n status: response.status,\n requestId,\n cause,\n });\n }\n\n if (body.errors) {\n return { data: body.data ?? null, errors: body.errors, headers: response.headers };\n }\n\n if (body.data == null) {\n throw new CustomerAccountApiError(\"Customer Account API response did not include data\", {\n status: response.status,\n requestId,\n });\n }\n\n return { data: body.data, headers: response.headers };\n}\n\nfunction cancelResponseBody(response: Response): void {\n try {\n void response.body?.cancel().catch(() => undefined);\n } catch {}\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 createCustomerAccountTimeoutSignal(timeoutInMs: number): {\n signal: AbortSignal;\n cleanup: () => void;\n} {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort(new CustomerAccountTimeoutError(timeoutInMs));\n }, timeoutInMs);\n\n return { signal: controller.signal, cleanup: () => clearTimeout(timeoutId) };\n}\n\nfunction withAbort<T>(promise: T | Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return Promise.resolve(promise);\n if (signal.aborted) {\n return Promise.reject(\n signal.reason ?? new DOMException(\"signal is aborted without reason\", \"AbortError\"),\n );\n }\n\n return new Promise<T>((resolve, reject) => {\n const abort = () => {\n signal.removeEventListener(\"abort\", abort);\n const reason =\n signal.reason ?? new DOMException(\"signal is aborted without reason\", \"AbortError\");\n reject(reason);\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 getVariables(\n document: { variableNames: ReadonlySet<string> },\n variables: Record<string, unknown>,\n language: ShopifyRequestContext[\"i18n\"][\"language\"],\n): Record<string, unknown> {\n if (!document.variableNames.has(\"language\")) return variables;\n if (\"language\" in variables) return variables;\n return { ...variables, language };\n}\n\nfunction throwIfAlreadyAborted(signals: AbortSignal[]): void {\n const abortedSignal = signals.find((signal) => signal.aborted);\n if (!abortedSignal) return;\n throw abortedSignal.reason ?? new DOMException(\"signal is aborted without reason\", \"AbortError\");\n}\n\nfunction throwIfCustomerAccountSignalAborted(\n signal: AbortSignal,\n timeoutSignal: AbortSignal,\n timeoutInMs: number,\n): void {\n if (!signal.aborted) return;\n if (timeoutSignal.aborted && signal.reason === timeoutSignal.reason) {\n const reason = timeoutSignal.reason;\n throw reason instanceof CustomerAccountTimeoutError\n ? reason\n : new CustomerAccountTimeoutError(timeoutInMs);\n }\n throw signal.reason ?? new DOMException(\"signal is aborted without reason\", \"AbortError\");\n}\n\nfunction isAbortError(cause: unknown): cause is DOMException {\n return cause instanceof DOMException && cause.name === \"AbortError\";\n}\n\nfunction parseGraphqlResponse(json: unknown): {\n data?: unknown;\n errors?: GraphQLFormattedError[];\n} {\n if (!isObjectRecord(json)) {\n throw new Error(\"Customer Account API returned unexpected JSON type\");\n }\n\n if (!(\"data\" in json) && !(\"errors\" in json)) {\n throw new Error(\"Customer Account API response must include data or errors\");\n }\n const errors = json.errors;\n if (errors !== undefined && !isGraphqlErrorArray(errors)) {\n throw new Error(\"Customer Account API returned invalid GraphQL errors\");\n }\n return { data: json.data, errors };\n}\n\nfunction isGraphqlErrorArray(value: unknown): value is GraphQLFormattedError[] {\n if (!Array.isArray(value)) return false;\n return value.every((error) => {\n return typeof error === \"object\" && error !== null && typeof error.message === \"string\";\n });\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 validateCustomerApiVersion(customerApiVersion: string): void {\n if (!CUSTOMER_API_VERSION_RE.test(customerApiVersion)) {\n throw new Error(\"customerApiVersion must use YYYY-MM format\");\n }\n}\n\nfunction validateAccessToken(accessToken: AccessToken): string {\n if (\n typeof accessToken !== \"string\" ||\n accessToken === \"\" ||\n accessToken.trim() !== accessToken ||\n hasAsciiControlCharacter(accessToken)\n ) {\n throw new CustomerAccountAuthenticationError();\n }\n return accessToken;\n}\n\nfunction validateGraphqlOptions(\n options: CustomerAccountGraphqlOptions<unknown> | undefined,\n): CustomerAccountGraphqlOptions<unknown> {\n if (typeof options !== \"object\" || options === null) {\n throw new CustomerAccountAuthenticationError();\n }\n return options;\n}\n\nfunction getOptionalVariables(variables: unknown): Record<string, unknown> {\n if (variables === undefined) return {};\n if (!isObjectRecord(variables)) {\n throw new TypeError(\"Customer Account API variables must be an object\");\n }\n return variables;\n}\n\nfunction hasAsciiControlCharacter(value: string): boolean {\n for (let index = 0; index < value.length; index++) {\n const codePoint = value.charCodeAt(index);\n if (codePoint <= MAX_ASCII_CONTROL_CODE_POINT || codePoint === ASCII_DELETE_CODE_POINT) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Customer Account API requires an HTTPS Origin, but local dev frameworks often\n * receive `http://localhost` requests. Keep that local-only exception without\n * weakening origin validation for non-local requests.\n */\nfunction getOrigin(requestUrl: string | undefined): string {\n if (!requestUrl) {\n throw new Error(\"requestContext.url is required for Customer Account API requests\");\n }\n\n const url = new URL(requestUrl);\n if (url.protocol === \"https:\") return url.origin;\n if (url.protocol === \"http:\" && LOCAL_ORIGIN_HOSTS.has(url.hostname)) {\n return `https://${url.host}`;\n }\n throw new Error(`Customer Account API origin must use HTTPS. Received: ${requestUrl}`);\n}\n"],"mappings":";;;;;;AAeA,MAAM,aAAa;AACnB,MAAM,0BAA0B;AAChC,MAAM,+BAA+B;AACrC,MAAM,0BAA0B;AAChC,MAAM,wBAAwB;AAC9B,MAAM,qBAAqB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAO;CAAQ,CAAC;AAC9E,MAAM,aAAa;AACnB,MAAM,kDAAkD;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6LxD,SAAgB,4BAA4B,EAC1C,QACA,qBAAqB,8BACrB,gBACA,OAAO,aACP,qBAAqB,yBACuC;AAC5D,KAAI,OAAO,aAAa,YACtB,OAAM,IAAI,MACR,oHACD;AAGH,gBAAe,OAAO;AACtB,4BAA2B,mBAAmB;AAC9C,iBAAgB,mBAAmB;CAEnC,MAAM,gBAAgB,eAAe,WAAW;AAChD,KAAI,OAAO,kBAAkB,WAC3B,OAAM,IAAI,MACR,sFACD;CAGH,MAAM,SAAS,UAAU,eAAe,IAAI;CAC5C,MAAM,SAAS,uBAAuB,OAAO,wBAAwB,mBAAmB;CAMxF,eAAe,QACb,UACA,GAAG,MAC6C;AAChD,gCAA8B,SAAS;EACvC,MAAM,UAAU,uBAAuB,KAAK,GAAG;AAC/C,iBAAe,2BAA2B,gDAAgD;EAC1F,MAAM,cAAc,oBAAoB,QAAQ,YAAY;EAC5D,MAAM,kBAAkB,CAAC,eAAe,QAAQ,QAAQ,OAAO,CAAC,QAC7D,WAAkC,QAAQ,OAAO,CACnD;AACD,wBAAsB,gBAAgB;EAEtC,MAAM,EAAE,QAAQ,eAAe,SAAS,mBACtC,mCAAmC,mBAAmB;AACxD,kBAAgB,KAAK,cAAc;EACnC,MAAM,SAAS,YAAY,IAAI,gBAAgB;AAE/C,MAAI;GACF,MAAM,YAAY,aAChB,UACA,qBAAqB,QAAQ,UAAU,EACvC,eAAe,KAAK,SACrB;AAaD,UAAO,MAAM,mCACX,MAbqB,4BAA4B;IACjD,OAAO;IACP;IACA;IACA;IACA,OAAO,SAAS;IAChB;IACA;IACA;IACA,aAAa;IACd,CAAC,EAIA,QACA,eACA,mBACD;YACO;AACR,mBAAgB;;;AAIpB,QAAO;EAAE;EAAQ;EAAS;;AAG5B,eAAe,4BAA4B,EACzC,OACA,QACA,aACA,QACA,OACA,WACA,QACA,eACA,eACuD;AACvD,KAAI;AACF,SAAO,MAAM,UACX,MAAM,QAAQ;GACZ,QAAQ;GACR,SAAS,IAAI,QAAQ;IACnB,eAAe;IACf,gBAAgB;IAChB,QAAQ;IACR,cAAc;IACf,CAAC;GACF,MAAM,KAAK,UAAU;IAAE;IAAO;IAAW,CAAC;GAC1C,OAAO;GACP,UAAU;GACV;GACD,CAAC,EACF,OACD;UACM,OAAO;AACd,sCAAoC,QAAQ,eAAe,YAAY;AACvE,MAAI,aAAa,MAAM,CAAE,OAAM;AAC/B,QAAM,IAAI,wBAAwB,uCAAuC,EAAE,OAAO,CAAC;;;AAIvF,eAAe,mCACb,UACA,QACA,eACA,aACgD;CAChD,MAAM,YAAY,SAAS,QAAQ,IAAI,eAAe,IAAI;AAC1D,KAAI,CAAC,SAAS,IAAI;AAChB,qBAAmB,SAAS;AAC5B,QAAM,IAAI,wBAAwB,uCAAuC,SAAS,UAAU;GAC1F,QAAQ,SAAS;GACjB;GACA,YAAY,SAAS,QAAQ,IAAI,cAAc,IAAI;GACpD,CAAC;;CAGJ,IAAI;AACJ,KAAI;AACF,SAAO,qBAAqB,MAAM,UAAU,SAAS,MAAM,EAAE,OAAO,CAAC;UAC9D,OAAO;AACd,qBAAmB,SAAS;AAE5B,sCAAoC,QAAQ,eAAe,YAAY;AACvE,MAAI,aAAa,MAAM,CAAE,OAAM;AAC/B,QAAM,IAAI,wBAAwB,yDAAyD;GACzF,QAAQ,SAAS;GACjB;GACA;GACD,CAAC;;AAGJ,KAAI,KAAK,OACP,QAAO;EAAE,MAAM,KAAK,QAAQ;EAAM,QAAQ,KAAK;EAAQ,SAAS,SAAS;EAAS;AAGpF,KAAI,KAAK,QAAQ,KACf,OAAM,IAAI,wBAAwB,sDAAsD;EACtF,QAAQ,SAAS;EACjB;EACD,CAAC;AAGJ,QAAO;EAAE,MAAM,KAAK;EAAM,SAAS,SAAS;EAAS;;AAGvD,SAAS,mBAAmB,UAA0B;AACpD,KAAI;AACF,EAAK,SAAS,MAAM,QAAQ,CAAC,YAAY,OAAU;SAC7C;;AAGV,SAAS,gBAAgB,aAA2B;AAClD,KACE,CAAC,OAAO,cAAc,YAAY,IAClC,eAAe,KACf,cAAc,sBAEd,OAAM,IAAI,MACR,sEAAsE,wBACvE;;AAIL,SAAS,mCAAmC,aAG1C;CACA,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB;AACjC,aAAW,MAAM,IAAI,4BAA4B,YAAY,CAAC;IAC7D,YAAY;AAEf,QAAO;EAAE,QAAQ,WAAW;EAAQ,eAAe,aAAa,UAAU;EAAE;;AAG9E,SAAS,UAAa,SAAyB,QAA6C;AAC1F,KAAI,CAAC,OAAQ,QAAO,QAAQ,QAAQ,QAAQ;AAC5C,KAAI,OAAO,QACT,QAAO,QAAQ,OACb,OAAO,UAAU,IAAI,aAAa,oCAAoC,aAAa,CACpF;AAGH,QAAO,IAAI,SAAY,SAAS,WAAW;EACzC,MAAM,cAAc;AAClB,UAAO,oBAAoB,SAAS,MAAM;AAG1C,UADE,OAAO,UAAU,IAAI,aAAa,oCAAoC,aAAa,CACvE;;AAEhB,SAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,MAAM,CAAC;AACvD,UAAQ,QAAQ,QAAQ,CAAC,MACtB,UAAU;AACT,UAAO,oBAAoB,SAAS,MAAM;AAC1C,WAAQ,MAAM;MAEf,UAAU;AACT,UAAO,oBAAoB,SAAS,MAAM;AAC1C,UAAO,MAAM;IAEhB;GACD;;AAGJ,SAAS,aACP,UACA,WACA,UACyB;AACzB,KAAI,CAAC,SAAS,cAAc,IAAI,WAAW,CAAE,QAAO;AACpD,KAAI,cAAc,UAAW,QAAO;AACpC,QAAO;EAAE,GAAG;EAAW;EAAU;;AAGnC,SAAS,sBAAsB,SAA8B;CAC3D,MAAM,gBAAgB,QAAQ,MAAM,WAAW,OAAO,QAAQ;AAC9D,KAAI,CAAC,cAAe;AACpB,OAAM,cAAc,UAAU,IAAI,aAAa,oCAAoC,aAAa;;AAGlG,SAAS,oCACP,QACA,eACA,aACM;AACN,KAAI,CAAC,OAAO,QAAS;AACrB,KAAI,cAAc,WAAW,OAAO,WAAW,cAAc,QAAQ;EACnE,MAAM,SAAS,cAAc;AAC7B,QAAM,kBAAkB,8BACpB,SACA,IAAI,4BAA4B,YAAY;;AAElD,OAAM,OAAO,UAAU,IAAI,aAAa,oCAAoC,aAAa;;AAG3F,SAAS,aAAa,OAAuC;AAC3D,QAAO,iBAAiB,gBAAgB,MAAM,SAAS;;AAGzD,SAAS,qBAAqB,MAG5B;AACA,KAAI,CAAC,eAAe,KAAK,CACvB,OAAM,IAAI,MAAM,qDAAqD;AAGvE,KAAI,EAAE,UAAU,SAAS,EAAE,YAAY,MACrC,OAAM,IAAI,MAAM,4DAA4D;CAE9E,MAAM,SAAS,KAAK;AACpB,KAAI,WAAW,UAAa,CAAC,oBAAoB,OAAO,CACtD,OAAM,IAAI,MAAM,uDAAuD;AAEzE,QAAO;EAAE,MAAM,KAAK;EAAM;EAAQ;;AAGpC,SAAS,oBAAoB,OAAkD;AAC7E,KAAI,CAAC,MAAM,QAAQ,MAAM,CAAE,QAAO;AAClC,QAAO,MAAM,OAAO,UAAU;AAC5B,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAO,MAAM,YAAY;GAC/E;;AAGJ,SAAS,eAAe,QAAsB;AAC5C,KAAI,CAAC,WAAW,KAAK,OAAO,CAC1B,OAAM,IAAI,MAAM,kDAAkD;;AAItE,SAAS,2BAA2B,oBAAkC;AACpE,KAAI,CAAC,wBAAwB,KAAK,mBAAmB,CACnD,OAAM,IAAI,MAAM,6CAA6C;;AAIjE,SAAS,oBAAoB,aAAkC;AAC7D,KACE,OAAO,gBAAgB,YACvB,gBAAgB,MAChB,YAAY,MAAM,KAAK,eACvB,yBAAyB,YAAY,CAErC,OAAM,IAAI,oCAAoC;AAEhD,QAAO;;AAGT,SAAS,uBACP,SACwC;AACxC,KAAI,OAAO,YAAY,YAAY,YAAY,KAC7C,OAAM,IAAI,oCAAoC;AAEhD,QAAO;;AAGT,SAAS,qBAAqB,WAA6C;AACzE,KAAI,cAAc,OAAW,QAAO,EAAE;AACtC,KAAI,CAAC,eAAe,UAAU,CAC5B,OAAM,IAAI,UAAU,mDAAmD;AAEzE,QAAO;;AAGT,SAAS,yBAAyB,OAAwB;AACxD,MAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS;EACjD,MAAM,YAAY,MAAM,WAAW,MAAM;AACzC,MAAI,aAAa,gCAAgC,cAAc,wBAC7D,QAAO;;AAGX,QAAO;;;;;;;AAQT,SAAS,UAAU,YAAwC;AACzD,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,mEAAmE;CAGrF,MAAM,MAAM,IAAI,IAAI,WAAW;AAC/B,KAAI,IAAI,aAAa,SAAU,QAAO,IAAI;AAC1C,KAAI,IAAI,aAAa,WAAW,mBAAmB,IAAI,IAAI,SAAS,CAClE,QAAO,WAAW,IAAI;AAExB,OAAM,IAAI,MAAM,yDAAyD,aAAa"}
@@ -1,7 +1,23 @@
1
1
  //#region src/customer-account/errors.d.ts
2
+ /**
3
+ * Base error for Customer Account API failures. Thrown by
4
+ * `CustomerAccountClient.graphql()` when the API responds with a non-OK
5
+ * HTTP status, when the response body cannot be parsed as JSON, or when the
6
+ * parsed response is missing `data`. Also thrown (without subclassing) for
7
+ * OAuth token-request timeouts and network failures.
8
+ *
9
+ * Not all fields are populated on every instance. `status` is present
10
+ * whenever an HTTP response was received. `requestId` is set when the
11
+ * `x-request-id` response header exists. Both are absent on network
12
+ * failures where no response arrived. `retryAfter` is populated only on
13
+ * non-OK responses.
14
+ */
2
15
  declare class CustomerAccountApiError extends Error {
16
+ /** HTTP status code from the API response. Present whenever an HTTP response was received (including parse failures), absent on network failures where no response arrived. */
3
17
  readonly status?: number;
18
+ /** Value of the `x-request-id` response header, when the API returned one. Useful for Shopify support requests. */
4
19
  readonly requestId?: string;
20
+ /** Value of the `retry-after` response header, when present. Indicates when the client should retry a rate-limited request. */
5
21
  readonly retryAfter?: string;
6
22
  constructor(message: string, options?: {
7
23
  status?: number;
@@ -10,14 +26,44 @@ declare class CustomerAccountApiError extends Error {
10
26
  cause?: unknown;
11
27
  });
12
28
  }
29
+ /**
30
+ * Client-side pre-flight check. Thrown when the access token is missing,
31
+ * empty, has leading or trailing whitespace, or contains ASCII control
32
+ * characters (including DEL). Also thrown when the options object is
33
+ * missing. A token the server rejects (e.g. expired or revoked) produces
34
+ * the base {@link CustomerAccountApiError} with `status: 401` instead.
35
+ */
13
36
  declare class CustomerAccountAuthenticationError extends CustomerAccountApiError {
14
37
  constructor(message?: string);
15
38
  }
39
+ /**
40
+ * Thrown when a `CustomerAccountClient.graphql()` request exceeds
41
+ * `defaultTimeoutInMs`. (OAuth token-request timeouts throw the base
42
+ * {@link CustomerAccountApiError} instead.) Extends
43
+ * {@link CustomerAccountApiError}.
44
+ */
16
45
  declare class CustomerAccountTimeoutError extends CustomerAccountApiError {
46
+ /** The timeout threshold (in milliseconds) that was exceeded. */
17
47
  readonly timeoutInMs: number;
18
48
  constructor(timeoutInMs: number);
19
49
  }
50
+ /**
51
+ * Thrown during the Customer Account OAuth authorization code exchange
52
+ * or id_token validation in `handleOAuthCallback`. Refresh failures never
53
+ * surface this error (they are caught internally and returned as
54
+ * `undefined`). Extends `Error` directly, **not**
55
+ * {@link CustomerAccountApiError}. A `catch` block for
56
+ * `CustomerAccountApiError` will not catch this error.
57
+ *
58
+ * Use the `code` field for programmatic handling. Known codes:
59
+ * `"missing_callback_params"`, `"state_mismatch"`,
60
+ * `"token_exchange_rejected"`, `"token_exchange_failed"`,
61
+ * `"invalid_token_response"`, `"nonce_mismatch"`, `"issuer_mismatch"`,
62
+ * `"audience_mismatch"`, `"expired_id_token"`, `"invalid_id_token"`,
63
+ * `"missing_pending_login"`.
64
+ */
20
65
  declare class CustomerAccountOAuthError extends Error {
66
+ /** Machine-readable error identifier for programmatic handling. */
21
67
  readonly code: string;
22
68
  constructor(code: string, message: string, options?: {
23
69
  cause?: unknown;