@doswiftly/cli 0.2.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +899 -0
  2. package/dist/lib/proxy-server.d.ts.map +1 -1
  3. package/dist/lib/proxy-server.js +20 -7
  4. package/dist/lib/proxy-server.js.map +1 -1
  5. package/package.json +4 -2
  6. package/templates/storefront-nextjs-shadcn/app/[locale]/account/addresses/page.tsx +10 -10
  7. package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/page.tsx +9 -9
  8. package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/tracking/page.tsx +1 -1
  9. package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/page.tsx +2 -2
  10. package/templates/storefront-nextjs-shadcn/app/[locale]/account/settings/page.tsx +3 -3
  11. package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/page.tsx +41 -41
  12. package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/product-client.tsx +1 -1
  13. package/templates/storefront-nextjs-shadcn/app/api/auth/whoami/route.ts +6 -0
  14. package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +43 -43
  15. package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +9 -9
  16. package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +5 -5
  17. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +8 -8
  18. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +2 -2
  19. package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +15 -13
  20. package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +6 -6
  21. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +10 -10
  22. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +9 -9
  23. package/templates/storefront-nextjs-shadcn/components/order/delivery-estimate.tsx +2 -2
  24. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +1 -1
  25. package/templates/storefront-nextjs-shadcn/components/seo/product-json-ld.ts +1 -1
  26. package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +43 -25
  27. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +1 -1
  28. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +14 -14
  29. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +24 -35
  30. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +21 -4
  31. package/templates/storefront-nextjs-shadcn/messages/en.json +6 -6
  32. package/templates/storefront-nextjs-shadcn/messages/pl.json +6 -6
  33. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +8 -8
@@ -9,7 +9,7 @@ import type { CartLineFields } from "@/lib/graphql/fragments";
9
9
  /**
10
10
  * Per-line attribute selection surfaced to cart UI (configurator choices).
11
11
  * Mirroruje CartLine.attributeSelections z GraphQL, ale ograniczony do pól
12
- * potrzebnych do wyświetlania — surchargeAmount kumulowany w cost.totalAmount
12
+ * potrzebnych do wyświetlania — surchargeAmount kumulowany w cost.total
13
13
  * (BUNDLED) lub emitowany jako child OrderItem po checkout (SEPARATE_LINE).
14
14
  */
