@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.
- package/README.md +357 -0
- package/bin/doswiftly.js +2 -0
- package/dist/commands/auth-github.d.ts +6 -0
- package/dist/commands/auth-github.d.ts.map +1 -0
- package/dist/commands/auth-github.js +89 -0
- package/dist/commands/auth-github.js.map +1 -0
- package/dist/commands/auth-token.d.ts +12 -0
- package/dist/commands/auth-token.d.ts.map +1 -0
- package/dist/commands/auth-token.js +43 -0
- package/dist/commands/auth-token.js.map +1 -0
- package/dist/commands/auth.d.ts +22 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +348 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/check.d.ts +5 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +234 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +104 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/deploy.d.ts +37 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +580 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +8 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +83 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +363 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/domain.d.ts +13 -0
- package/dist/commands/domain.d.ts.map +1 -0
- package/dist/commands/domain.js +128 -0
- package/dist/commands/domain.js.map +1 -0
- package/dist/commands/env.d.ts +25 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +228 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1028 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/inspect.d.ts +12 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +162 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/migrate.d.ts +18 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +355 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/preview.d.ts +29 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +199 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/proxy.d.ts +9 -0
- package/dist/commands/proxy.d.ts.map +1 -0
- package/dist/commands/proxy.js +37 -0
- package/dist/commands/proxy.js.map +1 -0
- package/dist/commands/sdk.d.ts +5 -0
- package/dist/commands/sdk.d.ts.map +1 -0
- package/dist/commands/sdk.js +82 -0
- package/dist/commands/sdk.js.map +1 -0
- package/dist/commands/template.d.ts +107 -0
- package/dist/commands/template.d.ts.map +1 -0
- package/dist/commands/template.js +1309 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/commands/types.d.ts +5 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +82 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +103 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/upgrade.d.ts +18 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +55 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/verify.d.ts +5 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +232 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/whoami.d.ts +5 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +60 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/config/types.d.ts +173 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +48 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +416 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-url.d.ts +14 -0
- package/dist/lib/api-url.d.ts.map +1 -0
- package/dist/lib/api-url.js +24 -0
- package/dist/lib/api-url.js.map +1 -0
- package/dist/lib/api.d.ts +67 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +36 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/config.d.ts +39 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +195 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/env-storage.d.ts +140 -0
- package/dist/lib/env-storage.d.ts.map +1 -0
- package/dist/lib/env-storage.js +464 -0
- package/dist/lib/env-storage.js.map +1 -0
- package/dist/lib/errors.d.ts +61 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +204 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/i18n.d.ts +99 -0
- package/dist/lib/i18n.d.ts.map +1 -0
- package/dist/lib/i18n.js +184 -0
- package/dist/lib/i18n.js.map +1 -0
- package/dist/lib/logger.d.ts +95 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +168 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/package-manager.d.ts +91 -0
- package/dist/lib/package-manager.d.ts.map +1 -0
- package/dist/lib/package-manager.js +205 -0
- package/dist/lib/package-manager.js.map +1 -0
- package/dist/lib/proxy-server.d.ts +24 -0
- package/dist/lib/proxy-server.d.ts.map +1 -0
- package/dist/lib/proxy-server.js +173 -0
- package/dist/lib/proxy-server.js.map +1 -0
- package/dist/lib/select-with-back.d.ts +34 -0
- package/dist/lib/select-with-back.d.ts.map +1 -0
- package/dist/lib/select-with-back.js +94 -0
- package/dist/lib/select-with-back.js.map +1 -0
- package/dist/lib/shared-api-client.d.ts +40 -0
- package/dist/lib/shared-api-client.d.ts.map +1 -0
- package/dist/lib/shared-api-client.js +92 -0
- package/dist/lib/shared-api-client.js.map +1 -0
- package/dist/lib/wizard-engine.d.ts +128 -0
- package/dist/lib/wizard-engine.d.ts.map +1 -0
- package/dist/lib/wizard-engine.js +168 -0
- package/dist/lib/wizard-engine.js.map +1 -0
- package/package.json +85 -0
- package/templates/storefront-minimal/.env.example +10 -0
- package/templates/storefront-minimal/.github/workflows/build-template.yml +109 -0
- package/templates/storefront-minimal/app/globals.css +18 -0
- package/templates/storefront-minimal/app/layout.tsx +26 -0
- package/templates/storefront-minimal/app/page.tsx +93 -0
- package/templates/storefront-minimal/lib/graphql-client.ts +23 -0
- package/templates/storefront-minimal/next.config.ts +15 -0
- package/templates/storefront-minimal/open-next.config.ts +3 -0
- package/templates/storefront-minimal/package.json +30 -0
- package/templates/storefront-minimal/postcss.config.mjs +5 -0
- package/templates/storefront-minimal/tailwind.config.ts +14 -0
- package/templates/storefront-minimal/tsconfig.json +27 -0
- package/templates/storefront-minimal/wrangler.toml +9 -0
- package/templates/storefront-nextjs/.env.example +68 -0
- package/templates/storefront-nextjs/.github/workflows/build-template.yml +109 -0
- package/templates/storefront-nextjs/.github/workflows/deploy.yml +25 -0
- package/templates/storefront-nextjs/.github/workflows/preview.yml +22 -0
- package/templates/storefront-nextjs/README.md +520 -0
- package/templates/storefront-nextjs/app/account/orders/page.tsx +216 -0
- package/templates/storefront-nextjs/app/account/page.tsx +167 -0
- package/templates/storefront-nextjs/app/auth/login/page.tsx +135 -0
- package/templates/storefront-nextjs/app/auth/register/page.tsx +228 -0
- package/templates/storefront-nextjs/app/cart/page.tsx +263 -0
- package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +200 -0
- package/templates/storefront-nextjs/app/categories/page.tsx +58 -0
- package/templates/storefront-nextjs/app/checkout/page.tsx +351 -0
- package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +158 -0
- package/templates/storefront-nextjs/app/collections/page.tsx +61 -0
- package/templates/storefront-nextjs/app/globals.css +98 -0
- package/templates/storefront-nextjs/app/layout.tsx +39 -0
- package/templates/storefront-nextjs/app/page.tsx +136 -0
- package/templates/storefront-nextjs/app/products/[slug]/page.tsx +119 -0
- package/templates/storefront-nextjs/app/products/page.tsx +107 -0
- package/templates/storefront-nextjs/app/search/page.tsx +127 -0
- package/templates/storefront-nextjs/components/auth/auth-guard.tsx +94 -0
- package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +77 -0
- package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +29 -0
- package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +217 -0
- package/templates/storefront-nextjs/components/commerce/pagination.tsx +62 -0
- package/templates/storefront-nextjs/components/commerce/product-actions.tsx +135 -0
- package/templates/storefront-nextjs/components/commerce/product-filters.tsx +109 -0
- package/templates/storefront-nextjs/components/commerce/product-price.tsx +375 -0
- package/templates/storefront-nextjs/components/commerce/search-input.tsx +178 -0
- package/templates/storefront-nextjs/components/commerce/sort-select.tsx +64 -0
- package/templates/storefront-nextjs/components/commerce/variant-selector.tsx +210 -0
- package/templates/storefront-nextjs/components/layout/footer.tsx +107 -0
- package/templates/storefront-nextjs/components/layout/header.tsx +104 -0
- package/templates/storefront-nextjs/components/providers.tsx +62 -0
- package/templates/storefront-nextjs/lib/auth/routes.ts +52 -0
- package/templates/storefront-nextjs/lib/currency.tsx +140 -0
- package/templates/storefront-nextjs/lib/format.ts +159 -0
- package/templates/storefront-nextjs/lib/graphql-queries.ts +629 -0
- package/templates/storefront-nextjs/lib/hooks.ts +30 -0
- package/templates/storefront-nextjs/middleware.ts +80 -0
- package/templates/storefront-nextjs/next.config.ts +37 -0
- package/templates/storefront-nextjs/open-next.config.ts +3 -0
- package/templates/storefront-nextjs/package.dev.json +30 -0
- package/templates/storefront-nextjs/package.json +32 -0
- package/templates/storefront-nextjs/package.json.template +32 -0
- package/templates/storefront-nextjs/postcss.config.mjs +8 -0
- package/templates/storefront-nextjs/tailwind.config.ts +111 -0
- package/templates/storefront-nextjs/tsconfig.json +27 -0
- package/templates/storefront-nextjs/wrangler.toml +9 -0
- package/templates/storefront-nextjs-shadcn/.env.example +68 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +109 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +25 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +22 -0
- package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +282 -0
- package/templates/storefront-nextjs-shadcn/CLAUDE.md +96 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +190 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +263 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +135 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +142 -0
- package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +448 -0
- package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +307 -0
- package/templates/storefront-nextjs-shadcn/README.md +195 -0
- package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +245 -0
- package/templates/storefront-nextjs-shadcn/app/about/page.tsx +34 -0
- package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +215 -0
- package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +484 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +128 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/tracking/page.tsx +206 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +80 -0
- package/templates/storefront-nextjs-shadcn/app/account/page.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +195 -0
- package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +87 -0
- package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +125 -0
- package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +131 -0
- package/templates/storefront-nextjs-shadcn/app/auth/login/page.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/app/auth/register/page.tsx +20 -0
- package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +323 -0
- package/templates/storefront-nextjs-shadcn/app/blog/page.tsx +159 -0
- package/templates/storefront-nextjs-shadcn/app/brands/[slug]/page.tsx +170 -0
- package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +73 -0
- package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +165 -0
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +78 -0
- package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +75 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +1752 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/success/[orderId]/page.tsx +256 -0
- package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +74 -0
- package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +75 -0
- package/templates/storefront-nextjs-shadcn/app/contact/page.tsx +114 -0
- package/templates/storefront-nextjs-shadcn/app/error.tsx +90 -0
- package/templates/storefront-nextjs-shadcn/app/globals.css +125 -0
- package/templates/storefront-nextjs-shadcn/app/layout.tsx +57 -0
- package/templates/storefront-nextjs-shadcn/app/not-found.tsx +68 -0
- package/templates/storefront-nextjs-shadcn/app/page.tsx +21 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +246 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +343 -0
- package/templates/storefront-nextjs-shadcn/app/products/page.tsx +25 -0
- package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +192 -0
- package/templates/storefront-nextjs-shadcn/app/returns/page.tsx +77 -0
- package/templates/storefront-nextjs-shadcn/app/robots.ts +53 -0
- package/templates/storefront-nextjs-shadcn/app/search/page.tsx +16 -0
- package/templates/storefront-nextjs-shadcn/app/search/search-client.tsx +47 -0
- package/templates/storefront-nextjs-shadcn/app/shipping/page.tsx +62 -0
- package/templates/storefront-nextjs-shadcn/app/sitemap.ts +144 -0
- package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +179 -0
- package/templates/storefront-nextjs-shadcn/codegen.ts +51 -0
- package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +348 -0
- package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +144 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +258 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +132 -0
- package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +188 -0
- package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +305 -0
- package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +240 -0
- package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +177 -0
- package/templates/storefront-nextjs-shadcn/components/blog/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +119 -0
- package/templates/storefront-nextjs-shadcn/components/brand/brand-grid.tsx +64 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +140 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +48 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +112 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +84 -0
- package/templates/storefront-nextjs-shadcn/components/cart/index.ts +17 -0
- package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +121 -0
- package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +162 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/index.ts +25 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +187 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +160 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +154 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +225 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +62 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +158 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +174 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/variant-selector.tsx +210 -0
- package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +97 -0
- package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +187 -0
- package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +151 -0
- package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +166 -0
- package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +245 -0
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +246 -0
- package/templates/storefront-nextjs-shadcn/components/discount/index.ts +19 -0
- package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +113 -0
- package/templates/storefront-nextjs-shadcn/components/error/index.ts +7 -0
- package/templates/storefront-nextjs-shadcn/components/filters/attribute-filter.tsx +153 -0
- package/templates/storefront-nextjs-shadcn/components/filters/checkbox-group-filter.tsx +167 -0
- package/templates/storefront-nextjs-shadcn/components/filters/color-swatch-filter.tsx +176 -0
- package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +220 -0
- package/templates/storefront-nextjs-shadcn/components/filters/index.ts +36 -0
- package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +193 -0
- package/templates/storefront-nextjs-shadcn/components/filters/toggle-filter.tsx +132 -0
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +321 -0
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +309 -0
- package/templates/storefront-nextjs-shadcn/components/gift-card/index.ts +24 -0
- package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +72 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +85 -0
- package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +34 -0
- package/templates/storefront-nextjs-shadcn/components/home/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +108 -0
- package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +133 -0
- package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +341 -0
- package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +128 -0
- package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +147 -0
- package/templates/storefront-nextjs-shadcn/components/layout/index.ts +9 -0
- package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +211 -0
- package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +95 -0
- package/templates/storefront-nextjs-shadcn/components/layout/theme-switcher.tsx +192 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/index.ts +11 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +93 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +177 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +250 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +217 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/tier-badge.tsx +106 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +131 -0
- package/templates/storefront-nextjs-shadcn/components/order/delivery-estimate.tsx +196 -0
- package/templates/storefront-nextjs-shadcn/components/order/index.ts +11 -0
- package/templates/storefront-nextjs-shadcn/components/order/order-tracking.tsx +200 -0
- package/templates/storefront-nextjs-shadcn/components/order/shipment-card.tsx +407 -0
- package/templates/storefront-nextjs-shadcn/components/order/tracking-status.tsx +222 -0
- package/templates/storefront-nextjs-shadcn/components/order/tracking-timeline.tsx +205 -0
- package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +161 -0
- package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +250 -0
- package/templates/storefront-nextjs-shadcn/components/product/discount-badge.tsx +196 -0
- package/templates/storefront-nextjs-shadcn/components/product/index.ts +41 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +147 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +217 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-gallery.tsx +143 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +83 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +155 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +158 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-quantity-selector.tsx +111 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +238 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +58 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +169 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +220 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +338 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-summary.tsx +143 -0
- package/templates/storefront-nextjs-shadcn/components/product/sale-countdown.tsx +166 -0
- package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +213 -0
- package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +57 -0
- package/templates/storefront-nextjs-shadcn/components/product/stock-indicator.tsx +91 -0
- package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +103 -0
- package/templates/storefront-nextjs-shadcn/components/providers/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/providers/query-provider.tsx +260 -0
- package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +13 -0
- package/templates/storefront-nextjs-shadcn/components/returns/index.ts +26 -0
- package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +608 -0
- package/templates/storefront-nextjs-shadcn/components/returns/return-status-card.tsx +554 -0
- package/templates/storefront-nextjs-shadcn/components/search/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +140 -0
- package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +58 -0
- package/templates/storefront-nextjs-shadcn/components/search/search-suggestions.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/components/seo/index.ts +12 -0
- package/templates/storefront-nextjs-shadcn/components/seo/json-ld.tsx +56 -0
- package/templates/storefront-nextjs-shadcn/components/seo/product-json-ld.ts +167 -0
- package/templates/storefront-nextjs-shadcn/components/shipping/index.ts +16 -0
- package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +337 -0
- package/templates/storefront-nextjs-shadcn/components/ui/accordion.tsx +153 -0
- package/templates/storefront-nextjs-shadcn/components/ui/alert.tsx +59 -0
- package/templates/storefront-nextjs-shadcn/components/ui/badge.tsx +34 -0
- package/templates/storefront-nextjs-shadcn/components/ui/button.tsx +51 -0
- package/templates/storefront-nextjs-shadcn/components/ui/card.tsx +77 -0
- package/templates/storefront-nextjs-shadcn/components/ui/checkbox.tsx +30 -0
- package/templates/storefront-nextjs-shadcn/components/ui/dialog.tsx +137 -0
- package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +207 -0
- package/templates/storefront-nextjs-shadcn/components/ui/index.ts +67 -0
- package/templates/storefront-nextjs-shadcn/components/ui/input.tsx +65 -0
- package/templates/storefront-nextjs-shadcn/components/ui/label.tsx +26 -0
- package/templates/storefront-nextjs-shadcn/components/ui/pagination.tsx +205 -0
- package/templates/storefront-nextjs-shadcn/components/ui/radio-group.tsx +44 -0
- package/templates/storefront-nextjs-shadcn/components/ui/select.tsx +160 -0
- package/templates/storefront-nextjs-shadcn/components/ui/separator.tsx +28 -0
- package/templates/storefront-nextjs-shadcn/components/ui/skeleton.tsx +20 -0
- package/templates/storefront-nextjs-shadcn/components/ui/spinner.tsx +82 -0
- package/templates/storefront-nextjs-shadcn/components/ui/tabs.tsx +119 -0
- package/templates/storefront-nextjs-shadcn/components/ui/toast.tsx +96 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/index.ts +9 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +148 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +47 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +165 -0
- package/templates/storefront-nextjs-shadcn/components.json +19 -0
- package/templates/storefront-nextjs-shadcn/generated/.gitkeep +2 -0
- package/templates/storefront-nextjs-shadcn/graphql/.gitkeep +31 -0
- package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +168 -0
- package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +160 -0
- package/templates/storefront-nextjs-shadcn/hooks/index.ts +9 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +310 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +286 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +110 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.test.ts +173 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.ts +298 -0
- package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +220 -0
- package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +57 -0
- package/templates/storefront-nextjs-shadcn/lib/config.ts +46 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +254 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/README.md +464 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +328 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +295 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +27 -0
- package/templates/storefront-nextjs-shadcn/lib/format.test.ts +397 -0
- package/templates/storefront-nextjs-shadcn/lib/format.ts +226 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +109 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +1183 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +267 -0
- package/templates/storefront-nextjs-shadcn/lib/hooks.ts +30 -0
- package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +89 -0
- package/templates/storefront-nextjs-shadcn/lib/utils.ts +6 -0
- package/templates/storefront-nextjs-shadcn/next.config.ts +47 -0
- package/templates/storefront-nextjs-shadcn/open-next.config.ts +3 -0
- package/templates/storefront-nextjs-shadcn/package.dev.json +30 -0
- package/templates/storefront-nextjs-shadcn/package.json +60 -0
- package/templates/storefront-nextjs-shadcn/package.json.template +46 -0
- package/templates/storefront-nextjs-shadcn/postcss.config.mjs +8 -0
- package/templates/storefront-nextjs-shadcn/proxy.ts +80 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/apple-pay.svg +8 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/bank-transfer.svg +10 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/blik.svg +6 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/cash-on-delivery.svg +11 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/google-pay.svg +11 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/mastercard.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/paypal.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/payu.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/przelewy24.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/stripe.svg +4 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/visa.svg +5 -0
- package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +66 -0
- package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +56 -0
- package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +184 -0
- package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +103 -0
- package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +291 -0
- package/templates/storefront-nextjs-shadcn/tailwind.config.ts +111 -0
- package/templates/storefront-nextjs-shadcn/tsconfig.json +27 -0
- 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";
|