@doswiftly/cli 0.1.1

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 (453) hide show
  1. package/README.md +357 -0
  2. package/bin/doswiftly.js +2 -0
  3. package/dist/commands/auth-github.d.ts +6 -0
  4. package/dist/commands/auth-github.d.ts.map +1 -0
  5. package/dist/commands/auth-github.js +89 -0
  6. package/dist/commands/auth-github.js.map +1 -0
  7. package/dist/commands/auth-token.d.ts +12 -0
  8. package/dist/commands/auth-token.d.ts.map +1 -0
  9. package/dist/commands/auth-token.js +43 -0
  10. package/dist/commands/auth-token.js.map +1 -0
  11. package/dist/commands/auth.d.ts +22 -0
  12. package/dist/commands/auth.d.ts.map +1 -0
  13. package/dist/commands/auth.js +348 -0
  14. package/dist/commands/auth.js.map +1 -0
  15. package/dist/commands/check.d.ts +5 -0
  16. package/dist/commands/check.d.ts.map +1 -0
  17. package/dist/commands/check.js +234 -0
  18. package/dist/commands/check.js.map +1 -0
  19. package/dist/commands/config.d.ts +3 -0
  20. package/dist/commands/config.d.ts.map +1 -0
  21. package/dist/commands/config.js +104 -0
  22. package/dist/commands/config.js.map +1 -0
  23. package/dist/commands/deploy.d.ts +37 -0
  24. package/dist/commands/deploy.d.ts.map +1 -0
  25. package/dist/commands/deploy.js +580 -0
  26. package/dist/commands/deploy.js.map +1 -0
  27. package/dist/commands/dev.d.ts +8 -0
  28. package/dist/commands/dev.d.ts.map +1 -0
  29. package/dist/commands/dev.js +83 -0
  30. package/dist/commands/dev.js.map +1 -0
  31. package/dist/commands/doctor.d.ts +5 -0
  32. package/dist/commands/doctor.d.ts.map +1 -0
  33. package/dist/commands/doctor.js +363 -0
  34. package/dist/commands/doctor.js.map +1 -0
  35. package/dist/commands/domain.d.ts +13 -0
  36. package/dist/commands/domain.d.ts.map +1 -0
  37. package/dist/commands/domain.js +128 -0
  38. package/dist/commands/domain.js.map +1 -0
  39. package/dist/commands/env.d.ts +25 -0
  40. package/dist/commands/env.d.ts.map +1 -0
  41. package/dist/commands/env.js +228 -0
  42. package/dist/commands/env.js.map +1 -0
  43. package/dist/commands/init.d.ts +11 -0
  44. package/dist/commands/init.d.ts.map +1 -0
  45. package/dist/commands/init.js +1028 -0
  46. package/dist/commands/init.js.map +1 -0
  47. package/dist/commands/inspect.d.ts +12 -0
  48. package/dist/commands/inspect.d.ts.map +1 -0
  49. package/dist/commands/inspect.js +162 -0
  50. package/dist/commands/inspect.js.map +1 -0
  51. package/dist/commands/migrate.d.ts +18 -0
  52. package/dist/commands/migrate.d.ts.map +1 -0
  53. package/dist/commands/migrate.js +355 -0
  54. package/dist/commands/migrate.js.map +1 -0
  55. package/dist/commands/preview.d.ts +29 -0
  56. package/dist/commands/preview.d.ts.map +1 -0
  57. package/dist/commands/preview.js +199 -0
  58. package/dist/commands/preview.js.map +1 -0
  59. package/dist/commands/proxy.d.ts +9 -0
  60. package/dist/commands/proxy.d.ts.map +1 -0
  61. package/dist/commands/proxy.js +37 -0
  62. package/dist/commands/proxy.js.map +1 -0
  63. package/dist/commands/sdk.d.ts +5 -0
  64. package/dist/commands/sdk.d.ts.map +1 -0
  65. package/dist/commands/sdk.js +82 -0
  66. package/dist/commands/sdk.js.map +1 -0
  67. package/dist/commands/template.d.ts +107 -0
  68. package/dist/commands/template.d.ts.map +1 -0
  69. package/dist/commands/template.js +1309 -0
  70. package/dist/commands/template.js.map +1 -0
  71. package/dist/commands/types.d.ts +5 -0
  72. package/dist/commands/types.d.ts.map +1 -0
  73. package/dist/commands/types.js +82 -0
  74. package/dist/commands/types.js.map +1 -0
  75. package/dist/commands/update.d.ts +2 -0
  76. package/dist/commands/update.d.ts.map +1 -0
  77. package/dist/commands/update.js +103 -0
  78. package/dist/commands/update.js.map +1 -0
  79. package/dist/commands/upgrade.d.ts +18 -0
  80. package/dist/commands/upgrade.d.ts.map +1 -0
  81. package/dist/commands/upgrade.js +55 -0
  82. package/dist/commands/upgrade.js.map +1 -0
  83. package/dist/commands/verify.d.ts +5 -0
  84. package/dist/commands/verify.d.ts.map +1 -0
  85. package/dist/commands/verify.js +232 -0
  86. package/dist/commands/verify.js.map +1 -0
  87. package/dist/commands/whoami.d.ts +5 -0
  88. package/dist/commands/whoami.d.ts.map +1 -0
  89. package/dist/commands/whoami.js +60 -0
  90. package/dist/commands/whoami.js.map +1 -0
  91. package/dist/config/types.d.ts +173 -0
  92. package/dist/config/types.d.ts.map +1 -0
  93. package/dist/config/types.js +48 -0
  94. package/dist/config/types.js.map +1 -0
  95. package/dist/index.d.ts +3 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +416 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/lib/api-url.d.ts +14 -0
  100. package/dist/lib/api-url.d.ts.map +1 -0
  101. package/dist/lib/api-url.js +24 -0
  102. package/dist/lib/api-url.js.map +1 -0
  103. package/dist/lib/api.d.ts +67 -0
  104. package/dist/lib/api.d.ts.map +1 -0
  105. package/dist/lib/api.js +36 -0
  106. package/dist/lib/api.js.map +1 -0
  107. package/dist/lib/config.d.ts +39 -0
  108. package/dist/lib/config.d.ts.map +1 -0
  109. package/dist/lib/config.js +195 -0
  110. package/dist/lib/config.js.map +1 -0
  111. package/dist/lib/env-storage.d.ts +140 -0
  112. package/dist/lib/env-storage.d.ts.map +1 -0
  113. package/dist/lib/env-storage.js +464 -0
  114. package/dist/lib/env-storage.js.map +1 -0
  115. package/dist/lib/errors.d.ts +61 -0
  116. package/dist/lib/errors.d.ts.map +1 -0
  117. package/dist/lib/errors.js +204 -0
  118. package/dist/lib/errors.js.map +1 -0
  119. package/dist/lib/i18n.d.ts +99 -0
  120. package/dist/lib/i18n.d.ts.map +1 -0
  121. package/dist/lib/i18n.js +184 -0
  122. package/dist/lib/i18n.js.map +1 -0
  123. package/dist/lib/logger.d.ts +95 -0
  124. package/dist/lib/logger.d.ts.map +1 -0
  125. package/dist/lib/logger.js +168 -0
  126. package/dist/lib/logger.js.map +1 -0
  127. package/dist/lib/package-manager.d.ts +91 -0
  128. package/dist/lib/package-manager.d.ts.map +1 -0
  129. package/dist/lib/package-manager.js +205 -0
  130. package/dist/lib/package-manager.js.map +1 -0
  131. package/dist/lib/proxy-server.d.ts +24 -0
  132. package/dist/lib/proxy-server.d.ts.map +1 -0
  133. package/dist/lib/proxy-server.js +173 -0
  134. package/dist/lib/proxy-server.js.map +1 -0
  135. package/dist/lib/select-with-back.d.ts +34 -0
  136. package/dist/lib/select-with-back.d.ts.map +1 -0
  137. package/dist/lib/select-with-back.js +94 -0
  138. package/dist/lib/select-with-back.js.map +1 -0
  139. package/dist/lib/shared-api-client.d.ts +40 -0
  140. package/dist/lib/shared-api-client.d.ts.map +1 -0
  141. package/dist/lib/shared-api-client.js +92 -0
  142. package/dist/lib/shared-api-client.js.map +1 -0
  143. package/dist/lib/wizard-engine.d.ts +128 -0
  144. package/dist/lib/wizard-engine.d.ts.map +1 -0
  145. package/dist/lib/wizard-engine.js +168 -0
  146. package/dist/lib/wizard-engine.js.map +1 -0
  147. package/package.json +85 -0
  148. package/templates/storefront-minimal/.env.example +10 -0
  149. package/templates/storefront-minimal/.github/workflows/build-template.yml +109 -0
  150. package/templates/storefront-minimal/app/globals.css +18 -0
  151. package/templates/storefront-minimal/app/layout.tsx +26 -0
  152. package/templates/storefront-minimal/app/page.tsx +93 -0
  153. package/templates/storefront-minimal/lib/graphql-client.ts +23 -0
  154. package/templates/storefront-minimal/next.config.ts +15 -0
  155. package/templates/storefront-minimal/open-next.config.ts +3 -0
  156. package/templates/storefront-minimal/package.json +30 -0
  157. package/templates/storefront-minimal/postcss.config.mjs +5 -0
  158. package/templates/storefront-minimal/tailwind.config.ts +14 -0
  159. package/templates/storefront-minimal/tsconfig.json +27 -0
  160. package/templates/storefront-minimal/wrangler.toml +9 -0
  161. package/templates/storefront-nextjs/.env.example +68 -0
  162. package/templates/storefront-nextjs/.github/workflows/build-template.yml +109 -0
  163. package/templates/storefront-nextjs/.github/workflows/deploy.yml +25 -0
  164. package/templates/storefront-nextjs/.github/workflows/preview.yml +22 -0
  165. package/templates/storefront-nextjs/README.md +520 -0
  166. package/templates/storefront-nextjs/app/account/orders/page.tsx +216 -0
  167. package/templates/storefront-nextjs/app/account/page.tsx +167 -0
  168. package/templates/storefront-nextjs/app/auth/login/page.tsx +135 -0
  169. package/templates/storefront-nextjs/app/auth/register/page.tsx +228 -0
  170. package/templates/storefront-nextjs/app/cart/page.tsx +263 -0
  171. package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +200 -0
  172. package/templates/storefront-nextjs/app/categories/page.tsx +58 -0
  173. package/templates/storefront-nextjs/app/checkout/page.tsx +351 -0
  174. package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +158 -0
  175. package/templates/storefront-nextjs/app/collections/page.tsx +61 -0
  176. package/templates/storefront-nextjs/app/globals.css +98 -0
  177. package/templates/storefront-nextjs/app/layout.tsx +39 -0
  178. package/templates/storefront-nextjs/app/page.tsx +136 -0
  179. package/templates/storefront-nextjs/app/products/[slug]/page.tsx +119 -0
  180. package/templates/storefront-nextjs/app/products/page.tsx +107 -0
  181. package/templates/storefront-nextjs/app/search/page.tsx +127 -0
  182. package/templates/storefront-nextjs/components/auth/auth-guard.tsx +94 -0
  183. package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +77 -0
  184. package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +29 -0
  185. package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +217 -0
  186. package/templates/storefront-nextjs/components/commerce/pagination.tsx +62 -0
  187. package/templates/storefront-nextjs/components/commerce/product-actions.tsx +135 -0
  188. package/templates/storefront-nextjs/components/commerce/product-filters.tsx +109 -0
  189. package/templates/storefront-nextjs/components/commerce/product-price.tsx +375 -0
  190. package/templates/storefront-nextjs/components/commerce/search-input.tsx +178 -0
  191. package/templates/storefront-nextjs/components/commerce/sort-select.tsx +64 -0
  192. package/templates/storefront-nextjs/components/commerce/variant-selector.tsx +210 -0
  193. package/templates/storefront-nextjs/components/layout/footer.tsx +107 -0
  194. package/templates/storefront-nextjs/components/layout/header.tsx +104 -0
  195. package/templates/storefront-nextjs/components/providers.tsx +62 -0
  196. package/templates/storefront-nextjs/lib/auth/routes.ts +52 -0
  197. package/templates/storefront-nextjs/lib/currency.tsx +140 -0
  198. package/templates/storefront-nextjs/lib/format.ts +159 -0
  199. package/templates/storefront-nextjs/lib/graphql-queries.ts +629 -0
  200. package/templates/storefront-nextjs/lib/hooks.ts +30 -0
  201. package/templates/storefront-nextjs/middleware.ts +80 -0
  202. package/templates/storefront-nextjs/next.config.ts +37 -0
  203. package/templates/storefront-nextjs/open-next.config.ts +3 -0
  204. package/templates/storefront-nextjs/package.dev.json +30 -0
  205. package/templates/storefront-nextjs/package.json +32 -0
  206. package/templates/storefront-nextjs/package.json.template +32 -0
  207. package/templates/storefront-nextjs/postcss.config.mjs +8 -0
  208. package/templates/storefront-nextjs/tailwind.config.ts +111 -0
  209. package/templates/storefront-nextjs/tsconfig.json +27 -0
  210. package/templates/storefront-nextjs/wrangler.toml +9 -0
  211. package/templates/storefront-nextjs-shadcn/.env.example +68 -0
  212. package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +109 -0
  213. package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +25 -0
  214. package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +22 -0
  215. package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +282 -0
  216. package/templates/storefront-nextjs-shadcn/CLAUDE.md +96 -0
  217. package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +190 -0
  218. package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +263 -0
  219. package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +135 -0
  220. package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +142 -0
  221. package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +448 -0
  222. package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +307 -0
  223. package/templates/storefront-nextjs-shadcn/README.md +195 -0
  224. package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +245 -0
  225. package/templates/storefront-nextjs-shadcn/app/about/page.tsx +34 -0
  226. package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +215 -0
  227. package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +484 -0
  228. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +128 -0
  229. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/tracking/page.tsx +206 -0
  230. package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +80 -0
  231. package/templates/storefront-nextjs-shadcn/app/account/page.tsx +107 -0
  232. package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +195 -0
  233. package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +87 -0
  234. package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +125 -0
  235. package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +131 -0
  236. package/templates/storefront-nextjs-shadcn/app/auth/login/page.tsx +24 -0
  237. package/templates/storefront-nextjs-shadcn/app/auth/register/page.tsx +20 -0
  238. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +323 -0
  239. package/templates/storefront-nextjs-shadcn/app/blog/page.tsx +159 -0
  240. package/templates/storefront-nextjs-shadcn/app/brands/[slug]/page.tsx +170 -0
  241. package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +73 -0
  242. package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +165 -0
  243. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +78 -0
  244. package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +75 -0
  245. package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +1752 -0
  246. package/templates/storefront-nextjs-shadcn/app/checkout/success/[orderId]/page.tsx +256 -0
  247. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +74 -0
  248. package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +75 -0
  249. package/templates/storefront-nextjs-shadcn/app/contact/page.tsx +114 -0
  250. package/templates/storefront-nextjs-shadcn/app/error.tsx +90 -0
  251. package/templates/storefront-nextjs-shadcn/app/globals.css +125 -0
  252. package/templates/storefront-nextjs-shadcn/app/layout.tsx +57 -0
  253. package/templates/storefront-nextjs-shadcn/app/not-found.tsx +68 -0
  254. package/templates/storefront-nextjs-shadcn/app/page.tsx +21 -0
  255. package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +246 -0
  256. package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +343 -0
  257. package/templates/storefront-nextjs-shadcn/app/products/page.tsx +25 -0
  258. package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +192 -0
  259. package/templates/storefront-nextjs-shadcn/app/returns/page.tsx +77 -0
  260. package/templates/storefront-nextjs-shadcn/app/robots.ts +53 -0
  261. package/templates/storefront-nextjs-shadcn/app/search/page.tsx +16 -0
  262. package/templates/storefront-nextjs-shadcn/app/search/search-client.tsx +47 -0
  263. package/templates/storefront-nextjs-shadcn/app/shipping/page.tsx +62 -0
  264. package/templates/storefront-nextjs-shadcn/app/sitemap.ts +144 -0
  265. package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +179 -0
  266. package/templates/storefront-nextjs-shadcn/codegen.ts +51 -0
  267. package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +348 -0
  268. package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +144 -0
  269. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +258 -0
  270. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +107 -0
  271. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +132 -0
  272. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +188 -0
  273. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +305 -0
  274. package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +240 -0
  275. package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +177 -0
  276. package/templates/storefront-nextjs-shadcn/components/blog/index.ts +8 -0
  277. package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +119 -0
  278. package/templates/storefront-nextjs-shadcn/components/brand/brand-grid.tsx +64 -0
  279. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +140 -0
  280. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +48 -0
  281. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +112 -0
  282. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +84 -0
  283. package/templates/storefront-nextjs-shadcn/components/cart/index.ts +17 -0
  284. package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +121 -0
  285. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +162 -0
  286. package/templates/storefront-nextjs-shadcn/components/checkout/index.ts +25 -0
  287. package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +187 -0
  288. package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +160 -0
  289. package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +154 -0
  290. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +225 -0
  291. package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +62 -0
  292. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +158 -0
  293. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +174 -0
  294. package/templates/storefront-nextjs-shadcn/components/commerce/variant-selector.tsx +210 -0
  295. package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +97 -0
  296. package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +187 -0
  297. package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +151 -0
  298. package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +166 -0
  299. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +245 -0
  300. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +246 -0
  301. package/templates/storefront-nextjs-shadcn/components/discount/index.ts +19 -0
  302. package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +113 -0
  303. package/templates/storefront-nextjs-shadcn/components/error/index.ts +7 -0
  304. package/templates/storefront-nextjs-shadcn/components/filters/attribute-filter.tsx +153 -0
  305. package/templates/storefront-nextjs-shadcn/components/filters/checkbox-group-filter.tsx +167 -0
  306. package/templates/storefront-nextjs-shadcn/components/filters/color-swatch-filter.tsx +176 -0
  307. package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +220 -0
  308. package/templates/storefront-nextjs-shadcn/components/filters/index.ts +36 -0
  309. package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +193 -0
  310. package/templates/storefront-nextjs-shadcn/components/filters/toggle-filter.tsx +132 -0
  311. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +321 -0
  312. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +309 -0
  313. package/templates/storefront-nextjs-shadcn/components/gift-card/index.ts +24 -0
  314. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +72 -0
  315. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +107 -0
  316. package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +85 -0
  317. package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +34 -0
  318. package/templates/storefront-nextjs-shadcn/components/home/index.ts +8 -0
  319. package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +108 -0
  320. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +133 -0
  321. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +341 -0
  322. package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +128 -0
  323. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +147 -0
  324. package/templates/storefront-nextjs-shadcn/components/layout/index.ts +9 -0
  325. package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +211 -0
  326. package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +95 -0
  327. package/templates/storefront-nextjs-shadcn/components/layout/theme-switcher.tsx +192 -0
  328. package/templates/storefront-nextjs-shadcn/components/loyalty/index.ts +11 -0
  329. package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +93 -0
  330. package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +177 -0
  331. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +250 -0
  332. package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +217 -0
  333. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-badge.tsx +106 -0
  334. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +131 -0
  335. package/templates/storefront-nextjs-shadcn/components/order/delivery-estimate.tsx +196 -0
  336. package/templates/storefront-nextjs-shadcn/components/order/index.ts +11 -0
  337. package/templates/storefront-nextjs-shadcn/components/order/order-tracking.tsx +200 -0
  338. package/templates/storefront-nextjs-shadcn/components/order/shipment-card.tsx +407 -0
  339. package/templates/storefront-nextjs-shadcn/components/order/tracking-status.tsx +222 -0
  340. package/templates/storefront-nextjs-shadcn/components/order/tracking-timeline.tsx +205 -0
  341. package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +161 -0
  342. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +250 -0
  343. package/templates/storefront-nextjs-shadcn/components/product/discount-badge.tsx +196 -0
  344. package/templates/storefront-nextjs-shadcn/components/product/index.ts +41 -0
  345. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +147 -0
  346. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +217 -0
  347. package/templates/storefront-nextjs-shadcn/components/product/product-gallery.tsx +143 -0
  348. package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +83 -0
  349. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +155 -0
  350. package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +158 -0
  351. package/templates/storefront-nextjs-shadcn/components/product/product-quantity-selector.tsx +111 -0
  352. package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +238 -0
  353. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +58 -0
  354. package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +169 -0
  355. package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +220 -0
  356. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +338 -0
  357. package/templates/storefront-nextjs-shadcn/components/product/review-summary.tsx +143 -0
  358. package/templates/storefront-nextjs-shadcn/components/product/sale-countdown.tsx +166 -0
  359. package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +213 -0
  360. package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +57 -0
  361. package/templates/storefront-nextjs-shadcn/components/product/stock-indicator.tsx +91 -0
  362. package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +103 -0
  363. package/templates/storefront-nextjs-shadcn/components/providers/index.ts +8 -0
  364. package/templates/storefront-nextjs-shadcn/components/providers/query-provider.tsx +260 -0
  365. package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +13 -0
  366. package/templates/storefront-nextjs-shadcn/components/returns/index.ts +26 -0
  367. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +608 -0
  368. package/templates/storefront-nextjs-shadcn/components/returns/return-status-card.tsx +554 -0
  369. package/templates/storefront-nextjs-shadcn/components/search/index.ts +8 -0
  370. package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +140 -0
  371. package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +58 -0
  372. package/templates/storefront-nextjs-shadcn/components/search/search-suggestions.tsx +43 -0
  373. package/templates/storefront-nextjs-shadcn/components/seo/index.ts +12 -0
  374. package/templates/storefront-nextjs-shadcn/components/seo/json-ld.tsx +56 -0
  375. package/templates/storefront-nextjs-shadcn/components/seo/product-json-ld.ts +167 -0
  376. package/templates/storefront-nextjs-shadcn/components/shipping/index.ts +16 -0
  377. package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +337 -0
  378. package/templates/storefront-nextjs-shadcn/components/ui/accordion.tsx +153 -0
  379. package/templates/storefront-nextjs-shadcn/components/ui/alert.tsx +59 -0
  380. package/templates/storefront-nextjs-shadcn/components/ui/badge.tsx +34 -0
  381. package/templates/storefront-nextjs-shadcn/components/ui/button.tsx +51 -0
  382. package/templates/storefront-nextjs-shadcn/components/ui/card.tsx +77 -0
  383. package/templates/storefront-nextjs-shadcn/components/ui/checkbox.tsx +30 -0
  384. package/templates/storefront-nextjs-shadcn/components/ui/dialog.tsx +137 -0
  385. package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +207 -0
  386. package/templates/storefront-nextjs-shadcn/components/ui/index.ts +67 -0
  387. package/templates/storefront-nextjs-shadcn/components/ui/input.tsx +65 -0
  388. package/templates/storefront-nextjs-shadcn/components/ui/label.tsx +26 -0
  389. package/templates/storefront-nextjs-shadcn/components/ui/pagination.tsx +205 -0
  390. package/templates/storefront-nextjs-shadcn/components/ui/radio-group.tsx +44 -0
  391. package/templates/storefront-nextjs-shadcn/components/ui/select.tsx +160 -0
  392. package/templates/storefront-nextjs-shadcn/components/ui/separator.tsx +28 -0
  393. package/templates/storefront-nextjs-shadcn/components/ui/skeleton.tsx +20 -0
  394. package/templates/storefront-nextjs-shadcn/components/ui/spinner.tsx +82 -0
  395. package/templates/storefront-nextjs-shadcn/components/ui/tabs.tsx +119 -0
  396. package/templates/storefront-nextjs-shadcn/components/ui/toast.tsx +96 -0
  397. package/templates/storefront-nextjs-shadcn/components/wishlist/index.ts +9 -0
  398. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +148 -0
  399. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +47 -0
  400. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +165 -0
  401. package/templates/storefront-nextjs-shadcn/components.json +19 -0
  402. package/templates/storefront-nextjs-shadcn/generated/.gitkeep +2 -0
  403. package/templates/storefront-nextjs-shadcn/graphql/.gitkeep +31 -0
  404. package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +168 -0
  405. package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +160 -0
  406. package/templates/storefront-nextjs-shadcn/hooks/index.ts +9 -0
  407. package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +310 -0
  408. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +286 -0
  409. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +110 -0
  410. package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.test.ts +173 -0
  411. package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.ts +298 -0
  412. package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +220 -0
  413. package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +57 -0
  414. package/templates/storefront-nextjs-shadcn/lib/config.ts +46 -0
  415. package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +254 -0
  416. package/templates/storefront-nextjs-shadcn/lib/currency/README.md +464 -0
  417. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +328 -0
  418. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +295 -0
  419. package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +27 -0
  420. package/templates/storefront-nextjs-shadcn/lib/format.test.ts +397 -0
  421. package/templates/storefront-nextjs-shadcn/lib/format.ts +226 -0
  422. package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +109 -0
  423. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +1183 -0
  424. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +267 -0
  425. package/templates/storefront-nextjs-shadcn/lib/hooks.ts +30 -0
  426. package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +89 -0
  427. package/templates/storefront-nextjs-shadcn/lib/utils.ts +6 -0
  428. package/templates/storefront-nextjs-shadcn/next.config.ts +47 -0
  429. package/templates/storefront-nextjs-shadcn/open-next.config.ts +3 -0
  430. package/templates/storefront-nextjs-shadcn/package.dev.json +30 -0
  431. package/templates/storefront-nextjs-shadcn/package.json +60 -0
  432. package/templates/storefront-nextjs-shadcn/package.json.template +46 -0
  433. package/templates/storefront-nextjs-shadcn/postcss.config.mjs +8 -0
  434. package/templates/storefront-nextjs-shadcn/proxy.ts +80 -0
  435. package/templates/storefront-nextjs-shadcn/public/icons/payment/apple-pay.svg +8 -0
  436. package/templates/storefront-nextjs-shadcn/public/icons/payment/bank-transfer.svg +10 -0
  437. package/templates/storefront-nextjs-shadcn/public/icons/payment/blik.svg +6 -0
  438. package/templates/storefront-nextjs-shadcn/public/icons/payment/cash-on-delivery.svg +11 -0
  439. package/templates/storefront-nextjs-shadcn/public/icons/payment/google-pay.svg +11 -0
  440. package/templates/storefront-nextjs-shadcn/public/icons/payment/mastercard.svg +7 -0
  441. package/templates/storefront-nextjs-shadcn/public/icons/payment/paypal.svg +7 -0
  442. package/templates/storefront-nextjs-shadcn/public/icons/payment/payu.svg +7 -0
  443. package/templates/storefront-nextjs-shadcn/public/icons/payment/przelewy24.svg +7 -0
  444. package/templates/storefront-nextjs-shadcn/public/icons/payment/stripe.svg +4 -0
  445. package/templates/storefront-nextjs-shadcn/public/icons/payment/visa.svg +5 -0
  446. package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +66 -0
  447. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +56 -0
  448. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +184 -0
  449. package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +103 -0
  450. package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +291 -0
  451. package/templates/storefront-nextjs-shadcn/tailwind.config.ts +111 -0
  452. package/templates/storefront-nextjs-shadcn/tsconfig.json +27 -0
  453. package/templates/storefront-nextjs-shadcn/wrangler.toml +9 -0
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Unit Tests for Cookie Manager
3
+ *
4
+ * These tests verify the cookie manager works correctly in both SSR and client contexts.
5
+ *
6
+ * Note: These tests require a test setup with:
7
+ * - Jest or Vitest
8
+ * - @testing-library/react for React component testing
9
+ * - Mock implementations for Next.js cookies() API
10
+ *
11
+ * To run: npm test or pnpm test
12
+ */
13
+
14
+ import { describe, it, expect, beforeEach, vi } from "vitest";
15
+ import Cookies from "js-cookie";
16
+ import {
17
+ getCookieManager,
18
+ resetCookieManager,
19
+ getCurrencyFromCookie,
20
+ setCurrencyInCookie,
21
+ removeCurrencyFromCookie,
22
+ CURRENCY_COOKIE_NAME,
23
+ } from "../cookie-manager";
24
+
25
+ // Mock js-cookie
26
+ vi.mock("js-cookie", () => ({
27
+ default: {
28
+ get: vi.fn(),
29
+ set: vi.fn(),
30
+ remove: vi.fn(),
31
+ },
32
+ }));
33
+
34
+ // Mock next/headers
35
+ vi.mock("next/headers", () => ({
36
+ cookies: vi.fn(),
37
+ }));
38
+
39
+ describe("CookieManager - Client Context", () => {
40
+ beforeEach(() => {
41
+ // Reset all mocks before each test
42
+ vi.clearAllMocks();
43
+ resetCookieManager();
44
+
45
+ // Mock window to simulate client context
46
+ global.window = {} as any;
47
+ });
48
+
49
+ describe("getCurrency()", () => {
50
+ it("should return currency from cookie when set", () => {
51
+ const mockCurrency = "EUR";
52
+ vi.mocked(Cookies.get).mockReturnValue(mockCurrency);
53
+
54
+ const manager = getCookieManager();
55
+ const result = manager.getCurrency();
56
+
57
+ expect(result).toBe(mockCurrency);
58
+ expect(Cookies.get).toHaveBeenCalledWith(CURRENCY_COOKIE_NAME);
59
+ });
60
+
61
+ it("should return null when cookie is not set", () => {
62
+ vi.mocked(Cookies.get).mockReturnValue(undefined);
63
+
64
+ const manager = getCookieManager();
65
+ const result = manager.getCurrency();
66
+
67
+ expect(result).toBeNull();
68
+ });
69
+
70
+ it("should return null and log warning when cookie read fails", () => {
71
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation();
72
+ vi.mocked(Cookies.get).mockImplementation(() => {
73
+ throw new Error("Cookie blocked");
74
+ });
75
+
76
+ const manager = getCookieManager();
77
+ const result = manager.getCurrency();
78
+
79
+ expect(result).toBeNull();
80
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
81
+ expect.stringContaining(
82
+ "[CookieManager] Failed to read currency cookie"
83
+ ),
84
+ expect.any(Error)
85
+ );
86
+
87
+ consoleWarnSpy.mockRestore();
88
+ });
89
+ });
90
+
91
+ describe("setCurrency()", () => {
92
+ it("should set currency in cookie with correct attributes", () => {
93
+ const currency = "USD";
94
+
95
+ const manager = getCookieManager();
96
+ manager.setCurrency(currency);
97
+
98
+ expect(Cookies.set).toHaveBeenCalledWith(
99
+ CURRENCY_COOKIE_NAME,
100
+ currency,
101
+ expect.objectContaining({
102
+ expires: expect.any(Number),
103
+ path: "/",
104
+ sameSite: "lax",
105
+ })
106
+ );
107
+ });
108
+
109
+ it("should handle cookie write failure gracefully", () => {
110
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation();
111
+ vi.mocked(Cookies.set).mockImplementation(() => {
112
+ throw new Error("Cookie blocked");
113
+ });
114
+
115
+ const manager = getCookieManager();
116
+
117
+ // Should not throw
118
+ expect(() => manager.setCurrency("EUR")).not.toThrow();
119
+
120
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
121
+ expect.stringContaining(
122
+ "[CookieManager] Failed to set currency cookie"
123
+ ),
124
+ expect.any(Error)
125
+ );
126
+
127
+ consoleWarnSpy.mockRestore();
128
+ });
129
+ });
130
+
131
+ describe("removeCurrency()", () => {
132
+ it("should remove currency cookie", () => {
133
+ const manager = getCookieManager();
134
+ manager.removeCurrency();
135
+
136
+ expect(Cookies.remove).toHaveBeenCalledWith(CURRENCY_COOKIE_NAME, {
137
+ path: "/",
138
+ });
139
+ });
140
+
141
+ it("should handle cookie removal failure gracefully", () => {
142
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation();
143
+ vi.mocked(Cookies.remove).mockImplementation(() => {
144
+ throw new Error("Cookie blocked");
145
+ });
146
+
147
+ const manager = getCookieManager();
148
+
149
+ // Should not throw
150
+ expect(() => manager.removeCurrency()).not.toThrow();
151
+
152
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
153
+ expect.stringContaining(
154
+ "[CookieManager] Failed to remove currency cookie"
155
+ ),
156
+ expect.any(Error)
157
+ );
158
+
159
+ consoleWarnSpy.mockRestore();
160
+ });
161
+ });
162
+
163
+ describe("isServer()", () => {
164
+ it("should return false in client context", () => {
165
+ const manager = getCookieManager();
166
+ expect(manager.isServer()).toBe(false);
167
+ });
168
+ });
169
+ });
170
+
171
+ describe("CookieManager - Server Context", () => {
172
+ beforeEach(() => {
173
+ vi.clearAllMocks();
174
+ resetCookieManager();
175
+
176
+ // Mock SSR context by removing window
177
+ delete (global as any).window;
178
+ });
179
+
180
+ describe("getCurrency()", () => {
181
+ it("should return currency from Next.js cookies API", async () => {
182
+ const mockCurrency = "GBP";
183
+ const { cookies } = await import("next/headers");
184
+
185
+ vi.mocked(cookies).mockReturnValue({
186
+ get: vi.fn().mockReturnValue({ value: mockCurrency }),
187
+ } as any);
188
+
189
+ const manager = getCookieManager();
190
+ const result = manager.getCurrency();
191
+
192
+ expect(result).toBe(mockCurrency);
193
+ });
194
+
195
+ it("should return null when cookie is not set in SSR", async () => {
196
+ const { cookies } = await import("next/headers");
197
+
198
+ vi.mocked(cookies).mockReturnValue({
199
+ get: vi.fn().mockReturnValue(undefined),
200
+ } as any);
201
+
202
+ const manager = getCookieManager();
203
+ const result = manager.getCurrency();
204
+
205
+ expect(result).toBeNull();
206
+ });
207
+
208
+ it("should handle SSR cookie read failure gracefully", async () => {
209
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation();
210
+ const { cookies } = await import("next/headers");
211
+
212
+ vi.mocked(cookies).mockImplementation(() => {
213
+ throw new Error("Headers not available");
214
+ });
215
+
216
+ const manager = getCookieManager();
217
+ const result = manager.getCurrency();
218
+
219
+ expect(result).toBeNull();
220
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
221
+ expect.stringContaining(
222
+ "[CookieManager] Failed to read currency cookie in SSR"
223
+ ),
224
+ expect.any(Error)
225
+ );
226
+
227
+ consoleWarnSpy.mockRestore();
228
+ });
229
+ });
230
+
231
+ describe("setCurrency()", () => {
232
+ it("should log warning when called in SSR context", () => {
233
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation();
234
+
235
+ const manager = getCookieManager();
236
+ manager.setCurrency("USD");
237
+
238
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
239
+ expect.stringContaining(
240
+ "[CookieManager] setCurrency called in SSR context"
241
+ )
242
+ );
243
+
244
+ consoleWarnSpy.mockRestore();
245
+ });
246
+ });
247
+
248
+ describe("removeCurrency()", () => {
249
+ it("should log warning when called in SSR context", () => {
250
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation();
251
+
252
+ const manager = getCookieManager();
253
+ manager.removeCurrency();
254
+
255
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
256
+ expect.stringContaining(
257
+ "[CookieManager] removeCurrency called in SSR context"
258
+ )
259
+ );
260
+
261
+ consoleWarnSpy.mockRestore();
262
+ });
263
+ });
264
+
265
+ describe("isServer()", () => {
266
+ it("should return true in SSR context", () => {
267
+ const manager = getCookieManager();
268
+ expect(manager.isServer()).toBe(true);
269
+ });
270
+ });
271
+ });
272
+
273
+ describe("Convenience Functions", () => {
274
+ beforeEach(() => {
275
+ vi.clearAllMocks();
276
+ resetCookieManager();
277
+ global.window = {} as any;
278
+ });
279
+
280
+ it("getCurrencyFromCookie should work", () => {
281
+ vi.mocked(Cookies.get).mockReturnValue("EUR");
282
+
283
+ const result = getCurrencyFromCookie();
284
+
285
+ expect(result).toBe("EUR");
286
+ });
287
+
288
+ it("setCurrencyInCookie should work", () => {
289
+ setCurrencyInCookie("USD");
290
+
291
+ expect(Cookies.set).toHaveBeenCalledWith(
292
+ CURRENCY_COOKIE_NAME,
293
+ "USD",
294
+ expect.any(Object)
295
+ );
296
+ });
297
+
298
+ it("removeCurrencyFromCookie should work", () => {
299
+ removeCurrencyFromCookie();
300
+
301
+ expect(Cookies.remove).toHaveBeenCalledWith(
302
+ CURRENCY_COOKIE_NAME,
303
+ expect.any(Object)
304
+ );
305
+ });
306
+ });
307
+
308
+ describe("Singleton Pattern", () => {
309
+ beforeEach(() => {
310
+ resetCookieManager();
311
+ global.window = {} as any;
312
+ });
313
+
314
+ it("should return the same instance on multiple calls", () => {
315
+ const manager1 = getCookieManager();
316
+ const manager2 = getCookieManager();
317
+
318
+ expect(manager1).toBe(manager2);
319
+ });
320
+
321
+ it("should create new instance after reset", () => {
322
+ const manager1 = getCookieManager();
323
+ resetCookieManager();
324
+ const manager2 = getCookieManager();
325
+
326
+ expect(manager1).not.toBe(manager2);
327
+ });
328
+ });
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Cookie Manager for Currency Preferences
3
+ *
4
+ * Provides a unified API for managing currency cookies in both SSR and client contexts.
5
+ * This is the single source of truth for currency preferences, replacing localStorage.
6
+ *
7
+ * Key Features:
8
+ * - Works in both Server-Side Rendering and client-side contexts
9
+ * - Type-safe API with full TypeScript support
10
+ * - Automatic fallback handling for blocked cookies
11
+ * - Zero hydration mismatch guarantee
12
+ *
13
+ * @module lib/currency/cookie-manager
14
+ */
15
+
16
+ import Cookies from 'js-cookie';
17
+
18
+ /**
19
+ * Cookie configuration constants
20
+ */
21
+ export const CURRENCY_COOKIE_NAME = 'preferred-currency';
22
+ export const CURRENCY_COOKIE_MAX_AGE = 365 * 24 * 60 * 60; // 365 days in seconds
23
+
24
+ /**
25
+ * Cookie Manager Interface
26
+ *
27
+ * Provides methods for reading and writing currency preferences to cookies.
28
+ * All methods work in both SSR and client contexts.
29
+ */
30
+ export interface CookieManager {
31
+ /**
32
+ * Get currency from cookie
33
+ * Works in both SSR and client contexts
34
+ *
35
+ * @returns The stored currency code, or null if not set
36
+ */
37
+ getCurrency(): string | null;
38
+
39
+ /**
40
+ * Set currency in cookie
41
+ * Only works in client context (SSR is read-only)
42
+ *
43
+ * @param currency - The currency code to store (e.g., "USD", "EUR")
44
+ */
45
+ setCurrency(currency: string): void;
46
+
47
+ /**
48
+ * Remove currency cookie
49
+ * Only works in client context
50
+ */
51
+ removeCurrency(): void;
52
+
53
+ /**
54
+ * Check if running in server context
55
+ *
56
+ * @returns true if running on server, false if in browser
57
+ */
58
+ isServer(): boolean;
59
+ }
60
+
61
+ /**
62
+ * Client-side Cookie Manager Implementation
63
+ * Uses js-cookie library for browser cookie operations
64
+ */
65
+ class ClientCookieManager implements CookieManager {
66
+ getCurrency(): string | null {
67
+ try {
68
+ const value = Cookies.get(CURRENCY_COOKIE_NAME);
69
+ return value || null;
70
+ } catch (error) {
71
+ console.warn("[CookieManager] Failed to read currency cookie:", error);
72
+ return null;
73
+ }
74
+ }
75
+
76
+ setCurrency(currency: string): void {
77
+ try {
78
+ // Set cookie with secure attributes
79
+ Cookies.set(CURRENCY_COOKIE_NAME, currency, {
80
+ expires: CURRENCY_COOKIE_MAX_AGE / (24 * 60 * 60), // Convert to days
81
+ path: "/",
82
+ sameSite: "lax",
83
+ // Secure flag is set automatically by js-cookie based on protocol
84
+ });
85
+ } catch (error) {
86
+ console.warn("[CookieManager] Failed to set currency cookie:", error);
87
+ // Fallback: Continue without persisting (in-memory only)
88
+ // This allows the app to function even if cookies are blocked
89
+ }
90
+ }
91
+
92
+ removeCurrency(): void {
93
+ try {
94
+ Cookies.remove(CURRENCY_COOKIE_NAME, { path: "/" });
95
+ } catch (error) {
96
+ console.warn("[CookieManager] Failed to remove currency cookie:", error);
97
+ }
98
+ }
99
+
100
+ isServer(): boolean {
101
+ return false;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Server-side Cookie Manager Implementation
107
+ * Uses Next.js cookies() API for SSR context
108
+ */
109
+ class ServerCookieManager implements CookieManager {
110
+ getCurrency(): string | null {
111
+ try {
112
+ // Dynamic import to avoid bundling next/headers in client code
113
+ // In Next.js 15+, cookies() returns a Promise
114
+ const { cookies } = require('next/headers');
115
+ const cookieStore = cookies();
116
+
117
+ // Check if it's a Promise (Next.js 15+) or direct access (Next.js 14)
118
+ if (cookieStore && typeof cookieStore.then === 'function') {
119
+ // This is async - we can't use await in a sync function
120
+ // Return null and log warning - SSR will use base currency
121
+ console.warn('[CookieManager] cookies() is async in Next.js 15+ - cannot read in sync context');
122
+ return null;
123
+ }
124
+
125
+ const cookie = cookieStore.get(CURRENCY_COOKIE_NAME);
126
+ return cookie?.value || null;
127
+ } catch (error) {
128
+ console.warn('[CookieManager] Failed to read currency cookie in SSR:', error);
129
+ return null;
130
+ }
131
+ }
132
+
133
+ setCurrency(currency: string): void {
134
+ // Setting cookies in SSR context is not supported
135
+ // This should only be called from client-side code
136
+ console.warn(
137
+ "[CookieManager] setCurrency called in SSR context - operation ignored"
138
+ );
139
+ }
140
+
141
+ removeCurrency(): void {
142
+ // Removing cookies in SSR context is not supported
143
+ console.warn(
144
+ "[CookieManager] removeCurrency called in SSR context - operation ignored"
145
+ );
146
+ }
147
+
148
+ isServer(): boolean {
149
+ return true;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Detect if code is running in server or client context
155
+ *
156
+ * This is a safe check that works in all environments:
157
+ * - Returns true during SSR
158
+ * - Returns false in browser
159
+ * - Handles edge cases gracefully
160
+ */
161
+ function isServerContext(): boolean {
162
+ try {
163
+ // typeof window === 'undefined' is the standard Next.js SSR detection
164
+ return typeof window === "undefined";
165
+ } catch {
166
+ // If we can't determine context, assume server (safer default)
167
+ // This prevents accidental client-side operations in ambiguous contexts
168
+ return true;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Cookie Manager Factory
174
+ *
175
+ * Creates the appropriate cookie manager instance based on execution context.
176
+ * This is a singleton pattern - the same instance is reused across the app.
177
+ */
178
+ let cookieManagerInstance: CookieManager | null = null;
179
+
180
+ /**
181
+ * Get the Cookie Manager singleton instance
182
+ *
183
+ * Automatically detects SSR vs client context and returns the appropriate implementation.
184
+ * The instance is cached for performance.
185
+ *
186
+ * @returns CookieManager instance (ClientCookieManager or ServerCookieManager)
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * const cookieManager = getCookieManager();
191
+ * const currency = cookieManager.getCurrency();
192
+ * ```
193
+ */
194
+ export function getCookieManager(): CookieManager {
195
+ // Reuse existing instance if available
196
+ if (cookieManagerInstance) {
197
+ return cookieManagerInstance;
198
+ }
199
+
200
+ // Create appropriate instance based on context
201
+ if (isServerContext()) {
202
+ cookieManagerInstance = new ServerCookieManager();
203
+ } else {
204
+ cookieManagerInstance = new ClientCookieManager();
205
+ }
206
+
207
+ return cookieManagerInstance;
208
+ }
209
+
210
+ /**
211
+ * Reset the cookie manager singleton
212
+ * This is primarily useful for testing
213
+ *
214
+ * @internal
215
+ */
216
+ export function resetCookieManager(): void {
217
+ cookieManagerInstance = null;
218
+ }
219
+
220
+ /**
221
+ * Convenience function: Get currency from cookie
222
+ * Shorthand for getCookieManager().getCurrency()
223
+ *
224
+ * @returns The stored currency code, or null if not set
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * const currency = getCurrencyFromCookie();
229
+ * if (currency) {
230
+ * console.log(`User prefers: ${currency}`);
231
+ * }
232
+ * ```
233
+ */
234
+ export function getCurrencyFromCookie(): string | null {
235
+ return getCookieManager().getCurrency();
236
+ }
237
+
238
+ /**
239
+ * Convenience function: Set currency in cookie
240
+ * Shorthand for getCookieManager().setCurrency(currency)
241
+ * Only works in client context
242
+ *
243
+ * @param currency - The currency code to store
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * setCurrencyInCookie('EUR');
248
+ * ```
249
+ */
250
+ export function setCurrencyInCookie(currency: string): void {
251
+ getCookieManager().setCurrency(currency);
252
+ }
253
+
254
+ /**
255
+ * Convenience function: Remove currency cookie
256
+ * Shorthand for getCookieManager().removeCurrency()
257
+ * Only works in client context
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * removeCurrencyFromCookie();
262
+ * ```
263
+ */
264
+ export function removeCurrencyFromCookie(): void {
265
+ getCookieManager().removeCurrency();
266
+ }
267
+
268
+ /**
269
+ * Get currency from cookie in Server Components (async)
270
+ *
271
+ * This is specifically for Next.js 15+ where cookies() is async.
272
+ * Use this in Server Components and Server Actions.
273
+ *
274
+ * @returns Promise<string | null> - The stored currency code, or null if not set
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * // In a Server Component
279
+ * export default async function ProductPage() {
280
+ * const currency = await getCurrencyFromCookieAsync();
281
+ * // ...
282
+ * }
283
+ * ```
284
+ */
285
+ export async function getCurrencyFromCookieAsync(): Promise<string | null> {
286
+ try {
287
+ const { cookies } = await import('next/headers');
288
+ const cookieStore = await cookies();
289
+ const cookie = cookieStore.get(CURRENCY_COOKIE_NAME);
290
+ return cookie?.value || null;
291
+ } catch (error) {
292
+ console.warn('[CookieManager] Failed to read currency cookie in SSR (async):', error);
293
+ return null;
294
+ }
295
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Currency Module
3
+ *
4
+ * Provides utilities for managing currency preferences in the storefront.
5
+ * Uses cookies as the single source of truth for SSR/client consistency.
6
+ *
7
+ * @module lib/currency
8
+ */
9
+
10
+ export {
11
+ // Types
12
+ type CookieManager,
13
+
14
+ // Constants
15
+ CURRENCY_COOKIE_NAME,
16
+ CURRENCY_COOKIE_MAX_AGE,
17
+
18
+ // Main API
19
+ getCookieManager,
20
+ resetCookieManager,
21
+
22
+ // Convenience functions
23
+ getCurrencyFromCookie,
24
+ getCurrencyFromCookieAsync,
25
+ setCurrencyInCookie,
26
+ removeCurrencyFromCookie,
27
+ } from "./cookie-manager";