@doswiftly/cli 0.1.18 → 0.1.19

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 (210) hide show
  1. package/README.md +23 -323
  2. package/dist/commands/check.js +1 -1
  3. package/dist/commands/check.js.map +1 -1
  4. package/dist/commands/deploy.d.ts.map +1 -1
  5. package/dist/commands/deploy.js +39 -20
  6. package/dist/commands/deploy.js.map +1 -1
  7. package/dist/commands/doctor.js +3 -3
  8. package/dist/commands/doctor.js.map +1 -1
  9. package/dist/commands/init.js +4 -4
  10. package/dist/commands/sdk.js +5 -5
  11. package/dist/commands/sdk.js.map +1 -1
  12. package/dist/commands/template.js +4 -4
  13. package/dist/commands/template.js.map +1 -1
  14. package/dist/commands/types.js +5 -5
  15. package/dist/commands/types.js.map +1 -1
  16. package/dist/commands/verify.js +2 -2
  17. package/dist/commands/verify.js.map +1 -1
  18. package/dist/lib/package-manager.d.ts +1 -1
  19. package/dist/lib/package-manager.js +1 -1
  20. package/package.json +1 -1
  21. package/templates/storefront-nextjs/README.md +16 -12
  22. package/templates/storefront-nextjs/app/account/orders/page.tsx +2 -2
  23. package/templates/storefront-nextjs/app/account/page.tsx +2 -2
  24. package/templates/storefront-nextjs/app/auth/login/page.tsx +1 -1
  25. package/templates/storefront-nextjs/app/auth/register/page.tsx +1 -1
  26. package/templates/storefront-nextjs/app/cart/page.tsx +1 -1
  27. package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +2 -2
  28. package/templates/storefront-nextjs/app/categories/page.tsx +1 -1
  29. package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +1 -1
  30. package/templates/storefront-nextjs/app/collections/page.tsx +1 -1
  31. package/templates/storefront-nextjs/app/page.tsx +1 -1
  32. package/templates/storefront-nextjs/app/products/[slug]/page.tsx +1 -1
  33. package/templates/storefront-nextjs/app/products/page.tsx +2 -2
  34. package/templates/storefront-nextjs/app/search/page.tsx +1 -1
  35. package/templates/storefront-nextjs/components/auth/auth-guard.tsx +1 -1
  36. package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +1 -1
  37. package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +1 -1
  38. package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +2 -2
  39. package/templates/storefront-nextjs/components/commerce/product-filters.tsx +1 -1
  40. package/templates/storefront-nextjs/components/commerce/product-price.tsx +1 -1
  41. package/templates/storefront-nextjs/components/commerce/search-input.tsx +1 -1
  42. package/templates/storefront-nextjs/components/commerce/sort-select.tsx +1 -1
  43. package/templates/storefront-nextjs/components/providers.tsx +1 -1
  44. package/templates/storefront-nextjs/lib/currency.tsx +3 -3
  45. package/templates/storefront-nextjs/lib/format.ts +1 -1
  46. package/templates/storefront-nextjs/lib/graphql-queries.ts +3 -3
  47. package/templates/storefront-nextjs/package.dev.json +1 -1
  48. package/templates/storefront-nextjs/package.json +1 -1
  49. package/templates/storefront-nextjs/package.json.template +1 -1
  50. package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +47 -0
  51. package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +47 -0
  52. package/templates/storefront-nextjs-shadcn/CLAUDE.md +148 -35
  53. package/templates/storefront-nextjs-shadcn/README.md +29 -162
  54. package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +98 -91
  55. package/templates/storefront-nextjs-shadcn/app/account/error.tsx +43 -0
  56. package/templates/storefront-nextjs-shadcn/app/account/loading.tsx +19 -0
  57. package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +53 -162
  58. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/loading.tsx +60 -0
  59. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +36 -47
  60. package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +46 -29
  61. package/templates/storefront-nextjs-shadcn/app/account/page.tsx +8 -5
  62. package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +108 -71
  63. package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +2 -86
  64. package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +2 -124
  65. package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +10 -5
  66. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/loading.tsx +17 -0
  67. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +43 -2
  68. package/templates/storefront-nextjs-shadcn/app/blog/loading.tsx +19 -0
  69. package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +2 -1
  70. package/templates/storefront-nextjs-shadcn/app/cart/loading.tsx +26 -0
  71. package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +6 -3
  72. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/category-products-client.tsx +56 -0
  73. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/loading.tsx +32 -0
  74. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +76 -59
  75. package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +8 -4
  76. package/templates/storefront-nextjs-shadcn/app/checkout/error.tsx +43 -0
  77. package/templates/storefront-nextjs-shadcn/app/checkout/loading.tsx +31 -0
  78. package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +116 -79
  79. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/loading.tsx +19 -0
  80. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +1 -1
  81. package/templates/storefront-nextjs-shadcn/app/collections/loading.tsx +18 -0
  82. package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +7 -4
  83. package/templates/storefront-nextjs-shadcn/app/global-error.tsx +117 -0
  84. package/templates/storefront-nextjs-shadcn/app/globals.css +8 -0
  85. package/templates/storefront-nextjs-shadcn/app/layout.tsx +46 -11
  86. package/templates/storefront-nextjs-shadcn/app/products/[slug]/error.tsx +43 -0
  87. package/templates/storefront-nextjs-shadcn/app/products/[slug]/loading.tsx +29 -0
  88. package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +6 -6
  89. package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +15 -61
  90. package/templates/storefront-nextjs-shadcn/app/products/loading.tsx +32 -0
  91. package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +405 -151
  92. package/templates/storefront-nextjs-shadcn/app/search/loading.tsx +18 -0
  93. package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +8 -5
  94. package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
  95. package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
  96. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +3 -1
  97. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +26 -24
  98. package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
  99. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +9 -9
  100. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +11 -37
  101. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +37 -23
  102. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +4 -3
  103. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +8 -5
  104. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +1 -1
  105. package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
  106. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +1 -1
  107. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +22 -7
  108. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +2 -2
  109. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +1 -1
  110. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +2 -2
  111. package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
  112. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +1 -1
  113. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +3 -3
  114. package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
  115. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +2 -2
  116. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +2 -1
  117. package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
  118. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +2 -12
  119. package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
  120. package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
  121. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +4 -4
  122. package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
  123. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +2 -2
  124. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +33 -23
  125. package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +2 -11
  126. package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +8 -25
  127. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +10 -19
  128. package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +17 -41
  129. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +2 -29
  130. package/templates/storefront-nextjs-shadcn/components/order/index.ts +6 -1
  131. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +3 -1
  132. package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +69 -0
  133. package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +84 -0
  134. package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +138 -0
  135. package/templates/storefront-nextjs-shadcn/components/product/index.ts +9 -2
  136. package/templates/storefront-nextjs-shadcn/components/product/product-card.fragment.graphql +49 -0
  137. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +3 -31
  138. package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
  139. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +176 -123
  140. package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
  141. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +2 -2
  142. package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +2 -2
  143. package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +5 -4
  144. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +19 -7
  145. package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +8 -23
  146. package/templates/storefront-nextjs-shadcn/components/product/product-variant.fragment.graphql +51 -0
  147. package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +1 -1
  148. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +1 -7
  149. package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +17 -2
  150. package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +3 -2
  151. package/templates/storefront-nextjs-shadcn/components/providers/index.ts +1 -1
  152. package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +30 -0
  153. package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +1 -1
  154. package/templates/storefront-nextjs-shadcn/components/returns/index.ts +2 -2
  155. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +3 -2
  156. package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
  157. package/templates/storefront-nextjs-shadcn/components/ui/form.tsx +174 -0
  158. package/templates/storefront-nextjs-shadcn/components/ui/index.ts +30 -2
  159. package/templates/storefront-nextjs-shadcn/components/ui/progress.tsx +40 -0
  160. package/templates/storefront-nextjs-shadcn/components/ui/sheet.tsx +107 -0
  161. package/templates/storefront-nextjs-shadcn/components/ui/slider.tsx +33 -0
  162. package/templates/storefront-nextjs-shadcn/components/ui/textarea.tsx +24 -0
  163. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +3 -1
  164. package/templates/storefront-nextjs-shadcn/generated/graphql.ts +12779 -0
  165. package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
  166. package/templates/storefront-nextjs-shadcn/hooks/index.ts +2 -0
  167. package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +42 -0
  168. package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +17 -295
  169. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +51 -19
  170. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +13 -9
  171. package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +4 -17
  172. package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +22 -99
  173. package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +32 -0
  174. package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
  175. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +687 -632
  176. package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +86 -0
  177. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +131 -182
  178. package/templates/storefront-nextjs-shadcn/lib/graphql/types.ts +62 -0
  179. package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +0 -17
  180. package/templates/storefront-nextjs-shadcn/next-env.d.ts +6 -0
  181. package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
  182. package/templates/storefront-nextjs-shadcn/package.json +12 -13
  183. package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
  184. package/templates/storefront-nextjs-shadcn/proxy.ts +3 -4
  185. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +41 -39
  186. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +64 -75
  187. package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +178 -177
  188. package/templates/storefront-nextjs-shadcn/tsconfig.json +23 -5
  189. package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +0 -282
  190. package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +0 -190
  191. package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +0 -263
  192. package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +0 -135
  193. package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +0 -142
  194. package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +0 -448
  195. package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +0 -307
  196. package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +0 -245
  197. package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +0 -103
  198. package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +0 -168
  199. package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +0 -160
  200. package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +0 -220
  201. package/templates/storefront-nextjs-shadcn/lib/config.ts +0 -46
  202. package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +0 -254
  203. package/templates/storefront-nextjs-shadcn/lib/currency/README.md +0 -464
  204. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +0 -328
  205. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +0 -295
  206. package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +0 -27
  207. package/templates/storefront-nextjs-shadcn/lib/format.ts +0 -226
  208. package/templates/storefront-nextjs-shadcn/lib/hooks.ts +0 -30
  209. package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +0 -66
  210. package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +0 -103
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Centralized React Query key definitions.
3
+ *
4
+ * All query keys in one place ensures:
5
+ * - Consistent cache invalidation across hooks
6
+ * - No key collisions or typos
7
+ * - Easy discovery of all cached data
8
+ * - Currency-aware keys for automatic refetch on currency change
9
+ *
10
+ * Convention:
11
+ * - `all()` — matches ALL queries in this domain (for broad invalidation)
12
+ * - `list(...)` — paginated/filtered lists
13
+ * - `detail(...)` — single entity by ID/handle
14
+ * - Currency is included in keys for price-sensitive queries
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // In a hook:
19
+ * const currency = useCurrencyStore((s) => s.currency);
20
+ * useQuery({ queryKey: queryKeys.products.detail(slug, currency), ... });
21
+ *
22
+ * // Invalidation:
23
+ * queryClient.invalidateQueries({ queryKey: queryKeys.cart.all() });
24
+ * ```
25
+ */
26
+ /** Currency type from useCurrencyStore — can be null before initialization */
27
+ type Currency = string | null;
28
+
29
+ export const queryKeys = {
30
+ products: {
31
+ all: () => ['Product'] as const,
32
+ list: (vars?: Record<string, unknown>, currency?: Currency) =>
33
+ ['Products', vars, currency] as const,
34
+ detail: (handleOrId: string, currency: Currency) =>
35
+ ['Product', handleOrId, currency] as const,
36
+ search: (query: string, currency: Currency) =>
37
+ ['ProductSearch', query, currency] as const,
38
+ },
39
+
40
+ collections: {
41
+ all: () => ['Collection'] as const,
42
+ list: (vars?: Record<string, unknown>, currency?: Currency) =>
43
+ ['Collections', vars, currency] as const,
44
+ detail: (handleOrId: string, currency: Currency) =>
45
+ ['Collection', handleOrId, currency] as const,
46
+ },
47
+
48
+ categories: {
49
+ all: () => ['Category'] as const,
50
+ list: (currency: Currency) => ['Categories', currency] as const,
51
+ detail: (slugOrId: string) => ['Category', slugOrId] as const,
52
+ },
53
+
54
+ filters: {
55
+ all: () => ['AvailableFilters'] as const,
56
+ forContext: (input?: Record<string, unknown> | null, currency?: Currency) =>
57
+ ['AvailableFilters', input, currency] as const,
58
+ },
59
+
60
+ cart: {
61
+ all: () => ['Cart'] as const,
62
+ detail: (cartId: string | null, currency: Currency) =>
63
+ ['Cart', cartId, currency] as const,
64
+ },
65
+
66
+ checkout: {
67
+ all: () => ['Checkout'] as const,
68
+ detail: (checkoutId: string | null, currency: Currency) =>
69
+ ['Checkout', checkoutId, currency] as const,
70
+ },
71
+
72
+ customer: {
73
+ all: () => ['Customer'] as const,
74
+ detail: (token: string) => ['Customer', token] as const,
75
+ order: (orderId: string, currency: Currency) => ['CustomerOrder', orderId, currency] as const,
76
+ },
77
+
78
+ loyalty: {
79
+ member: (currency: Currency) => ['LoyaltyMember', currency] as const,
80
+ rewards: (currency: Currency) => ['LoyaltyRewards', currency] as const,
81
+ transactions: (vars?: Record<string, unknown>, currency?: Currency) =>
82
+ ['LoyaltyTransactions', vars, currency] as const,
83
+ settings: () => ['LoyaltySettings'] as const,
84
+ referralStats: () => ['ReferralStats'] as const,
85
+ },
86
+ } as const;
@@ -1,67 +1,82 @@
1
1
  import { cache } from 'react';
2
- import { GraphQLClient } from 'graphql-request';
3
- import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
4
- import { getCurrencyFromCookieAsync } from '@/lib/currency/';
2
+ import { getStorefrontClient } from '@doswiftly/storefront-sdk/react/server';
3
+ import { getCurrencyFromCookieAsync } from '@doswiftly/storefront-sdk/react';
4
+ import type { NormalizedProductsResult, NormalizedCollectionsResult } from './types';
5
+ import type { TypedDocumentString } from '@/generated/graphql';
6
+ import { graphqlConfig } from './config';
5
7
 
6
- // Import config - will be injected by CLI during template generation
7
- let config: {
8
- shop: { slug: string };
9
- api: { url: string };
10
- } | null = null;
8
+ // Static imports Documents
9
+ import {
10
+ ShopDocument,
11
+ ProductDocument,
12
+ ProductsDocument,
13
+ CollectionsDocument,
14
+ CollectionDocument,
15
+ CategoriesDocument,
16
+ CategoryDocument,
17
+ AvailableFiltersDocument,
18
+ CustomerDocument,
19
+ } from '@/generated/graphql';
11
20
 
12
- try {
13
- // Dynamic import to handle cases where config doesn't exist yet
14
- const configModule = require('@/doswiftly.config');
15
- config = configModule.default || configModule;
16
- } catch (error) {
17
- // Fallback to environment variables if config file doesn't exist
18
- config = {
19
- shop: {
20
- slug: process.env.NEXT_PUBLIC_SHOP_SLUG || 'demo-shop',
21
- },
22
- api: {
23
- url: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000',
24
- },
25
- };
26
- }
21
+ // Static imports — Types
22
+ import type {
23
+ ShopQuery,
24
+ ProductQuery,
25
+ ProductQueryVariables,
26
+ ProductsQuery,
27
+ ProductsQueryVariables,
28
+ ProductSortKeys,
29
+ CollectionsQuery,
30
+ CollectionsQueryVariables,
31
+ CollectionSortKeys,
32
+ CollectionQuery,
33
+ CollectionQueryVariables,
34
+ CategoriesQuery,
35
+ CategoryQuery,
36
+ CategoryQueryVariables,
37
+ AvailableFiltersQuery,
38
+ AvailableFiltersQueryVariables,
39
+ AvailableFiltersInput,
40
+ CustomerQuery,
41
+ CustomerQueryVariables,
42
+ } from '@/generated/graphql';
27
43
 
28
- /**
29
- * Cached GraphQL client factory for Server Components
30
- *
31
- * Uses React cache() to deduplicate requests within a single render pass.
32
- * Server-side requests now include X-Preferred-Currency header from cookie
33
- * for SSR currency consistency.
34
- *
35
- * Note: This is an async function in Next.js 15+ because cookies() is async.
36
- *
37
- * @returns GraphQL client configured for server-side usage with currency support
38
- */
39
- export const getClient = cache(async () => {
40
- // Read currency from cookie (SSR-safe, async in Next.js 15+)
41
- const currency = await getCurrencyFromCookieAsync();
42
-
43
- return new GraphQLClient(`${config!.api.url}/storefront/graphql`, {
44
- headers: {
45
- 'X-Shop-Slug': config!.shop.slug,
46
- // Include X-Preferred-Currency from cookie for SSR consistency
47
- ...(currency && { 'X-Preferred-Currency': currency }),
48
- },
49
- });
50
- });
44
+ // ---------------------------------------------------------------------------
45
+ // Generic request helper with React cache
46
+ // ---------------------------------------------------------------------------
51
47
 
52
48
  /**
53
- * Generic request helper with React cache
54
- *
55
- * @param document - TypedDocumentNode from codegen
56
- * @param variables - Query variables
57
- * @returns Query result
49
+ * Generic request helper with React cache.
50
+ *
51
+ * Uses React cache() to deduplicate requests within a single render pass.
52
+ * Server-side requests include X-Preferred-Currency header from cookie.
58
53
  */
59
54
  export const request = cache(async <TResult, TVariables>(
60
- document: TypedDocumentNode<TResult, TVariables>,
61
- variables?: TVariables
55
+ document: TypedDocumentString<TResult, TVariables>,
56
+ variables?: TVariables,
57
+ extraHeaders?: Record<string, string>,
62
58
  ): Promise<TResult> => {
63
- const client = await getClient();
64
- return client.request(document, variables as any);
59
+ // Get currency from cookie for server-side requests
60
+ let currency: string | null = null;
61
+ try {
62
+ currency = await getCurrencyFromCookieAsync();
63
+ } catch {
64
+ // cookies() unavailable outside request scope (generateStaticParams, build time)
65
+ }
66
+
67
+ const headers: Record<string, string> = {
68
+ ...(currency && { 'X-Preferred-Currency': currency }),
69
+ ...extraHeaders,
70
+ };
71
+
72
+ // Use a fresh client with currency header for this request
73
+ const requestClient = getStorefrontClient({
74
+ apiUrl: graphqlConfig.apiUrl,
75
+ shopSlug: graphqlConfig.shopSlug,
76
+ defaultHeaders: headers,
77
+ });
78
+
79
+ return requestClient.query<TResult>(document.toString(), variables as Record<string, unknown>);
65
80
  });
66
81
 
67
82
  // ============================================================================
@@ -70,104 +85,49 @@ export const request = cache(async <TResult, TVariables>(
70
85
 
71
86
  /**
72
87
  * Fetch shop data with currency configuration
73
- *
74
- * Used in root layout to initialize currency store.
75
- * Cached per render to avoid duplicate requests.
76
- *
77
- * @returns Shop query result
78
- *
79
- * @example
80
- * ```typescript
81
- * // app/layout.tsx
82
- * import { fetchShop } from '@/lib/graphql/server';
83
- *
84
- * export default async function RootLayout() {
85
- * const data = await fetchShop();
86
- * return <CurrencyProvider shopData={data.shop}>...</CurrencyProvider>;
87
- * }
88
- * ```
89
88
  */
90
- export const fetchShop = cache(async () => {
91
- // Import generated types dynamically to avoid circular dependencies
92
- const { ShopDocument } = await import('@/generated/graphql');
93
- return request(ShopDocument, {});
89
+ export const fetchShop = cache(async (): Promise<ShopQuery> => {
90
+ return request(ShopDocument);
94
91
  });
95
92
 
96
93
  /**
97
94
  * Fetch single product by handle or ID
98
- *
99
- * Returns product in base currency for SSG compatibility.
100
- * Client components should refetch with preferred currency if needed.
101
- *
102
- * @param handleOrId - Product handle (string) or ID
103
- * @returns Product query result
104
- *
105
- * @example
106
- * ```typescript
107
- * // app/products/[handle]/page.tsx
108
- * import { fetchProduct } from '@/lib/graphql/server';
109
- *
110
- * export default async function ProductPage({ params }) {
111
- * const data = await fetchProduct(params.handle);
112
- * return <ProductClient initialProduct={data.product} />;
113
- * }
114
- * ```
115
95
  */
116
- export const fetchProduct = cache(async (handleOrId: string) => {
96
+ export const fetchProduct = cache(async (handleOrId: string): Promise<ProductQuery> => {
117
97
  if (!handleOrId) {
118
98
  throw new Error('Product handle or ID is required');
119
99
  }
120
-
121
- const { ProductDocument } = await import('@/generated/graphql');
122
-
123
- // Determine if it's an ID or handle
100
+
124
101
  const isId = handleOrId.startsWith('gid://');
125
-
126
- return request(ProductDocument, {
127
- ...(isId ? { id: handleOrId } : { handle: handleOrId }),
128
- });
102
+ const variables: ProductQueryVariables = isId
103
+ ? { id: handleOrId }
104
+ : { handle: handleOrId };
105
+
106
+ return request(ProductDocument, variables);
129
107
  });
130
108
 
131
109
  /**
132
110
  * Fetch products with pagination and normalization
133
- *
134
- * Automatically normalizes GraphQL edges/nodes structure to flat arrays
135
- * for easier consumption in components.
136
- *
137
- * @param variables - Query variables (first, after, query, sortKey, reverse)
138
- * @returns Normalized products response
139
- *
140
- * @example
141
- * ```typescript
142
- * // app/products/page.tsx
143
- * import { fetchProducts } from '@/lib/graphql/server';
144
- *
145
- * export default async function ProductsPage() {
146
- * const { products, pageInfo } = await fetchProducts({ first: 20 });
147
- * return products.map(product => <ProductCard key={product.id} product={product} />);
148
- * }
149
- * ```
150
111
  */
151
112
  export const fetchProducts = cache(async (variables?: {
152
113
  first?: number;
153
114
  after?: string;
154
115
  query?: string;
155
- sortKey?: string;
116
+ sortKey?: ProductSortKeys;
156
117
  reverse?: boolean;
157
- }) => {
158
- const { ProductsDocument } = await import('@/generated/graphql');
159
-
160
- const data = await request(ProductsDocument, {
118
+ }): Promise<NormalizedProductsResult> => {
119
+ const graphqlVariables: ProductsQueryVariables = {
161
120
  first: variables?.first ?? 20,
162
121
  after: variables?.after,
163
122
  query: variables?.query,
164
- sortKey: variables?.sortKey as any,
123
+ sortKey: variables?.sortKey,
165
124
  reverse: variables?.reverse,
166
- });
167
-
168
- // Normalize: edges/nodes flat array
125
+ };
126
+
127
+ const data = await request(ProductsDocument, graphqlVariables);
128
+
169
129
  return {
170
- products: data.products.edges.map((edge: any) => edge.node),
130
+ products: data.products.edges.map((edge) => edge.node),
171
131
  pageInfo: data.products.pageInfo,
172
132
  totalCount: data.products.totalCount,
173
133
  };
@@ -175,37 +135,26 @@ export const fetchProducts = cache(async (variables?: {
175
135
 
176
136
  /**
177
137
  * Fetch collections with pagination and normalization
178
- *
179
- * @param variables - Query variables
180
- * @returns Normalized collections response
181
- *
182
- * @example
183
- * ```typescript
184
- * import { fetchCollections } from '@/lib/graphql/server';
185
- *
186
- * const { collections } = await fetchCollections({ first: 10 });
187
- * ```
188
138
  */
189
139
  export const fetchCollections = cache(async (variables?: {
190
140
  first?: number;
191
141
  after?: string;
192
142
  query?: string;
193
- sortKey?: string;
143
+ sortKey?: CollectionSortKeys;
194
144
  reverse?: boolean;
195
- }) => {
196
- const { CollectionsDocument } = await import('@/generated/graphql');
197
-
198
- const data = await request(CollectionsDocument, {
145
+ }): Promise<NormalizedCollectionsResult> => {
146
+ const graphqlVariables: CollectionsQueryVariables = {
199
147
  first: variables?.first ?? 20,
200
148
  after: variables?.after,
201
149
  query: variables?.query,
202
- sortKey: variables?.sortKey as any,
150
+ sortKey: variables?.sortKey,
203
151
  reverse: variables?.reverse,
204
- });
205
-
206
- // Normalize: edges/nodes flat array
152
+ };
153
+
154
+ const data = await request(CollectionsDocument, graphqlVariables);
155
+
207
156
  return {
208
- collections: data.collections.edges.map((edge: any) => edge.node),
157
+ collections: data.collections.edges.map((edge) => edge.node),
209
158
  pageInfo: data.collections.pageInfo,
210
159
  totalCount: data.collections.totalCount,
211
160
  };
@@ -213,55 +162,55 @@ export const fetchCollections = cache(async (variables?: {
213
162
 
214
163
  /**
215
164
  * Fetch single collection by handle or ID
216
- *
217
- * @param handleOrId - Collection handle or ID
218
- * @returns Collection query result
219
- *
220
- * @example
221
- * ```typescript
222
- * import { fetchCollection } from '@/lib/graphql/server';
223
- *
224
- * const data = await fetchCollection('featured-products');
225
- * ```
226
165
  */
227
- export const fetchCollection = cache(async (handleOrId: string) => {
228
- const { CollectionDocument } = await import('@/generated/graphql');
229
-
166
+ export const fetchCollection = cache(async (handleOrId: string): Promise<CollectionQuery> => {
230
167
  const isId = handleOrId.startsWith('gid://');
231
-
232
- return request(CollectionDocument, {
233
- ...(isId ? { id: handleOrId } : { handle: handleOrId }),
234
- });
168
+ const variables: CollectionQueryVariables = isId
169
+ ? { id: handleOrId }
170
+ : { handle: handleOrId };
171
+
172
+ return request(CollectionDocument, variables);
235
173
  });
236
174
 
237
175
  /**
238
176
  * Fetch categories from GraphQL API
239
- *
240
- * @returns Categories query result with tree structure
241
177
  */
242
- export const fetchCategories = cache(async () => {
243
- const { CategoriesDocument } = await import('@/generated/graphql');
244
- return request(CategoriesDocument, {});
178
+ export const fetchCategories = cache(async (): Promise<CategoriesQuery> => {
179
+ return request(CategoriesDocument);
180
+ });
181
+
182
+ /**
183
+ * Fetch single category by slug or ID
184
+ */
185
+ export const fetchCategory = cache(async (slugOrId: string): Promise<CategoryQuery> => {
186
+ const isId = slugOrId.startsWith('gid://');
187
+ const variables: CategoryQueryVariables = isId
188
+ ? { id: slugOrId }
189
+ : { slug: slugOrId };
190
+
191
+ return request(CategoryDocument, variables);
192
+ });
193
+
194
+ /**
195
+ * Fetch available filters for product listing (SSR)
196
+ */
197
+ export const fetchAvailableFilters = cache(async (
198
+ input?: AvailableFiltersInput,
199
+ ): Promise<AvailableFiltersQuery> => {
200
+ const variables: AvailableFiltersQueryVariables = { input: input || null };
201
+ return request(AvailableFiltersDocument, variables);
245
202
  });
246
203
 
247
204
  /**
248
205
  * Fetch customer data (requires access token)
249
- *
250
- * @param accessToken - Customer access token
251
- * @returns Customer query result with addresses and orders
252
206
  */
253
- export const fetchCustomer = cache(async (accessToken: string) => {
254
- const { CustomerDocument } = await import('@/generated/graphql');
255
- return request(CustomerDocument, { customerAccessToken: accessToken });
207
+ export const fetchCustomer = cache(async (accessToken: string): Promise<CustomerQuery> => {
208
+ const variables: CustomerQueryVariables = { customerAccessToken: accessToken };
209
+ return request(CustomerDocument, variables);
256
210
  });
257
211
 
258
212
  // ============================================================================
259
213
  // TYPE EXPORTS
260
214
  // ============================================================================
261
215
 
262
- /**
263
- * Normalized response types for helper functions
264
- * These types will be inferred from generated GraphQL types
265
- */
266
- export type NormalizedProductsResponse = Awaited<ReturnType<typeof fetchProducts>>;
267
- export type NormalizedCollectionsResponse = Awaited<ReturnType<typeof fetchCollections>>;
216
+ export type { NormalizedProductsResult, NormalizedCollectionsResult } from './types';
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Shared domain types derived from GraphQL codegen.
3
+ *
4
+ * Two categories of types:
5
+ *
6
+ * 1. **Query-derived types** — extracted from query response shapes.
7
+ * Use when you need the EXACT shape returned by a specific query.
8
+ *
9
+ * 2. **Fragment-derived types** — from template-local fragments.
10
+ * Use for component props to enforce fragment-first architecture.
11
+ * Fragment = Component data contract.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // For a product card component (fragment-first):
16
+ * import type { ProductCardFields } from '@/lib/graphql/types';
17
+ * interface Props { product: ProductCardFields }
18
+ *
19
+ * // For a server helper return type (query-derived):
20
+ * import type { NormalizedProductsResult } from '@/lib/graphql/types';
21
+ * ```
22
+ */
23
+ import type {
24
+ ProductsQuery,
25
+ CollectionsQuery,
26
+ CategoriesQuery,
27
+ Category,
28
+ } from '@/generated/graphql';
29
+
30
+ // Fragment types → see lib/graphql/fragments.ts
31
+
32
+ // ============================================================================
33
+ // QUERY-DERIVED TYPES (from storefront-operations queries)
34
+ // ============================================================================
35
+
36
+ /** Single product node from Products list query */
37
+ export type ProductNode = ProductsQuery['products']['edges'][number]['node'];
38
+
39
+ /** Single collection node from Collections list query */
40
+ export type CollectionNode = CollectionsQuery['collections']['edges'][number]['node'];
41
+
42
+ // ============================================================================
43
+ // NORMALIZED RESULT TYPES (for server.ts and hooks.ts)
44
+ // ============================================================================
45
+
46
+ export interface NormalizedProductsResult {
47
+ products: ProductNode[];
48
+ pageInfo: ProductsQuery['products']['pageInfo'];
49
+ totalCount: number;
50
+ }
51
+
52
+ export interface NormalizedCollectionsResult {
53
+ collections: CollectionNode[];
54
+ pageInfo: CollectionsQuery['collections']['pageInfo'];
55
+ totalCount: number;
56
+ }
57
+
58
+ export interface NormalizedCategoriesResult {
59
+ categories: Category[];
60
+ roots: CategoriesQuery['categories']['roots'];
61
+ totalCount: number;
62
+ }
@@ -70,20 +70,3 @@ export const themeConfig = {
70
70
  } as const;