15
15
  export interface CartItemAttributeSelection {
@@ -74,7 +74,7 @@ export function useCartSync() {
74
74
 
75
75
  // Map GraphQL lines to display-friendly items
76
76
  const items: CartItemData[] = (cart?.lines ?? []).map((line: CartLineFields) => {
77
- const merchandiseTitle = line.merchandise.title ?? "";
77
+ const merchandiseTitle = line.variant.title ?? "";
78
78
  // Hide generic variant names like "Default" or "Default Title"
79
79
  const isDefaultVariant = /^default(\s+title)?$/i.test(merchandiseTitle);
80
80
  const productTitle = line.productTitle || merchandiseTitle;
@@ -82,19 +82,19 @@ export function useCartSync() {
82
82
 
83
83
  return {
84
84
  lineId: line.id,
85
- variantId: line.merchandise.id,
86
- productId: line.productId || line.merchandise.id,
85
+ variantId: line.variant.id,
86
+ productId: line.productId || line.variant.id,
87
87
  productHandle: line.productHandle ?? undefined,
88
88
  productTitle,
89
89
  variantTitle,
90
90
  productType: line.productType ?? undefined,
91
91
  quantity: line.quantity,
92
92
  price: {
93
- amount: line.merchandise.price.amount,
94
- currencyCode: line.merchandise.price.currencyCode,
93
+ amount: line.variant.price.amount,
94
+ currencyCode: line.variant.price.currencyCode,
95
95
  },
96
- image: line.merchandise.image || null,
97
- available: line.merchandise.available,
96
+ image: line.variant.image || null,
97
+ available: line.variant.isAvailable,
98
98
  // Faza 1 — configurator selections (backend snapshot).
99
99
  attributeSelections: (line.attributeSelections ?? []).map((sel) => ({
100
100
  attributeDefinitionId: sel.attributeDefinitionId,
@@ -108,17 +108,17 @@ export function useCartSync() {
108
108
  });
109
109
 
110
110
  const totalQuantity = cart?.totalQuantity ?? 0;
111
- const subtotal = cart?.cost?.subtotalAmount
112
- ? parseFloat(cart.cost.subtotalAmount.amount)
111
+ const subtotal = cart?.cost?.subtotal
112
+ ? parseFloat(cart.cost.subtotal.amount)
113
113
  : 0;
114
- const total = cart?.cost?.totalAmount
115
- ? parseFloat(cart.cost.totalAmount.amount)
114
+ const total = cart?.cost?.total
115
+ ? parseFloat(cart.cost.total.amount)
116
116
  : subtotal;
117
- const currency = cart?.cost?.subtotalAmount?.currencyCode ?? "PLN";
117
+ const currency = cart?.cost?.subtotal?.currencyCode ?? "PLN";
118
118
 
119
119
  // Discount data from server
120
120
  const discountCodes: string[] = (cart?.discountCodes ?? [])
121
- .filter((dc) => dc.applicable)
121
+ .filter((dc) => dc.isApplicable)
122
122
  .map((dc) => dc.code);
123
123
  const totalDiscount = subtotal - total;
124
124
 
@@ -925,15 +925,14 @@ export function useCustomer(
925
925
  options?: Omit<UseQueryOptions<CustomerQuery>, 'queryKey' | 'queryFn' | 'enabled'>
926
926
  ) {
927
927
  const execute = useExecute();
928
- const accessToken = useAuthStore((s) => s.accessToken);
928
+ // Token nie jest argumentem mutacji (auth context via httpOnly cookie / Bearer header).
929
+ // `isAuthenticated` flag (persisted) używamy do gate query — uniknąć round-trip dla guesta.
930
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
929
931
 
930
932
  return useQuery({
931
- queryKey: queryKeys.customer.detail(accessToken || ''),
932
- queryFn: () =>
933
- execute<CustomerQuery>(CustomerDocument.toString(), {
934
- customerAccessToken: accessToken!,
935
- } as CustomerQueryVariables),
936
- enabled: !!accessToken,
933
+ queryKey: queryKeys.customer.detail(isAuthenticated ? 'me' : 'guest'),
934
+ queryFn: () => execute<CustomerQuery>(CustomerDocument.toString()),
935
+ enabled: isAuthenticated,
937
936
  ...options,
938
937
  });
939
938
  }
@@ -946,15 +945,12 @@ export function useCustomerProfile(
946
945
  options?: Omit<UseQueryOptions<CustomerProfileQuery>, 'queryKey' | 'queryFn' | 'enabled'>
947
946
  ) {
948
947
  const execute = useExecute();
949
- const accessToken = useAuthStore((s) => s.accessToken);
948
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
950
949
 
951
950
  return useQuery({
952
- queryKey: queryKeys.customer.detail(accessToken || ''),
953
- queryFn: () =>
954
- execute<CustomerProfileQuery>(CustomerProfileDocument.toString(), {
955
- customerAccessToken: accessToken!,
956
- } as CustomerProfileQueryVariables),
957
- enabled: !!accessToken,
951
+ queryKey: queryKeys.customer.detail(isAuthenticated ? 'me' : 'guest'),
952
+ queryFn: () => execute<CustomerProfileQuery>(CustomerProfileDocument.toString()),
953
+ enabled: isAuthenticated,
958
954
  ...options,
959
955
  });
960
956
  }
@@ -968,7 +964,7 @@ export function useCustomerOrder(
968
964
  options?: Omit<UseQueryOptions<CustomerOrderQuery>, 'queryKey' | 'queryFn' | 'enabled'>
969
965
  ) {
970
966
  const execute = useExecute();
971
- const accessToken = useAuthStore((s) => s.accessToken);
967
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
972
968
  const currency = useCurrencyStore((s) => s.currency);
973
969
 
974
970
  return useQuery({
@@ -976,9 +972,8 @@ export function useCustomerOrder(
976
972
  queryFn: () =>
977
973
  execute<CustomerOrderQuery>(CustomerOrderDocument.toString(), {
978
974
  orderId,
979
- customerAccessToken: accessToken!,
980
975
  } as CustomerOrderQueryVariables),
981
- enabled: !!accessToken && !!orderId,
976
+ enabled: isAuthenticated && !!orderId,
982
977
  ...options,
983
978
  });
984
979
  }
@@ -991,14 +986,12 @@ export function useCustomerUpdate(
991
986
  options?: UseMutationOptions<CustomerUpdateMutation, Error, CustomerUpdateInput>
992
987
  ) {
993
988
  const execute = useExecute();
994
- const { accessToken } = useAuthStore();
995
989
  const queryClient = useQueryClient();
996
990
 
997
991
  return useMutation({
998
992
  mutationFn: (customer: CustomerUpdateInput) =>
999
993
  execute<CustomerUpdateMutation>(CustomerUpdateDocument.toString(), {
1000
994
  customer,
1001
- customerAccessToken: accessToken!,
1002
995
  } as CustomerUpdateMutationVariables),
1003
996
  onSettled: () => {
1004
997
  queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
@@ -1014,14 +1007,13 @@ export function useCustomerAddressCreate(
1014
1007
  options?: UseMutationOptions<CustomerAddressCreateMutation, Error, { address: Record<string, unknown> }>
1015
1008
  ) {
1016
1009
  const execute = useExecute();
1017
- const { accessToken } = useAuthStore();
1018
1010
  const queryClient = useQueryClient();
1019
1011
 
1020
1012
  return useMutation({
1021
1013
  mutationFn: ({ address }: { address: Record<string, unknown> }) =>
1022
1014
  execute<CustomerAddressCreateMutation>(
1023
1015
  CustomerAddressCreateDocument.toString(),
1024
- { address, customerAccessToken: accessToken! } as CustomerAddressCreateMutationVariables,
1016
+ { address } as CustomerAddressCreateMutationVariables,
1025
1017
  ),
1026
1018
  onSettled: () => {
1027
1019
  queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
@@ -1037,14 +1029,13 @@ export function useCustomerAddressUpdate(
1037
1029
  options?: UseMutationOptions<CustomerAddressUpdateMutation, Error, { id: string; address: Record<string, unknown> }>
1038
1030
  ) {
1039
1031
  const execute = useExecute();
1040
- const { accessToken } = useAuthStore();
1041
1032
  const queryClient = useQueryClient();
1042
1033
 
1043
1034
  return useMutation({
1044
1035
  mutationFn: ({ id, address }: { id: string; address: Record<string, unknown> }) =>
1045
1036
  execute<CustomerAddressUpdateMutation>(
1046
1037
  CustomerAddressUpdateDocument.toString(),
1047
- { id, address, customerAccessToken: accessToken! } as CustomerAddressUpdateMutationVariables,
1038
+ { id, address } as CustomerAddressUpdateMutationVariables,
1048
1039
  ),
1049
1040
  onSettled: () => {
1050
1041
  queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
@@ -1060,14 +1051,13 @@ export function useCustomerAddressDelete(
1060
1051
  options?: UseMutationOptions<CustomerAddressDeleteMutation, Error, string>
1061
1052
  ) {
1062
1053
  const execute = useExecute();
1063
- const { accessToken } = useAuthStore();
1064
1054
  const queryClient = useQueryClient();
1065
1055
 
1066
1056
  return useMutation({
1067
1057
  mutationFn: (id: string) =>
1068
1058
  execute<CustomerAddressDeleteMutation>(
1069
1059
  CustomerAddressDeleteDocument.toString(),
1070
- { id, customerAccessToken: accessToken! } as CustomerAddressDeleteMutationVariables,
1060
+ { id } as CustomerAddressDeleteMutationVariables,
1071
1061
  ),
1072
1062
  onSettled: () => {
1073
1063
  queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
@@ -1083,14 +1073,13 @@ export function useCustomerDefaultAddressUpdate(
1083
1073
  options?: UseMutationOptions<CustomerDefaultAddressUpdateMutation, Error, string>
1084
1074
  ) {
1085
1075
  const execute = useExecute();
1086
- const { accessToken } = useAuthStore();
1087
1076
  const queryClient = useQueryClient();
1088
1077
 
1089
1078
  return useMutation({
1090
1079
  mutationFn: (addressId: string) =>
1091
1080
  execute<CustomerDefaultAddressUpdateMutation>(
1092
1081
  CustomerDefaultAddressUpdateDocument.toString(),
1093
- { addressId, customerAccessToken: accessToken! } as CustomerDefaultAddressUpdateMutationVariables,
1082
+ { addressId } as CustomerDefaultAddressUpdateMutationVariables,
1094
1083
  ),
1095
1084
  onSettled: () => {
1096
1085
  queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
@@ -1144,13 +1133,13 @@ export function useLoyaltyMember(
1144
1133
  options?: Omit<UseQueryOptions<LoyaltyMemberQuery>, 'queryKey' | 'queryFn' | 'enabled'>
1145
1134
  ) {
1146
1135
  const execute = useExecute();
1147
- const accessToken = useAuthStore((s) => s.accessToken);
1136
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
1148
1137
  const currency = useCurrencyStore((s) => s.currency);
1149
1138
 
1150
1139
  return useQuery({
1151
1140
  queryKey: queryKeys.loyalty.member(currency),
1152
1141
  queryFn: () => execute<LoyaltyMemberQuery>(LoyaltyMemberDocument.toString()),
1153
- enabled: !!accessToken,
1142
+ enabled: isAuthenticated,
1154
1143
  ...options,
1155
1144
  });
1156
1145
  }
@@ -1162,13 +1151,13 @@ export function useLoyaltyRewards(
1162
1151
  options?: Omit<UseQueryOptions<LoyaltyRewardsQuery>, 'queryKey' | 'queryFn' | 'enabled'>
1163
1152
  ) {
1164
1153
  const execute = useExecute();
1165
- const accessToken = useAuthStore((s) => s.accessToken);
1154
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
1166
1155
  const currency = useCurrencyStore((s) => s.currency);
1167
1156
 
1168
1157
  return useQuery({
1169
1158
  queryKey: queryKeys.loyalty.rewards(currency),
1170
1159
  queryFn: () => execute<LoyaltyRewardsQuery>(LoyaltyRewardsDocument.toString()),
1171
- enabled: !!accessToken,
1160
+ enabled: isAuthenticated,
1172
1161
  ...options,
1173
1162
  });
1174
1163
  }
@@ -1181,7 +1170,7 @@ export function useLoyaltyTransactions(
1181
1170
  options?: Omit<UseQueryOptions<LoyaltyTransactionsQuery>, 'queryKey' | 'queryFn' | 'enabled'>
1182
1171
  ) {
1183
1172
  const execute = useExecute();
1184
- const accessToken = useAuthStore((s) => s.accessToken);
1173
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
1185
1174
  const currency = useCurrencyStore((s) => s.currency);
1186
1175
 
1187
1176
  return useQuery({
@@ -1193,7 +1182,7 @@ export function useLoyaltyTransactions(
1193
1182
  };
1194
1183
  return execute<LoyaltyTransactionsQuery>(LoyaltyTransactionsDocument.toString(), graphqlVariables);
1195
1184
  },
1196
- enabled: !!accessToken,
1185
+ enabled: isAuthenticated,
1197
1186
  ...options,
1198
1187
  });
1199
1188
  }
@@ -1220,12 +1209,12 @@ export function useReferralStats(
1220
1209
  options?: Omit<UseQueryOptions<ReferralStatsQuery>, 'queryKey' | 'queryFn' | 'enabled'>
1221
1210
  ) {
1222
1211
  const execute = useExecute();
1223
- const accessToken = useAuthStore((s) => s.accessToken);
1212
+ const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
1224
1213
 
1225
1214
  return useQuery({
1226
1215
  queryKey: queryKeys.loyalty.referralStats(),
1227
1216
  queryFn: () => execute<ReferralStatsQuery>(ReferralStatsDocument.toString()),
1228
- enabled: !!accessToken,
1217
+ enabled: isAuthenticated,
1229
1218
  ...options,
1230
1219
  });
1231
1220
  }
@@ -203,11 +203,28 @@ export const fetchAvailableFilters = cache(async (
203
203
  });
204
204
 
205
205
  /**
206
- * Fetch customer data (requires access token)
206
+ * Fetch customer data (requires authenticated session — httpOnly cookie OR explicit Bearer token).
207
+ *
208
+ * Server Components: czytaj `customerAccessToken` cookie via Next.js `cookies()` API,
209
+ * forward jako `Authorization: Bearer` do backend. Klient nie ma JS-access do cookie
210
+ * (XSS-safe), ale Next.js server może go odczytać via `cookies().get()`.
211
+ *
212
+ * @param accessToken Optional — gdy SSR helper ma już token (np. po login redirect SSR);
213
+ * domyślnie czyta z `cookies()` (App Router).
207
214
  */
208
- export const fetchCustomer = cache(async (accessToken: string): Promise<CustomerQuery> => {
209
- const variables: CustomerQueryVariables = { customerAccessToken: accessToken };
210
- return request(CustomerDocument, variables);
215
+ export const fetchCustomer = cache(async (accessToken?: string): Promise<CustomerQuery> => {
216
+ let token = accessToken;
217
+ if (!token) {
218
+ const { cookies } = await import('next/headers');
219
+ const cookieStore = await cookies();
220
+ token = cookieStore.get('customerAccessToken')?.value;
221
+ }
222
+
223
+ return request(
224
+ CustomerDocument,
225
+ {} as CustomerQueryVariables,
226
+ token ? { Authorization: `Bearer ${token}` } : undefined,
227
+ );
211
228
  });
212
229
 
213
230
  // ============================================================================
@@ -241,15 +241,15 @@
241
241
  "firstName": "First Name",
242
242
  "lastName": "Last Name",
243
243
  "company": "Company",
244
- "address1": "Address",
245
- "address1Placeholder": "Street and number",
246
- "address2": "Apartment, floor",
247
- "zip": "Postal Code",
248
- "zipPlaceholder": "00000",
244
+ "streetLine1": "Address",
245
+ "streetLine1Placeholder": "Street and number",
246
+ "streetLine2": "Apartment, floor",
247
+ "postalCode": "Postal Code",
248
+ "postalCodePlaceholder": "00000",
249
249
  "city": "City",
250
250
  "country": "Country",
251
251
  "countryPlaceholder": "Select country",
252
- "province": "State/Province",
252
+ "state": "State/Province",
253
253
  "phone": "Phone"
254
254
  },
255
255
  "validation": {
@@ -241,15 +241,15 @@
241
241
  "firstName": "Imię",
242
242
  "lastName": "Nazwisko",
243
243
  "company": "Firma",
244
- "address1": "Adres",
245
- "address1Placeholder": "Ulica i numer",
246
- "address2": "Mieszkanie, piętro",
247
- "zip": "Kod pocztowy",
248
- "zipPlaceholder": "00-000",
244
+ "streetLine1": "Adres",
245
+ "streetLine1Placeholder": "Ulica i numer",
246
+ "streetLine2": "Mieszkanie, piętro",
247
+ "postalCode": "Kod pocztowy",
248
+ "postalCodePlaceholder": "00-000",
249
249
  "city": "Miasto",
250
250
  "country": "Kraj",
251
251
  "countryPlaceholder": "Wybierz kraj",
252
- "province": "Województwo",
252
+ "state": "Województwo",
253
253
  "phone": "Telefon"
254
254
  },
255
255
  "validation": {
@@ -8,11 +8,11 @@ import { createStoreContext } from '@doswiftly/storefront-sdk/react';
8
8
  export interface AddressForm {
9
9
  firstName: string;
10
10
  lastName: string;
11
- address1: string;
12
- address2: string;
11
+ streetLine1: string;
12
+ streetLine2: string;
13
13
  city: string;
14
- province: string;
15
- zip: string;
14
+ state: string;
15
+ postalCode: string;
16
16
  country: string;
17
17
  phone: string;
18
18
  company: string;
@@ -68,11 +68,11 @@ export interface CheckoutState {
68
68
  const emptyAddress: AddressForm = {
69
69
  firstName: '',
70
70
  lastName: '',
71
- address1: '',
72
- address2: '',
71
+ streetLine1: '',
72
+ streetLine2: '',
73
73
  city: '',
74
- province: '',
75
- zip: '',
74
+ state: '',
75
+ postalCode: '',
76
76
  country: 'PL',
77
77
  phone: '',
78
78
  company: '',