@vendure-platform/create 0.5.0 → 1.0.0-major.202609091053.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +53 -41
  3. package/dist/add-to-existing.d.ts +19 -13
  4. package/dist/add-to-existing.js +1 -1
  5. package/dist/assets/demo-data/assets/branding/logo.svg +4 -0
  6. package/dist/assets/demo-data/assets/images/absorbent-pad.webp +0 -0
  7. package/dist/assets/demo-data/assets/images/adjustable-wrench.webp +0 -0
  8. package/dist/assets/demo-data/assets/images/anchor-bolt.webp +0 -0
  9. package/dist/assets/demo-data/assets/images/angle-grinder.webp +0 -0
  10. package/dist/assets/demo-data/assets/images/barrier-tape.webp +0 -0
  11. package/dist/assets/demo-data/assets/images/bench-grinder.webp +0 -0
  12. package/dist/assets/demo-data/assets/images/bracket.webp +0 -0
  13. package/dist/assets/demo-data/assets/images/broom.webp +0 -0
  14. package/dist/assets/demo-data/assets/images/bucket.webp +0 -0
  15. package/dist/assets/demo-data/assets/images/cable-gland.webp +0 -0
  16. package/dist/assets/demo-data/assets/images/cable-tie.webp +0 -0
  17. package/dist/assets/demo-data/assets/images/cable-tray.webp +0 -0
  18. package/dist/assets/demo-data/assets/images/cable.webp +0 -0
  19. package/dist/assets/demo-data/assets/images/chimney-base-kit.webp +0 -0
  20. package/dist/assets/demo-data/assets/images/chimney-cowl-bird-guard.webp +0 -0
  21. package/dist/assets/demo-data/assets/images/chimney-cowl-open.webp +0 -0
  22. package/dist/assets/demo-data/assets/images/chimney-storm-collar.webp +0 -0
  23. package/dist/assets/demo-data/assets/images/chimney-wall-bracket.webp +0 -0
  24. package/dist/assets/demo-data/assets/images/circular-saw.webp +0 -0
  25. package/dist/assets/demo-data/assets/images/cleaning-spray.webp +0 -0
  26. package/dist/assets/demo-data/assets/images/conduit.webp +0 -0
  27. package/dist/assets/demo-data/assets/images/cordless-drill.webp +0 -0
  28. package/dist/assets/demo-data/assets/images/drill-bits.webp +0 -0
  29. package/dist/assets/demo-data/assets/images/dust-mask.webp +0 -0
  30. package/dist/assets/demo-data/assets/images/ear-defenders.webp +0 -0
  31. package/dist/assets/demo-data/assets/images/ear-plugs.webp +0 -0
  32. package/dist/assets/demo-data/assets/images/extension-lead.webp +0 -0
  33. package/dist/assets/demo-data/assets/images/eye-wash-station.webp +0 -0
  34. package/dist/assets/demo-data/assets/images/face-shield.webp +0 -0
  35. package/dist/assets/demo-data/assets/images/file-set.webp +0 -0
  36. package/dist/assets/demo-data/assets/images/first-aid-kit.webp +0 -0
  37. package/dist/assets/demo-data/assets/images/floor-sign.webp +0 -0
  38. package/dist/assets/demo-data/assets/images/flue-bend-45.webp +0 -0
  39. package/dist/assets/demo-data/assets/images/flue-bend-90.webp +0 -0
  40. package/dist/assets/demo-data/assets/images/flue-section-1000mm.webp +0 -0
  41. package/dist/assets/demo-data/assets/images/flue-section-250mm.webp +0 -0
  42. package/dist/assets/demo-data/assets/images/flue-section-500mm.webp +0 -0
  43. package/dist/assets/demo-data/assets/images/fuse.webp +0 -0
  44. package/dist/assets/demo-data/assets/images/glove-dispenser.webp +0 -0
  45. package/dist/assets/demo-data/assets/images/grinding-disc.webp +0 -0
  46. package/dist/assets/demo-data/assets/images/hacksaw.webp +0 -0
  47. package/dist/assets/demo-data/assets/images/hammer.webp +0 -0
  48. package/dist/assets/demo-data/assets/images/hand-sanitiser.webp +0 -0
  49. package/dist/assets/demo-data/assets/images/head-torch.webp +0 -0
  50. package/dist/assets/demo-data/assets/images/heat-gun.webp +0 -0
  51. package/dist/assets/demo-data/assets/images/hex-bolt.webp +0 -0
  52. package/dist/assets/demo-data/assets/images/hex-key-set.webp +0 -0
  53. package/dist/assets/demo-data/assets/images/hex-nut.webp +0 -0
  54. package/dist/assets/demo-data/assets/images/hi-vis-jacket.webp +0 -0
  55. package/dist/assets/demo-data/assets/images/hi-vis-vest.webp +0 -0
  56. package/dist/assets/demo-data/assets/images/hose-clamp.webp +0 -0
  57. package/dist/assets/demo-data/assets/images/impact-driver.webp +0 -0
  58. package/dist/assets/demo-data/assets/images/jigsaw.webp +0 -0
  59. package/dist/assets/demo-data/assets/images/junction-box.webp +0 -0
  60. package/dist/assets/demo-data/assets/images/knee-pads.webp +0 -0
  61. package/dist/assets/demo-data/assets/images/machine-screw.webp +0 -0
  62. package/dist/assets/demo-data/assets/images/made-to-order-configurable-chimney.webp +0 -0
  63. package/dist/assets/demo-data/assets/images/mallet.webp +0 -0
  64. package/dist/assets/demo-data/assets/images/mcb.webp +0 -0
  65. package/dist/assets/demo-data/assets/images/mop.webp +0 -0
  66. package/dist/assets/demo-data/assets/images/paper-towels.webp +0 -0
  67. package/dist/assets/demo-data/assets/images/pipe-wrench.webp +0 -0
  68. package/dist/assets/demo-data/assets/images/pliers-set.webp +0 -0
  69. package/dist/assets/demo-data/assets/images/pry-bar.webp +0 -0
  70. package/dist/assets/demo-data/assets/images/rcd.webp +0 -0
  71. package/dist/assets/demo-data/assets/images/reciprocating-saw.webp +0 -0
  72. package/dist/assets/demo-data/assets/images/rivet.webp +0 -0
  73. package/dist/assets/demo-data/assets/images/roof-flashing-high-pitch.webp +0 -0
  74. package/dist/assets/demo-data/assets/images/roof-flashing-low-pitch.webp +0 -0
  75. package/dist/assets/demo-data/assets/images/safety-boots.webp +0 -0
  76. package/dist/assets/demo-data/assets/images/safety-glasses.webp +0 -0
  77. package/dist/assets/demo-data/assets/images/safety-harness.webp +0 -0
  78. package/dist/assets/demo-data/assets/images/safety-helmet.webp +0 -0
  79. package/dist/assets/demo-data/assets/images/safety-sign.webp +0 -0
  80. package/dist/assets/demo-data/assets/images/sanding-disc.webp +0 -0
  81. package/dist/assets/demo-data/assets/images/saw-blade.webp +0 -0
  82. package/dist/assets/demo-data/assets/images/screwdriver-set.webp +0 -0
  83. package/dist/assets/demo-data/assets/images/screwdriver.webp +0 -0
  84. package/dist/assets/demo-data/assets/images/self-tapping-screw.webp +0 -0
  85. package/dist/assets/demo-data/assets/images/socket-set.webp +0 -0
  86. package/dist/assets/demo-data/assets/images/socket.webp +0 -0
  87. package/dist/assets/demo-data/assets/images/spanner-set.webp +0 -0
  88. package/dist/assets/demo-data/assets/images/spanner.webp +0 -0
  89. package/dist/assets/demo-data/assets/images/spill-kit.webp +0 -0
  90. package/dist/assets/demo-data/assets/images/spirit-level.webp +0 -0
  91. package/dist/assets/demo-data/assets/images/switch.webp +0 -0
  92. package/dist/assets/demo-data/assets/images/tape-measure.webp +0 -0
  93. package/dist/assets/demo-data/assets/images/terminal-block.webp +0 -0
  94. package/dist/assets/demo-data/assets/images/threaded-rod.webp +0 -0
  95. package/dist/assets/demo-data/assets/images/torque-wrench.webp +0 -0
  96. package/dist/assets/demo-data/assets/images/utility-knife.webp +0 -0
  97. package/dist/assets/demo-data/assets/images/wall-pass-sleeve.webp +0 -0
  98. package/dist/assets/demo-data/assets/images/wall-plug.webp +0 -0
  99. package/dist/assets/demo-data/assets/images/washer.webp +0 -0
  100. package/dist/assets/demo-data/assets/images/waste-bin.webp +0 -0
  101. package/dist/assets/demo-data/assets/images/wire-connector.webp +0 -0
  102. package/dist/assets/demo-data/assets/images/wire-cutters.webp +0 -0
  103. package/dist/assets/demo-data/assets/images/wood-screw.webp +0 -0
  104. package/dist/assets/demo-data/assets/images/work-gloves.webp +0 -0
  105. package/dist/assets/demo-data/assets/images/work-light.webp +0 -0
  106. package/dist/assets/demo-data/assets/initial-data.json +667 -0
  107. package/dist/assets/demo-data/assets/products.csv +779 -0
  108. package/dist/assets/demo-data/initial-data.json +663 -663
  109. package/dist/assets/storefront/components.json +20 -20
  110. package/dist/assets/storefront/eslint.config.mjs +10 -10
  111. package/dist/assets/storefront/graphql.config.yml +1 -1
  112. package/dist/assets/storefront/next-env.d.ts +2 -1
  113. package/dist/assets/storefront/next.config.ts +19 -17
  114. package/dist/assets/storefront/package.json +3 -2
  115. package/dist/assets/storefront/postcss.config.mjs +1 -1
  116. package/dist/assets/storefront/src/app/[locale]/(catalog)/catalog/page.tsx +17 -21
  117. package/dist/assets/storefront/src/app/[locale]/(catalog)/collection/[slug]/page.tsx +64 -42
  118. package/dist/assets/storefront/src/app/[locale]/(catalog)/layout.tsx +1 -5
  119. package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/actions.ts +69 -65
  120. package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/page.tsx +64 -38
  121. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/page.tsx +24 -27
  122. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-results.tsx +11 -9
  123. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-term.tsx +4 -4
  124. package/dist/assets/storefront/src/app/[locale]/(scenario)/[...path]/page.tsx +1 -1
  125. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/accept-invitation-client.tsx +8 -13
  126. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/actions.ts +11 -14
  127. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/page.tsx +30 -33
  128. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/sign-out-and-retry.tsx +6 -6
  129. package/dist/assets/storefront/src/app/[locale]/account/addresses/actions.ts +30 -45
  130. package/dist/assets/storefront/src/app/[locale]/account/addresses/address-form.tsx +166 -159
  131. package/dist/assets/storefront/src/app/[locale]/account/addresses/addresses-client.tsx +35 -26
  132. package/dist/assets/storefront/src/app/[locale]/account/addresses/loading.tsx +1 -3
  133. package/dist/assets/storefront/src/app/[locale]/account/addresses/page.tsx +18 -18
  134. package/dist/assets/storefront/src/app/[locale]/account/approvals/actions.ts +5 -8
  135. package/dist/assets/storefront/src/app/[locale]/account/approvals/page.tsx +79 -69
  136. package/dist/assets/storefront/src/app/[locale]/account/company/actions.ts +10 -11
  137. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/actions.ts +6 -15
  138. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/address-form.tsx +57 -49
  139. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/addresses-client.tsx +23 -25
  140. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/page.tsx +30 -30
  141. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/actions.ts +20 -29
  142. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-unit-form.tsx +32 -37
  143. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-units-client.tsx +32 -25
  144. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/page.tsx +26 -24
  145. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/types.ts +7 -4
  146. package/dist/assets/storefront/src/app/[locale]/account/company/edit-company-form.tsx +11 -21
  147. package/dist/assets/storefront/src/app/[locale]/account/company/page.tsx +25 -28
  148. package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/page.tsx +26 -24
  149. package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/payment-methods-client.tsx +5 -7
  150. package/dist/assets/storefront/src/app/[locale]/account/company/roles/page.tsx +24 -25
  151. package/dist/assets/storefront/src/app/[locale]/account/company/roles/roles-client.tsx +11 -26
  152. package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/actions.ts +6 -10
  153. package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/page.tsx +18 -16
  154. package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/tax-ids-client.tsx +74 -67
  155. package/dist/assets/storefront/src/app/[locale]/account/company/users/actions.ts +16 -25
  156. package/dist/assets/storefront/src/app/[locale]/account/company/users/edit-user-form.tsx +27 -25
  157. package/dist/assets/storefront/src/app/[locale]/account/company/users/invite-user-form.tsx +27 -19
  158. package/dist/assets/storefront/src/app/[locale]/account/company/users/page.tsx +34 -29
  159. package/dist/assets/storefront/src/app/[locale]/account/company/users/users-client.tsx +35 -38
  160. package/dist/assets/storefront/src/app/[locale]/account/error.tsx +1 -3
  161. package/dist/assets/storefront/src/app/[locale]/account/layout.tsx +16 -22
  162. package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/page.tsx +151 -103
  163. package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-actions.ts +6 -7
  164. package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-dialog.tsx +22 -48
  165. package/dist/assets/storefront/src/app/[locale]/account/orders/loading.tsx +8 -8
  166. package/dist/assets/storefront/src/app/[locale]/account/orders/page.tsx +101 -61
  167. package/dist/assets/storefront/src/app/[locale]/account/profile/actions.ts +32 -23
  168. package/dist/assets/storefront/src/app/[locale]/account/profile/change-password-form.tsx +6 -16
  169. package/dist/assets/storefront/src/app/[locale]/account/profile/edit-email-form.tsx +7 -17
  170. package/dist/assets/storefront/src/app/[locale]/account/profile/edit-profile-form.tsx +6 -16
  171. package/dist/assets/storefront/src/app/[locale]/account/profile/loading.tsx +12 -18
  172. package/dist/assets/storefront/src/app/[locale]/account/profile/page.tsx +17 -15
  173. package/dist/assets/storefront/src/app/[locale]/account/quick-order/file-upload-tab.tsx +2 -6
  174. package/dist/assets/storefront/src/app/[locale]/account/quick-order/manual-entry-tab.tsx +18 -15
  175. package/dist/assets/storefront/src/app/[locale]/account/quick-order/page.tsx +5 -6
  176. package/dist/assets/storefront/src/app/[locale]/account/quick-order/preview-table.tsx +10 -2
  177. package/dist/assets/storefront/src/app/[locale]/account/quick-order/save-to-list-dialog.tsx +24 -26
  178. package/dist/assets/storefront/src/app/[locale]/account/quick-order/sku-autocomplete.tsx +19 -10
  179. package/dist/assets/storefront/src/app/[locale]/account/quick-order/text-input-tab.tsx +3 -8
  180. package/dist/assets/storefront/src/app/[locale]/account/quotes/[id]/page.tsx +93 -62
  181. package/dist/assets/storefront/src/app/[locale]/account/quotes/actions.ts +38 -45
  182. package/dist/assets/storefront/src/app/[locale]/account/quotes/new/new-quote-form.tsx +3 -5
  183. package/dist/assets/storefront/src/app/[locale]/account/quotes/new/page.tsx +6 -6
  184. package/dist/assets/storefront/src/app/[locale]/account/quotes/page.tsx +92 -59
  185. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/add-item-search.tsx +11 -9
  186. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/add-member-form.tsx +31 -15
  187. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/members-panel.tsx +52 -33
  188. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/page.tsx +22 -14
  189. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/rename-dialog.tsx +34 -13
  190. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/actions.ts +117 -72
  191. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/new/new-shared-cart-form.tsx +21 -14
  192. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/new/page.tsx +10 -10
  193. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/page.tsx +54 -41
  194. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/page.tsx +22 -8
  195. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/shopping-list-detail-client.tsx +62 -49
  196. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/actions.ts +9 -4
  197. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/create-shopping-list-form.tsx +5 -11
  198. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/loading.tsx +1 -1
  199. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/page.tsx +32 -42
  200. package/dist/assets/storefront/src/app/[locale]/account/store-credits/actions.ts +8 -8
  201. package/dist/assets/storefront/src/app/[locale]/account/store-credits/claim-form.tsx +6 -10
  202. package/dist/assets/storefront/src/app/[locale]/account/store-credits/page.tsx +76 -55
  203. package/dist/assets/storefront/src/app/[locale]/account/store-credits/reveal-code.tsx +10 -6
  204. package/dist/assets/storefront/src/app/[locale]/account/verify-email/page.tsx +32 -39
  205. package/dist/assets/storefront/src/app/[locale]/cart/actions.ts +29 -23
  206. package/dist/assets/storefront/src/app/[locale]/cart/cart-items.tsx +111 -40
  207. package/dist/assets/storefront/src/app/[locale]/cart/cart-line-actions.tsx +9 -9
  208. package/dist/assets/storefront/src/app/[locale]/cart/cart-quantity-control.tsx +61 -48
  209. package/dist/assets/storefront/src/app/[locale]/cart/cart-remove-button.tsx +15 -9
  210. package/dist/assets/storefront/src/app/[locale]/cart/cart.tsx +38 -41
  211. package/dist/assets/storefront/src/app/[locale]/cart/code-input.tsx +8 -8
  212. package/dist/assets/storefront/src/app/[locale]/cart/order-summary.tsx +56 -35
  213. package/dist/assets/storefront/src/app/[locale]/cart/page.tsx +11 -11
  214. package/dist/assets/storefront/src/app/[locale]/cart/promotion-code.tsx +56 -47
  215. package/dist/assets/storefront/src/app/[locale]/cart/reapproval-confirm.tsx +22 -20
  216. package/dist/assets/storefront/src/app/[locale]/cart/request-quote-button.tsx +7 -16
  217. package/dist/assets/storefront/src/app/[locale]/checkout/actions.ts +39 -65
  218. package/dist/assets/storefront/src/app/[locale]/checkout/cancel-quote-checkout-button.tsx +5 -5
  219. package/dist/assets/storefront/src/app/[locale]/checkout/checkout-flow.tsx +202 -202
  220. package/dist/assets/storefront/src/app/[locale]/checkout/checkout-provider.tsx +142 -142
  221. package/dist/assets/storefront/src/app/[locale]/checkout/order-summary.tsx +158 -139
  222. package/dist/assets/storefront/src/app/[locale]/checkout/page.tsx +58 -47
  223. package/dist/assets/storefront/src/app/[locale]/checkout/steps/delivery-step.tsx +86 -88
  224. package/dist/assets/storefront/src/app/[locale]/checkout/steps/payment-step.tsx +90 -91
  225. package/dist/assets/storefront/src/app/[locale]/checkout/steps/review-step.tsx +166 -161
  226. package/dist/assets/storefront/src/app/[locale]/checkout/steps/shipping-address-step.tsx +573 -494
  227. package/dist/assets/storefront/src/app/[locale]/error.tsx +3 -5
  228. package/dist/assets/storefront/src/app/[locale]/forgot-password/actions.ts +10 -7
  229. package/dist/assets/storefront/src/app/[locale]/forgot-password/forgot-password-form.tsx +10 -25
  230. package/dist/assets/storefront/src/app/[locale]/forgot-password/page.tsx +5 -5
  231. package/dist/assets/storefront/src/app/[locale]/globals.css +48 -48
  232. package/dist/assets/storefront/src/app/[locale]/layout.tsx +56 -50
  233. package/dist/assets/storefront/src/app/[locale]/not-found.tsx +3 -5
  234. package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/order-confirmation.tsx +71 -102
  235. package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/page.tsx +7 -5
  236. package/dist/assets/storefront/src/app/[locale]/page.tsx +33 -35
  237. package/dist/assets/storefront/src/app/[locale]/register/actions.ts +7 -7
  238. package/dist/assets/storefront/src/app/[locale]/register/page.tsx +22 -16
  239. package/dist/assets/storefront/src/app/[locale]/register/registration-form.tsx +22 -34
  240. package/dist/assets/storefront/src/app/[locale]/register-company/actions.ts +15 -19
  241. package/dist/assets/storefront/src/app/[locale]/register-company/page.tsx +17 -15
  242. package/dist/assets/storefront/src/app/[locale]/register-company/registration-form.tsx +9 -15
  243. package/dist/assets/storefront/src/app/[locale]/reset-password/actions.ts +7 -8
  244. package/dist/assets/storefront/src/app/[locale]/reset-password/page.tsx +18 -12
  245. package/dist/assets/storefront/src/app/[locale]/reset-password/reset-password-form.tsx +8 -16
  246. package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/add-to-cart-button.tsx +10 -4
  247. package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/page.tsx +62 -30
  248. package/dist/assets/storefront/src/app/[locale]/sign-in/actions.ts +14 -16
  249. package/dist/assets/storefront/src/app/[locale]/sign-in/login-form.tsx +17 -30
  250. package/dist/assets/storefront/src/app/[locale]/sign-in/page.tsx +35 -30
  251. package/dist/assets/storefront/src/app/[locale]/verify/actions.ts +8 -8
  252. package/dist/assets/storefront/src/app/[locale]/verify/page.tsx +18 -14
  253. package/dist/assets/storefront/src/app/[locale]/verify/verify-content.tsx +25 -27
  254. package/dist/assets/storefront/src/app/[locale]/verify-pending/page.tsx +23 -23
  255. package/dist/assets/storefront/src/app/api/clear-company-context/route.ts +6 -3
  256. package/dist/assets/storefront/src/app/api/revalidate/route.ts +19 -28
  257. package/dist/assets/storefront/src/app/api/search-analytics/route.ts +1 -1
  258. package/dist/assets/storefront/src/components/commerce/account-nav-items.ts +43 -18
  259. package/dist/assets/storefront/src/components/commerce/account-sidebar.tsx +30 -22
  260. package/dist/assets/storefront/src/components/commerce/active-filters.tsx +1 -1
  261. package/dist/assets/storefront/src/components/commerce/add-to-shared-cart.tsx +49 -27
  262. package/dist/assets/storefront/src/components/commerce/approval-decision-form.tsx +9 -13
  263. package/dist/assets/storefront/src/components/commerce/approval-status-badge.tsx +23 -15
  264. package/dist/assets/storefront/src/components/commerce/business-unit-switcher.tsx +11 -11
  265. package/dist/assets/storefront/src/components/commerce/catalog-search-bar.tsx +2 -2
  266. package/dist/assets/storefront/src/components/commerce/category-product-section.tsx +28 -16
  267. package/dist/assets/storefront/src/components/commerce/category-sidebar.tsx +31 -18
  268. package/dist/assets/storefront/src/components/commerce/collection-hero-banner.tsx +2 -4
  269. package/dist/assets/storefront/src/components/commerce/company-state-badge.tsx +10 -16
  270. package/dist/assets/storefront/src/components/commerce/company-switcher.tsx +12 -12
  271. package/dist/assets/storefront/src/components/commerce/configurator-form.tsx +188 -79
  272. package/dist/assets/storefront/src/components/commerce/facet-filters.tsx +29 -49
  273. package/dist/assets/storefront/src/components/commerce/gift-card-pdp.tsx +4 -4
  274. package/dist/assets/storefront/src/components/commerce/gift-card-product-info.tsx +20 -30
  275. package/dist/assets/storefront/src/components/commerce/instant-search-dropdown.tsx +258 -227
  276. package/dist/assets/storefront/src/components/commerce/order-status-badge.tsx +27 -19
  277. package/dist/assets/storefront/src/components/commerce/packaging-unit-selector.tsx +27 -26
  278. package/dist/assets/storefront/src/components/commerce/permission-gate.tsx +4 -4
  279. package/dist/assets/storefront/src/components/commerce/price-range-filter.tsx +5 -5
  280. package/dist/assets/storefront/src/components/commerce/price-tiers.tsx +38 -21
  281. package/dist/assets/storefront/src/components/commerce/price.tsx +3 -3
  282. package/dist/assets/storefront/src/components/commerce/product-card-list.tsx +39 -16
  283. package/dist/assets/storefront/src/components/commerce/product-card.tsx +35 -18
  284. package/dist/assets/storefront/src/components/commerce/product-carousel.tsx +20 -12
  285. package/dist/assets/storefront/src/components/commerce/product-grid.tsx +14 -13
  286. package/dist/assets/storefront/src/components/commerce/product-image-carousel.tsx +10 -8
  287. package/dist/assets/storefront/src/components/commerce/product-info.tsx +118 -74
  288. package/dist/assets/storefront/src/components/commerce/product-list-expansion.tsx +102 -74
  289. package/dist/assets/storefront/src/components/commerce/product-list-view.tsx +1 -1
  290. package/dist/assets/storefront/src/components/commerce/product-sidebar.tsx +36 -11
  291. package/dist/assets/storefront/src/components/commerce/product-view-switcher.tsx +33 -21
  292. package/dist/assets/storefront/src/components/commerce/quote-action-buttons.tsx +80 -45
  293. package/dist/assets/storefront/src/components/commerce/quote-activity-timeline.tsx +126 -50
  294. package/dist/assets/storefront/src/components/commerce/quote-comment-form.tsx +9 -13
  295. package/dist/assets/storefront/src/components/commerce/quote-csv-export-button.tsx +6 -6
  296. package/dist/assets/storefront/src/components/commerce/quote-product-search.tsx +22 -18
  297. package/dist/assets/storefront/src/components/commerce/quote-request-form.tsx +28 -27
  298. package/dist/assets/storefront/src/components/commerce/quote-status-badge.tsx +26 -18
  299. package/dist/assets/storefront/src/components/commerce/quote-validity-indicator.tsx +7 -7
  300. package/dist/assets/storefront/src/components/commerce/quote-version-comparison.tsx +50 -30
  301. package/dist/assets/storefront/src/components/commerce/related-products.tsx +37 -28
  302. package/dist/assets/storefront/src/components/commerce/shared-cart-status-badge.tsx +12 -8
  303. package/dist/assets/storefront/src/components/commerce/shop-by-category.tsx +8 -7
  304. package/dist/assets/storefront/src/components/commerce/shopping-list-toggle.tsx +14 -20
  305. package/dist/assets/storefront/src/components/commerce/sort-dropdown.tsx +9 -16
  306. package/dist/assets/storefront/src/components/commerce/store-credit-summary.tsx +30 -32
  307. package/dist/assets/storefront/src/components/commerce/subcollection-tiles.tsx +11 -13
  308. package/dist/assets/storefront/src/components/layout/auth-page-shell.tsx +2 -2
  309. package/dist/assets/storefront/src/components/layout/draft-mode-indicator.tsx +2 -7
  310. package/dist/assets/storefront/src/components/layout/draft-mode-link.tsx +4 -2
  311. package/dist/assets/storefront/src/components/layout/footer.tsx +70 -28
  312. package/dist/assets/storefront/src/components/layout/navbar/actions.ts +9 -12
  313. package/dist/assets/storefront/src/components/layout/navbar/cart-icon.tsx +9 -9
  314. package/dist/assets/storefront/src/components/layout/navbar/channel-picker-wrapper.tsx +4 -9
  315. package/dist/assets/storefront/src/components/layout/navbar/channel-picker.tsx +13 -8
  316. package/dist/assets/storefront/src/components/layout/navbar/currency-picker-wrapper.tsx +4 -4
  317. package/dist/assets/storefront/src/components/layout/navbar/currency-picker.tsx +11 -6
  318. package/dist/assets/storefront/src/components/layout/navbar/language-picker.tsx +7 -7
  319. package/dist/assets/storefront/src/components/layout/navbar/login-button.tsx +24 -21
  320. package/dist/assets/storefront/src/components/layout/navbar/mobile-nav.tsx +8 -8
  321. package/dist/assets/storefront/src/components/layout/navbar/nav-menu-items.tsx +4 -6
  322. package/dist/assets/storefront/src/components/layout/navbar/navbar-cart.tsx +1 -1
  323. package/dist/assets/storefront/src/components/layout/navbar/navbar-collections.tsx +41 -42
  324. package/dist/assets/storefront/src/components/layout/navbar/navbar-link.tsx +7 -10
  325. package/dist/assets/storefront/src/components/layout/navbar/navbar-user.tsx +18 -23
  326. package/dist/assets/storefront/src/components/layout/navbar/theme-switcher.tsx +9 -9
  327. package/dist/assets/storefront/src/components/layout/navbar.tsx +36 -31
  328. package/dist/assets/storefront/src/components/layout/search-input.tsx +35 -32
  329. package/dist/assets/storefront/src/components/providers/awaiting-approval-banner.tsx +12 -10
  330. package/dist/assets/storefront/src/components/providers/cart-count-server.tsx +14 -12
  331. package/dist/assets/storefront/src/components/providers/company-context-guard.tsx +4 -4
  332. package/dist/assets/storefront/src/components/providers/company-permissions-server.tsx +2 -6
  333. package/dist/assets/storefront/src/components/providers/theme-provider.tsx +3 -3
  334. package/dist/assets/storefront/src/components/shared/breadcrumbs.tsx +4 -1
  335. package/dist/assets/storefront/src/components/shared/country-select.tsx +63 -67
  336. package/dist/assets/storefront/src/components/shared/pagination.tsx +15 -28
  337. package/dist/assets/storefront/src/components/shared/prefetch-link.tsx +1 -1
  338. package/dist/assets/storefront/src/components/shared/skeletons/search-input-skeleton.tsx +1 -3
  339. package/dist/assets/storefront/src/components/ui/accordion.tsx +46 -51
  340. package/dist/assets/storefront/src/components/ui/alert-dialog.tsx +94 -121
  341. package/dist/assets/storefront/src/components/ui/alert.tsx +48 -54
  342. package/dist/assets/storefront/src/components/ui/aspect-ratio.tsx +5 -7
  343. package/dist/assets/storefront/src/components/ui/avatar.tsx +29 -44
  344. package/dist/assets/storefront/src/components/ui/badge.tsx +28 -37
  345. package/dist/assets/storefront/src/components/ui/breadcrumb.tsx +82 -89
  346. package/dist/assets/storefront/src/components/ui/button.tsx +43 -49
  347. package/dist/assets/storefront/src/components/ui/calendar.tsx +157 -198
  348. package/dist/assets/storefront/src/components/ui/card.tsx +53 -76
  349. package/dist/assets/storefront/src/components/ui/carousel.tsx +194 -212
  350. package/dist/assets/storefront/src/components/ui/chart.tsx +274 -317
  351. package/dist/assets/storefront/src/components/ui/checkbox.tsx +24 -27
  352. package/dist/assets/storefront/src/components/ui/collapsible.tsx +9 -21
  353. package/dist/assets/storefront/src/components/ui/command.tsx +114 -159
  354. package/dist/assets/storefront/src/components/ui/context-menu.tsx +167 -199
  355. package/dist/assets/storefront/src/components/ui/dialog.tsx +97 -114
  356. package/dist/assets/storefront/src/components/ui/drawer.tsx +93 -107
  357. package/dist/assets/storefront/src/components/ui/dropdown-menu.tsx +171 -206
  358. package/dist/assets/storefront/src/components/ui/empty-state.tsx +5 -5
  359. package/dist/assets/storefront/src/components/ui/field.tsx +196 -208
  360. package/dist/assets/storefront/src/components/ui/form.tsx +115 -139
  361. package/dist/assets/storefront/src/components/ui/hover-card.tsx +27 -33
  362. package/dist/assets/storefront/src/components/ui/input-otp.tsx +50 -59
  363. package/dist/assets/storefront/src/components/ui/input.tsx +17 -17
  364. package/dist/assets/storefront/src/components/ui/kbd-shortcut.tsx +3 -1
  365. package/dist/assets/storefront/src/components/ui/label.tsx +16 -19
  366. package/dist/assets/storefront/src/components/ui/menubar.tsx +193 -223
  367. package/dist/assets/storefront/src/components/ui/navigation-menu.tsx +123 -130
  368. package/dist/assets/storefront/src/components/ui/pagination.tsx +87 -108
  369. package/dist/assets/storefront/src/components/ui/popover.tsx +29 -35
  370. package/dist/assets/storefront/src/components/ui/progress.tsx +19 -26
  371. package/dist/assets/storefront/src/components/ui/radio-group.tsx +32 -38
  372. package/dist/assets/storefront/src/components/ui/resizable.tsx +37 -42
  373. package/dist/assets/storefront/src/components/ui/scroll-area.tsx +41 -47
  374. package/dist/assets/storefront/src/components/ui/select.tsx +130 -152
  375. package/dist/assets/storefront/src/components/ui/separator.tsx +21 -21
  376. package/dist/assets/storefront/src/components/ui/sheet.tsx +90 -111
  377. package/dist/assets/storefront/src/components/ui/sidebar.tsx +611 -649
  378. package/dist/assets/storefront/src/components/ui/skeleton.tsx +10 -10
  379. package/dist/assets/storefront/src/components/ui/slider.tsx +50 -55
  380. package/dist/assets/storefront/src/components/ui/sonner.tsx +20 -20
  381. package/dist/assets/storefront/src/components/ui/switch.tsx +23 -26
  382. package/dist/assets/storefront/src/components/ui/table.tsx +65 -97
  383. package/dist/assets/storefront/src/components/ui/tabs.tsx +39 -55
  384. package/dist/assets/storefront/src/components/ui/textarea.tsx +14 -14
  385. package/dist/assets/storefront/src/components/ui/toggle-group.tsx +56 -62
  386. package/dist/assets/storefront/src/components/ui/toggle.tsx +37 -38
  387. package/dist/assets/storefront/src/components/ui/tooltip.tsx +38 -46
  388. package/dist/assets/storefront/src/config/capabilities.ts +41 -0
  389. package/dist/assets/storefront/src/config/scenario-meta.ts +2 -2
  390. package/dist/assets/storefront/src/config/scenario.ts +24 -16
  391. package/dist/assets/storefront/src/contexts/auth-context.tsx +5 -6
  392. package/dist/assets/storefront/src/graphql-env.d.ts +47 -41
  393. package/dist/assets/storefront/src/graphql.ts +5 -5
  394. package/dist/assets/storefront/src/hooks/use-mobile.ts +13 -13
  395. package/dist/assets/storefront/src/i18n/locale-utils.ts +3 -3
  396. package/dist/assets/storefront/src/i18n/navigation.ts +4 -4
  397. package/dist/assets/storefront/src/i18n/request.ts +4 -4
  398. package/dist/assets/storefront/src/i18n/routing.ts +5 -5
  399. package/dist/assets/storefront/src/i18n/server.ts +35 -7
  400. package/dist/assets/storefront/src/lib/actions/switch-channel.ts +9 -9
  401. package/dist/assets/storefront/src/lib/actions/switch-currency.ts +7 -7
  402. package/dist/assets/storefront/src/lib/auth.ts +1 -1
  403. package/dist/assets/storefront/src/lib/backend-health.ts +14 -6
  404. package/dist/assets/storefront/src/lib/cart-count-context.tsx +10 -4
  405. package/dist/assets/storefront/src/lib/cart-guard-utils.ts +2 -1
  406. package/dist/assets/storefront/src/lib/cart-guard.ts +7 -6
  407. package/dist/assets/storefront/src/lib/channel-server.ts +4 -4
  408. package/dist/assets/storefront/src/lib/company-context.ts +1 -1
  409. package/dist/assets/storefront/src/lib/company-permissions-context.tsx +1 -5
  410. package/dist/assets/storefront/src/lib/currency-server.ts +4 -4
  411. package/dist/assets/storefront/src/lib/format.ts +16 -7
  412. package/dist/assets/storefront/src/lib/metadata.ts +39 -44
  413. package/dist/assets/storefront/src/lib/quantity.ts +2 -4
  414. package/dist/assets/storefront/src/lib/search-bar-context.tsx +7 -4
  415. package/dist/assets/storefront/src/lib/use-recent-searches.ts +8 -6
  416. package/dist/assets/storefront/src/lib/utils.ts +3 -3
  417. package/dist/assets/storefront/src/lib/vendure/actions.ts +18 -16
  418. package/dist/assets/storefront/src/lib/vendure/api.ts +36 -23
  419. package/dist/assets/storefront/src/lib/vendure/approval-utils.ts +9 -8
  420. package/dist/assets/storefront/src/lib/vendure/cached.ts +20 -15
  421. package/dist/assets/storefront/src/lib/vendure/capability-documents.ts +246 -0
  422. package/dist/assets/storefront/src/lib/vendure/capability-schema.ts +358 -0
  423. package/dist/assets/storefront/src/lib/vendure/capability-ui.ts +15 -0
  424. package/dist/assets/storefront/src/lib/vendure/mutations.ts +835 -532
  425. package/dist/assets/storefront/src/lib/vendure/queries.ts +877 -524
  426. package/dist/assets/storefront/src/proxy.ts +11 -0
  427. package/dist/assets/storefront/src/scenarios/customer/hero.tsx +2 -2
  428. package/dist/assets/storefront/src/scenarios/customer/meta.ts +1 -1
  429. package/dist/assets/storefront/src/schema.graphql +337 -243
  430. package/dist/assets/templates/README.md.hbs +25 -1
  431. package/dist/assets/templates/docker-compose.yml.hbs +4 -0
  432. package/dist/assets/templates/env.hbs +34 -9
  433. package/dist/assets/templates/environment.d.ts.hbs +1 -0
  434. package/dist/assets/templates/gitignore.hbs +7 -4
  435. package/dist/assets/templates/migrate.ts.hbs +55 -0
  436. package/dist/assets/templates/npmrc.hbs +7 -2
  437. package/dist/assets/templates/platform-edition.ts.hbs +21 -0
  438. package/dist/assets/templates/server-package.json.hbs +15 -8
  439. package/dist/assets/templates/vendure-config.ts.hbs +18 -0
  440. package/dist/bin/run.js +1 -1
  441. package/dist/cli.d.ts +1 -1
  442. package/dist/cli.js +1 -1
  443. package/dist/console-onboarding.d.ts +44 -0
  444. package/dist/console-onboarding.js +1 -0
  445. package/dist/hub-projects.d.ts +48 -0
  446. package/dist/hub-projects.js +1 -0
  447. package/dist/index.d.ts +5 -5
  448. package/dist/index.js +1 -1
  449. package/dist/plugin-registry.d.ts +61 -2
  450. package/dist/plugin-registry.js +1 -1
  451. package/dist/registry.d.ts +16 -13
  452. package/dist/registry.js +1 -1
  453. package/dist/scaffold-shared.js +1 -1
  454. package/dist/scaffold-storefront-only.js +1 -1
  455. package/dist/scaffolder.d.ts +11 -0
  456. package/dist/scaffolder.js +1 -1
  457. package/dist/storefront-copier.d.ts +7 -2
  458. package/dist/storefront-copier.js +1 -1
  459. package/dist/template-renderer.d.ts +26 -5
  460. package/dist/template-renderer.js +1 -1
  461. package/dist/types.d.ts +54 -23
  462. package/package.json +14 -2
  463. package/dist/assets/storefront/src/middleware.ts +0 -11
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useState, useCallback } from 'react';
3
+ import { useCallback, useState } from 'react';
4
4
 