71
71
 
72
72
  export type Theme = (typeof themeConfig.themes)[number];
73
-
74
- /**
75
- * Get theme colors for a specific theme
76
- */
77
- export function getThemeColors(theme: "light" | "dark") {
78
- return themeConfig.colors[theme];
79
- }
80
-
81
- /**
82
- * Generate CSS variables for theme
83
- */
84
- export function generateThemeVariables(theme: "light" | "dark") {
85
- const colors = getThemeColors(theme);
86
- return Object.entries(colors)
87
- .map(([key, value]) => `--${key}: ${value};`)
88
- .join("\n ");
89
- }
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/dev/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -9,9 +9,7 @@
9
9
  "lint": "next lint"
10
10
  },
11
11
  "dependencies": {
12
- "@doswiftly/commerce-sdk": "file:../../../commerce-sdk",
13
- "graphql-request": "^7.1.2",
14
- "graphql": "^16.10.0",
12
+ "@doswiftly/storefront-sdk": "file:../../@doswiftly/storefront-sdk",
15
13
  "next": "latest",
16
14
  "react": "^19",
17
15
  "react-dom": "^19",
@@ -13,19 +13,16 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@doswiftly/storefront-operations": "{{STOREFRONT_OPS_VERSION}}",
16
- "@graphql-typed-document-node/core": "^3.2.0",
16
+ "@doswiftly/storefront-sdk": "^4.0.0",
17
17
  "@tanstack/react-query": "^5.62.0",
18
- "graphql-request": "^7.1.2",
19
- "graphql": "^16.10.0",
20
- "graphql-tag": "^2.12.6",
21
- "js-cookie": "^3.0.5",
22
- "next": "^15.1.9",
18
+ "next": "^16.0.0",
23
19
  "react": "^19",
24
20
  "react-dom": "^19",
25
21
  "lucide-react": "latest",
26
22
  "@radix-ui/react-accordion": "^1.2.0",
27
23
  "@radix-ui/react-checkbox": "^1.1.0",
28
24
  "@radix-ui/react-dialog": "^1.1.0",
25
+ "@radix-ui/react-slider": "^1.2.0",
29
26
  "@radix-ui/react-label": "^2.1.0",
30
27
  "@radix-ui/react-radio-group": "^1.2.0",
31
28
  "@radix-ui/react-select": "^2.1.0",
@@ -37,24 +34,26 @@
37
34
  "zustand": "^5.0.2",
38
35
  "zod": "^3.23.8",
39
36
  "sonner": "^1.7.1",
40
- "next-themes": "^0.4.4"
37
+ "next-themes": "^0.4.4",
38
+ "react-hook-form": "^7.55.0",
39
+ "@hookform/resolvers": "^5.2.0"
41
40
  },