5
5
  const STORAGE_KEY = 'vendure-recent-searches';
6
6
  const MAX_RECENT = 5;
@@ -11,7 +11,9 @@ function readFromStorage(): string[] {
11
11
  const raw = localStorage.getItem(STORAGE_KEY);
12
12
  if (!raw) return [];
13
13
  const parsed = JSON.parse(raw);
14
- return Array.isArray(parsed) ? parsed.filter((s): s is string => typeof s === 'string').slice(0, MAX_RECENT) : [];
14
+ return Array.isArray(parsed)
15
+ ? parsed.filter((s): s is string => typeof s === 'string').slice(0, MAX_RECENT)
16
+ : [];
15
17
  } catch {
16
18
  return [];
17
19
  }
@@ -31,16 +33,16 @@ export function useRecentSearches() {
31
33
  const addSearch = useCallback((term: string) => {
32
34
  const trimmed = term.trim();
33
35
  if (!trimmed) return;
34
- setSearches(prev => {
35
- const next = [trimmed, ...prev.filter(s => s !== trimmed)].slice(0, MAX_RECENT);
36
+ setSearches((prev) => {
37
+ const next = [trimmed, ...prev.filter((s) => s !== trimmed)].slice(0, MAX_RECENT);
36
38
  writeToStorage(next);
37
39
  return next;
38
40
  });
39
41
  }, []);
40
42
 
41
43
  const removeSearch = useCallback((term: string) => {
42
- setSearches(prev => {
43
- const next = prev.filter(s => s !== term);
44
+ setSearches((prev) => {
45
+ const next = prev.filter((s) => s !== term);
44
46
  writeToStorage(next);
45
47
  return next;
46
48
  });
@@ -1,6 +1,6 @@
1
- import { clsx, type ClassValue } from "clsx"
2
- import { twMerge } from "tailwind-merge"
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
3
 
4
4
  export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs))
5
+ return twMerge(clsx(inputs));
6
6
  }
@@ -1,8 +1,4 @@
1
- import {client} from './api';
2
- import {GetActiveCustomerQuery, GetCompanyAccountQuery, GetMyCompaniesQuery, type CompanyUserCompany} from './queries';
3
- import {getActiveChannelCached} from './cached';
4
- import {cache} from "react";
5
- import {readFragment, ResultOf} from "@/graphql";
1
+ import { readFragment, ResultOf } from '@/graphql';
6
2
  import {
7
3
  ActiveCustomerFragment,
8
4
  CompanyBusinessUnitFragment,
@@ -10,7 +6,16 @@ import {
10
6
  CompanyPermissionFragment,
11
7
  CompanyRoleFragment,
12
8
  CompanyUserFragment,
13
- } from "@/lib/vendure/fragments";
9
+ } from '@/lib/vendure/fragments';
10
+ import { cache } from 'react';
11
+ import { client } from './api';
12
+ import { getActiveChannelCached } from './cached';
13
+ import {
14
+ GetActiveCustomerQuery,
15
+ GetCompanyAccountQuery,
16
+ GetMyCompaniesQuery,
17
+ type CompanyUserCompany,
18
+ } from './queries';
14
19
 
15
20
  type CompanyRoleData = Omit<ResultOf<typeof CompanyRoleFragment>, 'permissions'> & {
16
21
  permissions: ResultOf<typeof CompanyPermissionFragment>[];
@@ -34,7 +39,7 @@ export const getActiveCustomer = cache(async () => {
34
39
  return null;
35
40
  }
36
41
  return readFragment(ActiveCustomerFragment, result.data.activeCustomer);
37
- })
42
+ });
38
43
 
39
44
  export const getCompanyAccount = cache(async (): Promise<CompanyAccount | null> => {
40
45
  const [result, error] = await client.query(GetCompanyAccountQuery);
@@ -52,14 +57,14 @@ export const getCompanyAccount = cache(async (): Promise<CompanyAccount | null>
52
57
 
53
58
  const companyUser: CompanyUserData = {
54
59
  ...rawUser,
55
- companyRoles: rawUser.companyRoles.map(r => {
60
+ companyRoles: rawUser.companyRoles.map((r) => {
56
61
  const role = readFragment(CompanyRoleFragment, r);
57
62
  return {
58
63
  ...role,
59
- permissions: role.permissions.map(p => readFragment(CompanyPermissionFragment, p)),
64
+ permissions: role.permissions.map((p) => readFragment(CompanyPermissionFragment, p)),
60
65
  };
61
66
  }),
62
- businessUnits: rawUser.businessUnits.map(bu => readFragment(CompanyBusinessUnitFragment, bu)),
67
+ businessUnits: rawUser.businessUnits.map((bu) => readFragment(CompanyBusinessUnitFragment, bu)),
63
68
  };
64
69
 
65
70
  return {
@@ -76,8 +81,8 @@ export const getMyCompanies = cache(async (): Promise<CompanyUserCompany[]> => {
76
81
  const customer = result.data.activeCustomer;
77
82
  if (!customer) return [];
78
83
  return customer.companyUsers
79
- .filter(cu => cu.state === 'Active')
80
- .map(cu => ({ id: cu.company.id, name: cu.company.name, state: cu.company.state }));
84
+ .filter((cu) => cu.state === 'Active')
85
+ .map((cu) => ({ id: cu.company.id, name: cu.company.name, state: cu.company.state }));
81
86
  });
82
87
 
83
88
  /**
@@ -86,10 +91,7 @@ export const getMyCompanies = cache(async (): Promise<CompanyUserCompany[]> => {
86
91
  * to getMyCompanies (which includes pending companies via CompanyUser state).
87
92
  */
88
93
  export const getHasCompany = cache(async (): Promise<boolean> => {
89
- const [companyAccount, companies] = await Promise.all([
90
- getCompanyAccount(),
91
- getMyCompanies(),
92
- ]);
94
+ const [companyAccount, companies] = await Promise.all([getCompanyAccount(), getMyCompanies()]);
93
95
  return !!companyAccount || companies.length > 0;
94
96
  });
95
97
 
@@ -1,5 +1,5 @@
1
- import {createClient} from '@vendure-platform/storefront-client';
2
- import type {RequestMiddleware} from '@vendure-platform/storefront-client';
1
+ import type { RequestMiddleware } from '@vendure-platform/storefront-client';
2
+ import { createClient } from '@vendure-platform/storefront-client';
3
3
 
4
4
  const DRAFT_MODE_SECRET = process.env.DRAFT_MODE_SECRET;
5
5
 
@@ -11,7 +11,7 @@ const DRAFT_MODE_SECRET = process.env.DRAFT_MODE_SECRET;
11
11
  const draftModeMiddleware: RequestMiddleware = async (req, next) => {
12
12
  if (DRAFT_MODE_SECRET) {
13
13
  try {
14
- const {draftMode} = await import('next/headers');
14
+ const { draftMode } = await import('next/headers');
15
15
  const draft = await draftMode();
16
16
  if (draft.isEnabled) {
17
17
  const url = new URL(req.url);
@@ -26,35 +26,44 @@ const draftModeMiddleware: RequestMiddleware = async (req, next) => {
26
26
  };
27
27
 
28
28
  const VENDURE_API_URL = process.env.VENDURE_SHOP_API_URL || process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL;
29
- const VENDURE_CHANNEL_TOKEN = process.env.VENDURE_CHANNEL_TOKEN || process.env.NEXT_PUBLIC_VENDURE_CHANNEL_TOKEN;
29
+ const VENDURE_CHANNEL_TOKEN =
30
+ process.env.VENDURE_CHANNEL_TOKEN || process.env.NEXT_PUBLIC_VENDURE_CHANNEL_TOKEN;
30
31
  const VENDURE_AUTH_TOKEN_HEADER = process.env.VENDURE_AUTH_TOKEN_HEADER || 'vendure-auth-token';
31
32
  const VENDURE_CHANNEL_TOKEN_HEADER = process.env.VENDURE_CHANNEL_TOKEN_HEADER || 'vendure-token';
32
33
 
33
34
  if (!VENDURE_API_URL) {
34
- throw new Error('VENDURE_SHOP_API_URL or NEXT_PUBLIC_VENDURE_SHOP_API_URL environment variable is not set');
35
+ throw new Error(
36
+ 'VENDURE_SHOP_API_URL or NEXT_PUBLIC_VENDURE_SHOP_API_URL environment variable is not set',
37
+ );
35
38
  }
36
39
 
37
40
  // Dynamic imports to avoid Next.js static analysis marking this module as dynamic.
38
41
  // cookies() is only available in request context — providers gracefully return undefined otherwise.
39
42
  async function safeGetAuthToken(): Promise<string | undefined> {
40
43
  try {
41
- const {getAuthToken} = await import('@/lib/auth');
44
+ const { getAuthToken } = await import('@/lib/auth');
42
45
  return await getAuthToken();
43
- } catch { return undefined; }
46
+ } catch {
47
+ return undefined;
48
+ }
44
49
  }
45
50
 
46
51
  async function safeGetCompanyId(): Promise<string | undefined> {
47
52
  try {
48
- const {getCompanyId} = await import('@/lib/company-context');
53
+ const { getCompanyId } = await import('@/lib/company-context');
49
54
  return await getCompanyId();
50
- } catch { return undefined; }
55
+ } catch {
56
+ return undefined;
57
+ }
51
58
  }
52
59
 
53
60
  async function safeGetBusinessUnitId(): Promise<string | undefined> {
54
61
  try {
55
- const {getBusinessUnitId} = await import('@/lib/company-context');
62
+ const { getBusinessUnitId } = await import('@/lib/company-context');
56
63
  return await getBusinessUnitId();
57
- } catch { return undefined; }
64
+ } catch {
65
+ return undefined;
66
+ }
58
67
  }
59
68
 
60
69
  /**
@@ -65,16 +74,20 @@ async function safeGetBusinessUnitId(): Promise<string | undefined> {
65
74
  */
66
75
  async function safeGetChannelToken(): Promise<string | undefined> {
67
76
  try {
68
- const {getActiveChannelToken} = await import('@/lib/channel-server');
77
+ const { getActiveChannelToken } = await import('@/lib/channel-server');
69
78
  return await getActiveChannelToken();
70
- } catch { return undefined; }
79
+ } catch {
80
+ return undefined;
81
+ }
71
82
  }
72
83
 
73
84
  async function safeGetLanguageCode(): Promise<string | undefined> {
74
85
  try {
75
- const {getLocale} = await import('next-intl/server');
86
+ const { getLocale } = await import('next-intl/server');
76
87
  return await getLocale();
77
- } catch { return undefined; }
88
+ } catch {
89
+ return undefined;
90
+ }
78
91
  }
79
92
 
80
93
  const sharedConfig = {
@@ -95,7 +108,7 @@ const sharedConfig = {
95
108
  */
96
109
  const currencyMiddleware: RequestMiddleware = async (req, next) => {
97
110
  try {
98
- const {getActiveCurrencyCode} = await import('@/lib/currency-server');
111
+ const { getActiveCurrencyCode } = await import('@/lib/currency-server');
99
112
  const currency = await getActiveCurrencyCode();
100
113
  const url = new URL(req.url);
101
114
  url.searchParams.set('currencyCode', currency);
@@ -130,7 +143,7 @@ export const client = createClient({
130
143
  * access cookies, headers, or draftMode. Pass languageCode and currencyCode explicitly
131
144
  * as query options from the calling function.
132
145
  */
133
- export const publicClient = createClient({...sharedConfig});
146
+ export const publicClient = createClient({ ...sharedConfig });
134
147
 
135
148
  /**
136
149
  * Pre-resolved context for use at cache boundaries where cookies() is not available.
@@ -149,20 +162,20 @@ export interface VendureContext {
149
162
  * Use this at cache boundaries ('use cache') where providers can't access cookies().
150
163
  */
151
164
  export async function getVendureContext(): Promise<VendureContext> {
152
- const {getAuthToken} = await import('@/lib/auth');
165
+ const { getAuthToken } = await import('@/lib/auth');
153
166
  const authToken = await getAuthToken();
154
167
  let companyId: string | undefined;
155
168
  let businessUnitId: string | undefined;
156
169
  try {
157
- const {getCompanyId, getBusinessUnitId} = await import('@/lib/company-context');
170
+ const { getCompanyId, getBusinessUnitId } = await import('@/lib/company-context');
158
171
  companyId = await getCompanyId();
159
172
  businessUnitId = await getBusinessUnitId();
160
173
  } catch {}
161
- const {getActiveChannelToken} = await import('@/lib/channel-server');
174
+ const { getActiveChannelToken } = await import('@/lib/channel-server');
162
175
  const channelToken = await getActiveChannelToken();
163
- const {getRouteLocale} = await import('@/i18n/server');
176
+ const { getRouteLocale } = await import('@/i18n/server');
164
177
  const locale = await getRouteLocale();
165
- const {getActiveCurrencyCode} = await import('@/lib/currency-server');
178
+ const { getActiveCurrencyCode } = await import('@/lib/currency-server');
166
179
  const currencyCode = await getActiveCurrencyCode();
167
- return {authToken, channelToken, companyId, businessUnitId, locale, currencyCode};
180
+ return { authToken, channelToken, companyId, businessUnitId, locale, currencyCode };
168
181
  }
@@ -1,21 +1,22 @@
1
- import {client} from '@/lib/vendure/api';
2
- import type {VendureContext} from '@/lib/vendure/api';
3
- import {GetApprovalHistoryForEntityQuery} from '@/lib/vendure/queries';
1
+ import type { VendureContext } from '@/lib/vendure/api';
2
+ import { client } from '@/lib/vendure/api';
3
+ import { GetApprovalHistoryForEntityQuery } from '@/lib/vendure/queries';
4
4
 
5
5
  export async function getLatestApprovalForOrder(orderId: string, ctx?: VendureContext) {
6
6
  const queryArgs = { entityType: 'Order', entityId: orderId } as const;
7
7
  const [result] = ctx
8
8
  ? await client.query(GetApprovalHistoryForEntityQuery, queryArgs, ctx)
9
9
  : await client.query(GetApprovalHistoryForEntityQuery, queryArgs);
10
- const history = [...(result?.data?.approvalHistoryForEntity ?? [])]
11
- .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
10
+ const history = [...(result?.data?.approvalHistoryForEntity ?? [])].sort(
11
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
12
+ );
12
13
  const latestApproval = history[0] ?? null;
13
14
  const changesRequested = latestApproval?.status === 'CHANGES_REQUESTED';
14
15
  const changesComment = changesRequested
15
16
  ? latestApproval.decisions
16
- ?.filter((d: any) => d.decision === 'CHANGES_REQUESTED' && d.comment)
17
- .map((d: any) => d.comment)
18
- .at(-1) ?? null
17
+ ?.filter((d: any) => d.decision === 'CHANGES_REQUESTED' && d.comment)
18
+ .map((d: any) => d.comment)
19
+ .at(-1) ?? null
19
20
  : null;
20
21
  return { latestApproval, changesRequested, changesComment };
21
22
  }
@@ -1,6 +1,6 @@
1
- import {cacheLife, cacheTag} from 'next/cache';
2
- import {publicClient} from './api';
3
- import {GetActiveChannelQuery, GetAvailableCountriesQuery, GetTopCollectionsQuery} from './queries';
1
+ import { cacheLife, cacheTag } from 'next/cache';
2
+ import { publicClient } from './api';
3
+ import { GetActiveChannelQuery, GetAvailableCountriesQuery, GetTopCollectionsQuery } from './queries';
4
4
 
5
5
  /**
6
6
  * Get the active channel with caching enabled.
@@ -19,11 +19,18 @@ export async function getActiveChannelCached(channelToken?: string) {
19
19
  const [result, error] = await publicClient.query(
20
20
  GetActiveChannelQuery,
21
21
  undefined,
22
- channelToken ? {channelToken} : undefined,
22
+ channelToken ? { channelToken } : undefined,
23
23
  );
24
24
  if (error) {
25
25
  // Vendure API unavailable during build
26
- return { id: '', code: '', defaultLanguageCode: 'en' as const, availableLanguageCodes: [], defaultCurrencyCode: 'USD' as const, availableCurrencyCodes: [] };
26
+ return {
27
+ id: '',
28
+ code: '',
29
+ defaultLanguageCode: 'en' as const,
30
+ availableLanguageCodes: [],
31
+ defaultCurrencyCode: 'USD' as const,
32
+ availableCurrencyCodes: [],
33
+ };
27
34
  }
28
35
  return result.data.activeChannel;
29
36
  }
@@ -38,11 +45,10 @@ export async function getAvailableCountriesCached(locale: string, channelToken?:
38
45
  cacheLife('max');
39
46
  cacheTag(`countries-${locale}${channelToken ? `-${channelToken}` : ''}`);
40
47
 
41
- const [result, error] = await publicClient.query(
42
- GetAvailableCountriesQuery,
43
- undefined,
44
- {languageCode: locale, ...(channelToken ? {channelToken} : {})},
45
- );
48
+ const [result, error] = await publicClient.query(GetAvailableCountriesQuery, undefined, {
49
+ languageCode: locale,
50
+ ...(channelToken ? { channelToken } : {}),
51
+ });
46
52
  if (error) {
47
53
  return [];
48
54
  }
@@ -59,11 +65,10 @@ export async function getTopCollections(locale: string, channelToken?: string) {
59
65
  cacheLife('days');
60
66
  cacheTag(`collections-${locale}${channelToken ? `-${channelToken}` : ''}`);
61
67
 
62
- const [result, error] = await publicClient.query(
63
- GetTopCollectionsQuery,
64
- undefined,
65
- {languageCode: locale, ...(channelToken ? {channelToken} : {})},
66
- );
68
+ const [result, error] = await publicClient.query(GetTopCollectionsQuery, undefined, {
69
+ languageCode: locale,
70
+ ...(channelToken ? { channelToken } : {}),
71
+ });
67
72
  if (error) {
68
73
  return [];
69
74
  }
@@ -0,0 +1,246 @@
1
+ import { hasCapability } from '@/config/capabilities';
2
+ import type {
3
+ DefinitionNode,
4
+ DocumentNode,
5
+ FieldNode,
6
+ FragmentDefinitionNode,
7
+ SelectionNode,
8
+ SelectionSetNode,
9
+ } from 'graphql';
10
+ import { visit } from 'graphql';
11
+
12
+ /**
13
+ * A selection to drop, written as the path of field names from the operation
14
+ * root, e.g. `['product', 'customFields', 'configuratorSystemCode']`.
15
+ */
16
+ export type FieldPath = readonly string[];
17
+
18
+ export interface CapabilityDocumentRule {
19
+ capability: string;
20
+ paths: readonly FieldPath[];
21
+ }
22
+
23
+ export interface CapabilityDocumentRegistration<T extends DocumentNode = DocumentNode> {
24
+ name: string;
25
+ fullDocument: T;
26
+ rules: readonly CapabilityDocumentRule[];
27
+ }
28
+
29
+ /** Capability-owned storefront documents, used by the schema composition tests. */
30
+ export const STOREFRONT_CAPABILITY_DOCUMENTS: CapabilityDocumentRegistration[] = [];
31
+
32
+ function isField(selection: SelectionNode): selection is FieldNode {
33
+ return selection.kind === 'Field';
34
+ }
35
+
36
+ function fieldName(field: FieldNode): string {
37
+ return field.name.value;
38
+ }
39
+
40
+ function addOperationFallback<T extends DocumentNode>(document: T): T {
41
+ return {
42
+ ...document,
43
+ definitions: document.definitions.map((definition) => {
44
+ if (definition.kind !== 'OperationDefinition') return definition;
45
+ if (
46
+ definition.selectionSet.selections.some(
47
+ (selection) => isField(selection) && fieldName(selection) === '__typename',
48
+ )
49
+ ) {
50
+ return definition;
51
+ }
52
+ return {
53
+ ...definition,
54
+ selectionSet: {
55
+ ...definition.selectionSet,
56
+ selections: [
57
+ ...definition.selectionSet.selections,
58
+ { kind: 'Field', name: { kind: 'Name', value: '__typename' } },
59
+ ],
60
+ },
61
+ };
62
+ }),
63
+ } as T;
64
+ }
65
+
66
+ function fragmentSpreads(selectionSet: SelectionSetNode): string[] {
67
+ const names: string[] = [];
68
+ for (const selection of selectionSet.selections) {
69
+ if (selection.kind === 'FragmentSpread') {
70
+ names.push(selection.name.value);
71
+ } else if (selection.kind === 'Field' && selection.selectionSet) {
72
+ names.push(...fragmentSpreads(selection.selectionSet));
73
+ } else if (selection.kind === 'InlineFragment') {
74
+ names.push(...fragmentSpreads(selection.selectionSet));
75
+ }
76
+ }
77
+ return names;
78
+ }
79
+
80
+ function removeUnusedFragments(definitions: readonly DefinitionNode[]): readonly DefinitionNode[] {
81
+ const fragments = new Map<string, FragmentDefinitionNode>();
82
+ for (const definition of definitions) {
83
+ if (definition.kind === 'FragmentDefinition') {
84
+ fragments.set(definition.name.value, definition);
85
+ }
86
+ }
87
+
88
+ const used = new Set<string>();
89
+ const pending = definitions
90
+ .filter((definition) => definition.kind === 'OperationDefinition')
91
+ .flatMap((definition) => fragmentSpreads(definition.selectionSet));
92
+ while (pending.length > 0) {
93
+ const name = pending.pop()!;
94
+ if (used.has(name)) continue;
95
+ used.add(name);
96
+ const fragment = fragments.get(name);
97
+ if (fragment) pending.push(...fragmentSpreads(fragment.selectionSet));
98
+ }
99
+
100
+ return definitions.filter(
101
+ (definition) => definition.kind !== 'FragmentDefinition' || used.has(definition.name.value),
102
+ );
103
+ }
104
+
105
+ function removeUnusedVariables(definitions: readonly DefinitionNode[]): readonly DefinitionNode[] {
106
+ const used = new Set<string>();
107
+ visit({ kind: 'Document', definitions } as DocumentNode, {
108
+ VariableDefinition() {
109
+ return false;
110
+ },
111
+ Variable(node) {
112
+ used.add(node.name.value);
113
+ },
114
+ });
115
+
116
+ return definitions.map((definition) => {
117
+ if (definition.kind !== 'OperationDefinition' || !definition.variableDefinitions) {
118
+ return definition;
119
+ }
120
+ return {
121
+ ...definition,
122
+ variableDefinitions: definition.variableDefinitions.filter((variable) =>
123
+ used.has(variable.variable.name.value),
124
+ ),
125
+ };
126
+ });
127
+ }
128
+
129
+ /**
130
+ * Returns `selectionSet` with `path` removed, or `null` when removing it would
131
+ * leave an empty selection set — GraphQL has no syntax for one, so the caller
132
+ * drops the parent field instead.
133
+ */
134
+ function stripPath(selectionSet: SelectionSetNode, path: FieldPath): SelectionSetNode | null {
135
+ const [head, ...rest] = path;
136
+ const selections: SelectionNode[] = [];
137
+
138
+ for (const selection of selectionSet.selections) {
139
+ if (!isField(selection) || fieldName(selection) !== head) {
140
+ selections.push(selection);
141
+ continue;
142
+ }
143
+ if (rest.length === 0) {
144
+ // Leaf reached: drop it by not re-adding it.
145
+ continue;
146
+ }
147
+ if (!selection.selectionSet) {
148
+ selections.push(selection);
149
+ continue;
150
+ }
151
+ const nested = stripPath(selection.selectionSet, rest);
152
+ if (nested) {
153
+ selections.push({ ...selection, selectionSet: nested });
154
+ }
155
+ }
156
+
157
+ return selections.length > 0 ? { ...selectionSet, selections } : null;
158
+ }
159
+
160
+ /**
161
+ * Removes capability-gated selections from a GraphQL document.
162
+ *
163
+ * The server composes its schema from the capability modules the deployment is
164
+ * entitled to, so selecting a field owned by an absent module fails validation
165
+ * on every request. Rather than maintaining a hand-written copy of each
166
+ * document per composition, the full document stays the single typed source of
167
+ * truth and the selections a composition cannot serve are pruned from it here.
168
+ *
169
+ * The returned document keeps the input's TypeScript type: a pruned field is
170
+ * `undefined` at runtime, so consumers must treat capability-gated data as
171
+ * optional. That is the same contract they already need for a field the server
172
+ * may legitimately return as null.
173
+ */
174
+ export function stripCapabilityFields<T extends DocumentNode>(document: T, paths: FieldPath[]): T {
175
+ if (paths.length === 0) {
176
+ return document;
177
+ }
178
+ let definitions = document.definitions;
179
+ for (const path of paths) {
180
+ definitions = definitions.map((definition) => {
181
+ if (definition.kind !== 'OperationDefinition' && definition.kind !== 'FragmentDefinition') {
182
+ return definition;
183
+ }
184
+ const stripped = stripPath(definition.selectionSet, path);
185
+ if (!stripped) {
186
+ // Every selection collapsed, which GraphQL cannot express. A
187
+ // prune list that empties a whole operation is a mistake in the
188
+ // capability mapping, not something to paper over by keeping
189
+ // the field that was supposed to be removed.
190
+ throw new Error(
191
+ `Pruning "${path.join('.')}" would leave an empty selection set. ` +
192
+ 'Split the document instead of pruning its only selection.',
193
+ );
194
+ }
195
+ return { ...definition, selectionSet: stripped };
196
+ });
197
+ }
198
+ const usedDefinitions = removeUnusedFragments(definitions);
199
+ return { ...document, definitions: removeUnusedVariables(usedDefinitions) } as T;
200
+ }
201
+
202
+ /**
203
+ * Convenience wrapper: prunes `paths` only when `granted` is false.
204
+ */
205
+ export function whenGranted<T extends DocumentNode>(granted: boolean, document: T, paths: FieldPath[]): T {
206
+ return granted ? document : stripCapabilityFields(addOperationFallback(document), paths);
207
+ }
208
+
209
+ /**
210
+ * Registers and composes one typed storefront document from capability rules.
211
+ * The full typed document remains the source of truth. Missing grants prune
212
+ * only the selections that their modules own.
213
+ */
214
+ export function composeCapabilityDocument<T extends DocumentNode>(
215
+ document: T,
216
+ rules: readonly CapabilityDocumentRule[],
217
+ ): T {
218
+ const operation = document.definitions.find((definition) => definition.kind === 'OperationDefinition');
219
+ const name = operation?.name?.value;
220
+ if (!name) {
221
+ throw new Error('Capability-composed storefront documents must use a named operation.');
222
+ }
223
+ STOREFRONT_CAPABILITY_DOCUMENTS.push({ name, fullDocument: document, rules });
224
+ return composeCapabilityDocumentFor(document, rules, (capability) => hasCapability(capability));
225
+ }
226
+
227
+ /** Compose a registered document for an explicit grant set in tests. */
228
+ export function composeCapabilityDocumentFor<T extends DocumentNode>(
229
+ document: T,
230
+ rules: readonly CapabilityDocumentRule[],
231
+ granted: (capability: string) => boolean,
232
+ ): T {
233
+ return rules.reduce(
234
+ (composed, rule) => whenGranted(granted(rule.capability), composed, [...rule.paths]),
235
+ document,
236
+ );
237
+ }
238
+
239
+ /** Registers a document owned by one capability. */
240
+ export function forCapability<T extends DocumentNode>(
241
+ capability: string,
242
+ document: T,
243
+ paths: readonly FieldPath[],
244
+ ): T {
245
+ return composeCapabilityDocument(document, [{ capability, paths }]);
246
+ }