42
41
  "devDependencies": {
43
42
  "@graphql-codegen/cli": "^5.0.3",
44
- "@graphql-codegen/typescript": "^4.1.2",
45
- "@graphql-codegen/typescript-operations": "^4.4.2",
46
- "@graphql-codegen/typed-document-node": "^5.0.12",
47
- "@next/env": "^15.1.9",
43
+ "@graphql-codegen/client-preset": "^4.5.1",
44
+ "@graphql-typed-document-node/core": "^3.2.0",
45
+ "graphql": "^16.10.0",
46
+ "@next/env": "^16.0.0",
48
47
  "@opennextjs/cloudflare": "^1.0.0",
49
- "@types/js-cookie": "^3.0.6",
50
48
  "@types/node": "latest",
51
49
  "@types/react": "latest",
52
50
  "@types/react-dom": "latest",
51
+ "schema-dts": "^1.1.2",
53
52
  "fast-check": "^3.15.0",
54
53
  "typescript": "latest",
55
54
  "@tailwindcss/postcss": "latest",
56
55
  "tailwindcss": "latest",
57
56
  "eslint": "latest",
58
- "eslint-config-next": "^15.1.9"
57
+ "eslint-config-next": "^16.0.0"
59
58
  }
60
59
  }
@@ -12,15 +12,14 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@doswiftly/storefront-operations": "{{STOREFRONT_OPS_VERSION}}",
15
- "@graphql-typed-document-node/core": "^3.2.0",
15
+ "@doswiftly/storefront-sdk": "{{SDK_VERSION}}",
16
16
  "@tanstack/react-query": "^5.62.0",
17
- "graphql-request": "^7.1.2",
18
- "graphql": "^16.10.0",
19
- "graphql-tag": "^2.12.6",
20
17
  "next": "latest",
21
18
  "react": "^19",
22
19
  "react-dom": "^19",
23
20
  "lucide-react": "latest",
21
+ "@radix-ui/react-dialog": "^1.1.0",
22
+ "@radix-ui/react-slider": "^1.2.0",
24
23
  "@radix-ui/react-slot": "^1.1.0",
25
24
  "class-variance-authority": "^0.7.0",
26
25
  "clsx": "^2.1.0",
@@ -30,9 +29,9 @@
30
29
  },
31
30
  "devDependencies": {
32
31
  "@graphql-codegen/cli": "^5.0.3",
33
- "@graphql-codegen/typescript": "^4.1.2",
34
- "@graphql-codegen/typescript-operations": "^4.4.2",
35
- "@graphql-codegen/typed-document-node": "^5.0.12",
32
+ "@graphql-codegen/client-preset": "^4.5.1",
33
+ "@graphql-typed-document-node/core": "^3.2.0",
34
+ "graphql": "^16.10.0",
36
35
  "@next/env": "latest",
37
36
  "@types/node": "latest",
38
37
  "@types/react": "latest",