@kasuvia/sdk 0.0.2 → 0.0.5

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 (77) hide show
  1. package/dist/hooks/index.d.mts +430 -0
  2. package/dist/hooks/index.d.ts +430 -0
  3. package/dist/hooks/index.js +3073 -0
  4. package/dist/hooks/index.js.map +1 -0
  5. package/dist/hooks/index.mjs +2962 -0
  6. package/dist/hooks/index.mjs.map +1 -0
  7. package/dist/index-CxNLI4nw.d.ts +786 -0
  8. package/dist/index-CzVMyUs4.d.mts +492 -0
  9. package/dist/index-CzVMyUs4.d.ts +492 -0
  10. package/dist/index-Dowl5224.d.mts +786 -0
  11. package/dist/index.d.mts +48 -1739
  12. package/dist/index.d.ts +48 -1739
  13. package/dist/index.js +1544 -2359
  14. package/dist/index.js.map +1 -1
  15. package/dist/index.mjs +1527 -2258
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/sdk-context-BuarJbeb.d.ts +75 -0
  18. package/dist/sdk-context-Db8kf1Tv.d.mts +75 -0
  19. package/dist/services/index.d.mts +2 -0
  20. package/dist/services/index.d.ts +2 -0
  21. package/dist/services/index.js +2369 -0
  22. package/dist/services/index.js.map +1 -0
  23. package/dist/services/index.mjs +2252 -0
  24. package/dist/services/index.mjs.map +1 -0
  25. package/package.json +11 -2
  26. package/src/config/index.ts +10 -91
  27. package/src/constants.ts +13 -0
  28. package/src/context/sdk-context.tsx +70 -0
  29. package/src/graphql/collections.graphql.ts +2 -10
  30. package/src/graphql/products.graphql.ts +215 -65
  31. package/src/graphql/wishlist.graphql.ts +7 -6
  32. package/src/hooks/index.ts +2 -0
  33. package/src/hooks/use-auth.ts +82 -158
  34. package/src/hooks/use-bespoke-availability.ts +25 -8
  35. package/src/hooks/use-cart.ts +222 -119
  36. package/src/hooks/use-cms.ts +179 -86
  37. package/src/hooks/use-collections.ts +52 -15
  38. package/src/hooks/use-consultation-stream.ts +4 -3
  39. package/src/hooks/use-consultations.ts +120 -51
  40. package/src/hooks/use-countries.ts +35 -13
  41. package/src/hooks/use-currencies.ts +26 -13
  42. package/src/hooks/use-google-translate.ts +22 -8
  43. package/src/hooks/use-languages.ts +40 -20
  44. package/src/hooks/use-mobile.ts +23 -0
  45. package/src/hooks/use-order-stream.ts +9 -5
  46. package/src/hooks/use-orders.ts +99 -44
  47. package/src/hooks/use-payment.ts +108 -40
  48. package/src/hooks/use-products.ts +251 -53
  49. package/src/hooks/use-service-booking.ts +152 -90
  50. package/src/hooks/use-states.ts +35 -15
  51. package/src/hooks/use-wishlist.ts +92 -91
  52. package/src/index.ts +26 -30
  53. package/src/services/auth.service.ts +209 -167
  54. package/src/services/bespoke.service.ts +12 -4
  55. package/src/services/cart.service.ts +172 -70
  56. package/src/services/cms.service.ts +70 -35
  57. package/src/services/collection.service.ts +22 -5
  58. package/src/services/config.service.ts +34 -14
  59. package/src/services/consultation.service.ts +72 -18
  60. package/src/services/logistics.service.ts +35 -11
  61. package/src/services/notifications.service.ts +27 -12
  62. package/src/services/order.service.ts +74 -15
  63. package/src/services/payment.service.ts +86 -31
  64. package/src/services/product.service.ts +279 -39
  65. package/src/services/service-booking.service.ts +98 -26
  66. package/src/services/wishlist.service.ts +69 -25
  67. package/src/types/auth/index.ts +6 -4
  68. package/src/types/business/index.ts +7 -2
  69. package/src/types/payments/index.ts +3 -3
  70. package/src/types/services/index.ts +2 -5
  71. package/src/types/store/index.ts +38 -3
  72. package/src/utils/error-handler.ts +10 -5
  73. package/src/utils/storage.ts +48 -26
  74. package/src/utils/validators.ts +85 -0
  75. package/src/client/authClient.ts +0 -135
  76. package/src/client/graphqlClient.ts +0 -53
  77. package/src/client/httpClient.ts +0 -298
@@ -1,118 +1,180 @@
1
1
  /**
2
2
  * Service Booking Hooks
3
- *
3
+ *
4
4
  * React Query hooks for service bookings.
5
+ * config and accessToken are optional — if omitted, they are read from SdkContext.
5
6
  */
6
7
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
7
- import { serviceBookingService } from '../services/service-booking.service'
8
+ import {
9
+ getAvailableSlots,
10
+ bookAppointment,
11
+ getAppointments,
12
+ getAppointment,
13
+ cancelAppointment,
14
+ getDeliverables,
15
+ approveDeliverable,
16
+ requestRevision,
17
+ } from '../services/service-booking.service'
18
+ import type { SdkConfig } from '../config'
8
19
  import type {
9
- ServiceAppointment,
10
20
  ServiceAppointmentCreatePayload,
11
- ServiceDeliverable,
12
21
  } from '../types/services'
22
+ import { logger } from '../utils'
23
+ import { useSdkContext, resolveHookArgs } from '../context/sdk-context'
13
24
 
14
- /**
15
- * Get available slots for a service product
16
- */
17
25
  export function useAvailableSlots(
18
- product_id: string,
19
- date_from: string,
20
- date_to: string
26
+ arg1?: SdkConfig | string,
27
+ arg2?: string,
28
+ arg3?: string,
29
+ arg4?: string
21
30
  ) {
22
- return useQuery({
23
- queryKey: ["available-slots", product_id, date_from, date_to],
24
- queryFn: () =>
25
- serviceBookingService.getAvailableSlots(product_id, date_from, date_to),
26
- enabled: !!product_id && !!date_from && !!date_to,
27
- })
28
- }
29
-
30
- /**
31
- * Book a service appointment
32
- */
33
- export function useBookAppointment() {
34
- const queryClient = useQueryClient()
31
+ const ctx = useSdkContext()
32
+ const isArg1Config = typeof arg1 === 'object' && arg1 !== null && 'businessId' in arg1
33
+ const explicitConfig = isArg1Config ? (arg1 as SdkConfig) : undefined
34
+ const product_id = isArg1Config ? arg2 : (typeof arg1 === 'string' ? arg1 : undefined)
35
+ const date_from = isArg1Config ? arg3 : (typeof arg2 === 'string' ? arg2 : undefined)
36
+ const date_to = isArg1Config ? arg4 : (typeof arg3 === 'string' ? arg3 : undefined)
35
37
 
36
- return useMutation({
37
- mutationFn: serviceBookingService.bookAppointment,
38
- onSuccess: () => {
39
- queryClient.invalidateQueries({ queryKey: ["appointments"] })
40
- },
41
- })
38
+ try {
39
+ const { config: cfg } = resolveHookArgs(explicitConfig, undefined, ctx)
40
+ return useQuery({
41
+ queryKey: ["available-slots", product_id, date_from, date_to],
42
+ queryFn: () => getAvailableSlots(cfg, product_id!, date_from!, date_to!),
43
+ enabled: !!product_id && !!date_from && !!date_to,
44
+ })
45
+ } catch (error) {
46
+ logger.warn('[useAvailableSlots] Called outside QueryClientProvider, returning undefined')
47
+ return { data: undefined, isLoading: false, error: null, isFetching: false, refetch: () => Promise.resolve({ data: undefined }) } as any
48
+ }
42
49
  }
43
50
 
44
- /**
45
- * Get all appointments for the current user
46
- */
47
- export function useAppointments() {
48
- return useQuery({
49
- queryKey: ["appointments"],
50
- queryFn: serviceBookingService.getAppointments,
51
- })
51
+ export function useBookAppointment(config?: SdkConfig, accessToken?: string | null) {
52
+ const ctx = useSdkContext()
53
+ try {
54
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)
55
+ const queryClient = useQueryClient()
56
+ return useMutation({
57
+ mutationFn: (payload: ServiceAppointmentCreatePayload) =>
58
+ token ? bookAppointment(cfg, token, payload) : Promise.reject('No access token'),
59
+ onSuccess: () => {
60
+ queryClient.invalidateQueries({ queryKey: ["appointments"] })
61
+ },
62
+ })
63
+ } catch (error) {
64
+ logger.warn('[useBookAppointment] Called outside QueryClientProvider, returning no-op mutation')
65
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
66
+ }
52
67
  }
53
68
 
54
- /**
55
- * Get a single appointment by ID
56
- */
57
- export function useAppointment(id: string) {
58
- return useQuery({
59
- queryKey: ["appointment", id],
60
- queryFn: () => serviceBookingService.getAppointment(id),
61
- enabled: !!id,
62
- })
69
+ export function useAppointments(config?: SdkConfig, accessToken?: string | null) {
70
+ const ctx = useSdkContext()
71
+ try {
72
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)
73
+ return useQuery({
74
+ queryKey: ["appointments"],
75
+ queryFn: () => token ? getAppointments(cfg, token) : Promise.resolve([]),
76
+ enabled: !!token,
77
+ })
78
+ } catch (error) {
79
+ logger.warn('[useAppointments] Called outside QueryClientProvider, returning empty list')
80
+ return { data: undefined, isLoading: false, error: null, isFetching: false, refetch: () => Promise.resolve({ data: undefined }) } as any
81
+ }
63
82
  }
64
83
 
65
- /**
66
- * Cancel an appointment
67
- */
68
- export function useCancelAppointment() {
69
- const queryClient = useQueryClient()
84
+ export function useAppointment(arg1?: SdkConfig | string, arg2?: string | null, arg3?: string) {
85
+ const ctx = useSdkContext()
86
+ const isArg1Config = typeof arg1 === 'object' && arg1 !== null && 'businessId' in arg1
87
+ const explicitConfig = isArg1Config ? (arg1 as SdkConfig) : undefined
88
+ const explicitToken = isArg1Config ? (arg2 as string | null) : undefined
89
+ const id = isArg1Config ? arg3 : (typeof arg1 === 'string' ? arg1 : undefined)
70
90
 
71
- return useMutation({
72
- mutationFn: (id: string) => serviceBookingService.cancelAppointment(id),
73
- onSuccess: () => {
74
- queryClient.invalidateQueries({ queryKey: ["appointments"] })
75
- },
76
- })
91
+ try {
92
+ const { config: cfg, accessToken: token } = resolveHookArgs(explicitConfig, explicitToken, ctx)
93
+ return useQuery({
94
+ queryKey: ["appointment", id],
95
+ queryFn: () => token && id ? getAppointment(cfg, token, id) : Promise.resolve(undefined),
96
+ enabled: !!id && !!token,
97
+ })
98
+ } catch (error) {
99
+ logger.warn('[useAppointment] Called outside QueryClientProvider, returning undefined')
100
+ return { data: undefined, isLoading: false, error: null, isFetching: false, refetch: () => Promise.resolve({ data: undefined }) } as any
101
+ }
77
102
  }
78
103
 
79
- /**
80
- * Get deliverables for a project-based service order
81
- */
82
- export function useDeliverables(order_id: string) {
83
- return useQuery({
84
- queryKey: ["deliverables", order_id],
85
- queryFn: () => serviceBookingService.getDeliverables(order_id),
86
- enabled: !!order_id,
87
- })
104
+ export function useCancelAppointment(config?: SdkConfig, accessToken?: string | null) {
105
+ const ctx = useSdkContext()
106
+ try {
107
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)
108
+ const queryClient = useQueryClient()
109
+ return useMutation({
110
+ mutationFn: (id: string) =>
111
+ token ? cancelAppointment(cfg, token, id) : Promise.reject('No access token'),
112
+ onSuccess: () => {
113
+ queryClient.invalidateQueries({ queryKey: ["appointments"] })
114
+ },
115
+ })
116
+ } catch (error) {
117
+ logger.warn('[useCancelAppointment] Called outside QueryClientProvider, returning no-op mutation')
118
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
119
+ }
88
120
  }
89
121
 
90
- /**
91
- * Approve a deliverable
92
- */
93
- export function useApproveDeliverable(order_id: string, deliverable_id: string) {
94
- const queryClient = useQueryClient()
122
+ export function useDeliverables(arg1?: SdkConfig | string, arg2?: string | null, arg3?: string) {
123
+ const ctx = useSdkContext()
124
+ const isArg1Config = typeof arg1 === 'object' && arg1 !== null && 'businessId' in arg1
125
+ const explicitConfig = isArg1Config ? (arg1 as SdkConfig) : undefined
126
+ const explicitToken = isArg1Config ? (arg2 as string | null) : undefined
127
+ const order_id = isArg1Config ? arg3 : (typeof arg1 === 'string' ? arg1 : undefined)
95
128
 
96
- return useMutation({
97
- mutationFn: () =>
98
- serviceBookingService.approveDeliverable(order_id, deliverable_id),
99
- onSuccess: () => {
100
- queryClient.invalidateQueries({ queryKey: ["deliverables", order_id] })
101
- },
102
- })
129
+ try {
130
+ const { config: cfg, accessToken: token } = resolveHookArgs(explicitConfig, explicitToken, ctx)
131
+ return useQuery({
132
+ queryKey: ["deliverables", order_id],
133
+ queryFn: () => token && order_id ? getDeliverables(cfg, token, order_id) : Promise.resolve([]),
134
+ enabled: !!order_id && !!token,
135
+ })
136
+ } catch (error) {
137
+ logger.warn('[useDeliverables] Called outside QueryClientProvider, returning empty list')
138
+ return { data: undefined, isLoading: false, error: null, isFetching: false, refetch: () => Promise.resolve({ data: undefined }) } as any
139
+ }
103
140
  }
104
141
 
105
- /**
106
- * Request revision for a deliverable
107
- */
108
- export function useRequestRevision(order_id: string, deliverable_id: string) {
109
- const queryClient = useQueryClient()
142
+ export function useApproveDeliverable(config?: SdkConfig, accessToken?: string | null, order_id?: string, deliverable_id?: string) {
143
+ const ctx = useSdkContext()
144
+ try {
145
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)
146
+ const queryClient = useQueryClient()
147
+ return useMutation({
148
+ mutationFn: () =>
149
+ token && order_id && deliverable_id
150
+ ? approveDeliverable(cfg, token, order_id, deliverable_id)
151
+ : Promise.reject('No access token'),
152
+ onSuccess: () => {
153
+ queryClient.invalidateQueries({ queryKey: ["deliverables", order_id] })
154
+ },
155
+ })
156
+ } catch (error) {
157
+ logger.warn('[useApproveDeliverable] Called outside QueryClientProvider, returning no-op mutation')
158
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
159
+ }
160
+ }
110
161
 
111
- return useMutation({
112
- mutationFn: (feedback: string) =>
113
- serviceBookingService.requestRevision(order_id, deliverable_id, feedback),
114
- onSuccess: () => {
115
- queryClient.invalidateQueries({ queryKey: ["deliverables", order_id] })
116
- },
117
- })
162
+ export function useRequestRevision(config?: SdkConfig, accessToken?: string | null, order_id?: string, deliverable_id?: string) {
163
+ const ctx = useSdkContext()
164
+ try {
165
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)
166
+ const queryClient = useQueryClient()
167
+ return useMutation({
168
+ mutationFn: (feedback: string) =>
169
+ token && order_id && deliverable_id
170
+ ? requestRevision(cfg, token, order_id, deliverable_id, feedback)
171
+ : Promise.reject('No access token'),
172
+ onSuccess: () => {
173
+ queryClient.invalidateQueries({ queryKey: ["deliverables", order_id] })
174
+ },
175
+ })
176
+ } catch (error) {
177
+ logger.warn('[useRequestRevision] Called outside QueryClientProvider, returning no-op mutation')
178
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
179
+ }
118
180
  }
@@ -2,27 +2,47 @@
2
2
  * States Hook
3
3
  *
4
4
  * React Query hook for fetching states for a country.
5
+ * Stateless - requires config parameter.
5
6
  */
6
7
  import { useQuery } from '@tanstack/react-query'
7
- import { authAxios } from '../client/httpClient'
8
+ import type { SdkConfig } from '../config'
8
9
  import type { State } from '../types/ui'
10
+ import { logger } from '../utils'
11
+ import { useSdkContext, resolveHookArgs } from '../context/sdk-context'
9
12
 
10
- export async function getStates(countryIso: string): Promise<State[]> {
13
+ export async function getStates(config: SdkConfig, countryIso: string): Promise<State[]> {
11
14
  if (!countryIso) return []
12
- const { data } = await authAxios.get<State[]>('auth/countries/', {
13
- params: { iso: countryIso }
15
+ const response = await fetch(`${config.backendUrls.auth}/auth/countries/?iso=${countryIso}`, {
16
+ headers: {
17
+ "X-Storefront-Key": config.storefrontApiKey,
18
+ "X-Business-ID": config.businessId,
19
+ },
14
20
  })
15
- return data
21
+ if (!response.ok) throw new Error('Failed to fetch states')
22
+ return response.json()
16
23
  }
17
24
 
18
- export function useStates(countryIso: string) {
19
- return useQuery({
20
- queryKey: ['states', countryIso],
21
- queryFn: () => getStates(countryIso),
22
- enabled: !!countryIso,
23
- retry: 1,
24
- refetchOnWindowFocus: false,
25
- refetchOnReconnect: false,
26
- staleTime: 24 * 60 * 60 * 1000, // 24 hours
27
- })
25
+ export function useStates(countryIso: string, config?: SdkConfig) {
26
+ const ctx = useSdkContext()
27
+ try {
28
+ const { config: cfg } = resolveHookArgs(config, undefined, ctx)
29
+ return useQuery({
30
+ queryKey: ['states', countryIso],
31
+ queryFn: () => getStates(cfg, countryIso),
32
+ enabled: !!countryIso,
33
+ retry: 1,
34
+ refetchOnWindowFocus: false,
35
+ refetchOnReconnect: false,
36
+ staleTime: 24 * 60 * 60 * 1000,
37
+ })
38
+ } catch (error) {
39
+ logger.warn('[useStates] Called outside QueryClientProvider, returning empty list')
40
+ return {
41
+ data: [],
42
+ isLoading: false,
43
+ error: null,
44
+ isFetching: false,
45
+ refetch: () => Promise.resolve({ data: [] }),
46
+ } as any
47
+ }
28
48
  }
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Wishlist Hooks
3
- *
3
+ *
4
4
  * React Query hooks for wishlist operations.
5
+ * config and accessToken are optional — if omitted, they are read from SdkContext.
5
6
  */
6
7
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
7
8
  import {
@@ -11,7 +12,9 @@ import {
11
12
  removeProductFromWishlist,
12
13
  checkInWishlist,
13
14
  } from '../services/wishlist.service'
15
+ import type { SdkConfig } from '../config'
14
16
  import { getCacheTier } from '../utils/cache'
17
+ import { useSdkContext, resolveHookArgs } from '../context/sdk-context'
15
18
 
16
19
  export const wishlistKeys = {
17
20
  all: ["wishlist"] as const,
@@ -19,103 +22,101 @@ export const wishlistKeys = {
19
22
  check: (productId: string) => [...wishlistKeys.all, "check", productId] as const,
20
23
  }
21
24
 
22
- /**
23
- * Hook for fetching the customer's wishlist.
24
- * Authentication-gated only - returns empty for unauthenticated users.
25
- */
26
- export function useWishlist(params?: { page?: number; pageSize?: number }) {
27
- return useQuery({
28
- queryKey: [...wishlistKeys.list(), params],
29
- queryFn: () => getWishlist(params),
30
- // medium — wishlist changes infrequently
31
- ...getCacheTier('medium'),
32
- })
33
- }
25
+ export function useWishlist(arg1?: SdkConfig | { page?: number; pageSize?: number }, arg2?: string | null, arg3?: { page?: number; pageSize?: number }) {
26
+ const ctx = useSdkContext()
27
+ const isArg1Config = typeof arg1 === 'object' && arg1 !== null && 'businessId' in arg1
28
+ const explicitConfig = isArg1Config ? (arg1 as SdkConfig) : undefined
29
+ const explicitToken = isArg1Config ? arg2 : undefined
30
+ const params = isArg1Config ? arg3 : (arg1 as { page?: number; pageSize?: number } | undefined)
34
31
 
35
- /**
36
- * Hook for checking if a product is in the wishlist.
37
- * Authentication-gated only - returns false for unauthenticated users.
38
- */
39
- export function useCheckInWishlist(productId: string) {
40
- return useQuery({
41
- queryKey: wishlistKeys.check(productId),
42
- queryFn: () => checkInWishlist(productId),
43
- // low — wishlist check changes frequently
44
- ...getCacheTier('low'),
45
- enabled: !!productId,
46
- })
32
+ try {
33
+ const { config: cfg, accessToken: token } = resolveHookArgs(explicitConfig, explicitToken, ctx)
34
+ return useQuery({
35
+ queryKey: [...wishlistKeys.list(), params],
36
+ queryFn: () => token ? getWishlist(cfg, token, params) : Promise.resolve(undefined),
37
+ enabled: !!token,
38
+ ...getCacheTier('medium'),
39
+ })
40
+ } catch (error) {
41
+ console.warn('[useWishlist] Called outside QueryClientProvider, returning empty wishlist')
42
+ return { data: undefined, isLoading: false, error: null, isFetching: false, refetch: () => Promise.resolve({ data: undefined }) } as any
43
+ }
47
44
  }
48
45
 
49
- /**
50
- * Hook for adding a product to the wishlist.
51
- * Authentication-gated only - triggers login modal for unauthenticated users.
52
- */
53
- export function useAddToWishlist() {
54
- const queryClient = useQueryClient()
55
-
56
- return useMutation({
57
- mutationFn: ({ productId, notes }: { productId: string; notes?: string }) => {
58
- return addToWishlist(productId, notes)
59
- },
60
- onSuccess: () => {
61
- // Invalidate all wishlist queries to ensure cache is updated
62
- queryClient.invalidateQueries({ queryKey: wishlistKeys.all })
63
- // Also invalidate specific check queries for all products
64
- queryClient.invalidateQueries({ queryKey: ['wishlist', 'check'] })
65
- },
66
- })
67
- }
46
+ export function useCheckInWishlist(arg1?: SdkConfig | string, arg2?: string | null, arg3?: string) {
47
+ const ctx = useSdkContext()
48
+ const isArg1Config = typeof arg1 === 'object' && arg1 !== null && 'businessId' in arg1
49
+ const explicitConfig = isArg1Config ? (arg1 as SdkConfig) : undefined
50
+ const explicitToken = isArg1Config ? (arg2 as string | null) : undefined
51
+ const productId = isArg1Config ? arg3 : (typeof arg1 === 'string' ? arg1 : undefined)
68
52
 
69
- /**
70
- * Hook for removing an item from the wishlist by item ID.
71
- */
72
- export function useRemoveFromWishlist() {
73
- const queryClient = useQueryClient()
74
-
75
- return useMutation({
76
- mutationFn: (itemId: string) => {
77
- return removeFromWishlist(itemId)
78
- },
79
- onSuccess: () => {
80
- // Invalidate all wishlist queries to ensure cache is updated
81
- queryClient.invalidateQueries({ queryKey: wishlistKeys.all })
82
- // Also invalidate specific check queries for all products
83
- queryClient.invalidateQueries({ queryKey: ['wishlist', 'check'] })
84
- },
85
- })
53
+ try {
54
+ const { config: cfg, accessToken: token } = resolveHookArgs(explicitConfig, explicitToken, ctx)
55
+ return useQuery({
56
+ queryKey: wishlistKeys.check(productId ?? ''),
57
+ queryFn: () => token && productId ? checkInWishlist(cfg, token, productId) : Promise.resolve({ in_wishlist: false }),
58
+ ...getCacheTier('low'),
59
+ enabled: !!productId && !!token,
60
+ })
61
+ } catch (error) {
62
+ console.warn('[useCheckInWishlist] Called outside QueryClientProvider, returning false')
63
+ return { data: false, isLoading: false, error: null, isFetching: false, refetch: () => Promise.resolve({ data: false }) } as any
64
+ }
86
65
  }
87
66
 
88
- /**
89
- * Hook for removing a product from the wishlist by product ID.
90
- */
91
- export function useRemoveProductFromWishlist() {
92
- const queryClient = useQueryClient()
93
-
94
- return useMutation({
95
- mutationFn: (productId: string) => {
96
- return removeProductFromWishlist(productId)
97
- },
98
- onSuccess: () => {
99
- // Invalidate all wishlist queries to ensure cache is updated
100
- queryClient.invalidateQueries({ queryKey: wishlistKeys.all })
101
- // Also invalidate specific check queries for all products
102
- queryClient.invalidateQueries({ queryKey: ['wishlist', 'check'] })
103
- },
104
- })
67
+ export function useAddToWishlist(config?: SdkConfig, accessToken?: string | null) {
68
+ const ctx = useSdkContext()
69
+ try {
70
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)
71
+ const queryClient = useQueryClient()
72
+ return useMutation({
73
+ mutationFn: ({ productId, notes }: { productId: string; notes?: string }) =>
74
+ token ? addToWishlist(cfg, token, productId, notes) : Promise.reject('No access token'),
75
+ onSuccess: () => {
76
+ queryClient.invalidateQueries({ queryKey: wishlistKeys.all })
77
+ queryClient.invalidateQueries({ queryKey: ['wishlist', 'check'] })
78
+ },
79
+ })
80
+ } catch (error) {
81
+ console.warn('[useAddToWishlist] Called outside QueryClientProvider, returning no-op mutation')
82
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
83
+ }
105
84
  }
106
85
 
107
- /**
108
- * Hook for getting wishlist count.
109
- * Authentication-gated only - returns 0 for unauthenticated users.
110
- */
111
- export function useWishlistCount() {
112
- const backendQuery = useQuery({
113
- queryKey: wishlistKeys.list(),
114
- queryFn: () => getWishlist(),
115
- // low — wishlist count changes frequently
116
- ...getCacheTier('low'),
117
- select: (data) => data.total,
118
- })
86
+ export function useRemoveFromWishlist(config?: SdkConfig, accessToken?: string | null) {
87
+ const ctx = useSdkContext()
88
+ try {
89
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)
90
+ const queryClient = useQueryClient()
91
+ return useMutation({
92
+ mutationFn: (itemId: string) =>
93
+ token ? removeFromWishlist(cfg, token, itemId) : Promise.reject('No access token'),
94
+ onSuccess: () => {
95
+ queryClient.invalidateQueries({ queryKey: wishlistKeys.all })
96
+ queryClient.invalidateQueries({ queryKey: ['wishlist', 'check'] })
97
+ },
98
+ })
99
+ } catch (error) {
100
+ console.warn('[useRemoveFromWishlist] Called outside QueryClientProvider, returning no-op mutation')
101
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
102
+ }
103
+ }
119
104
 
120
- return backendQuery
105
+ export function useRemoveProductFromWishlist(config?: SdkConfig, accessToken?: string | null) {
106
+ const ctx = useSdkContext()
107
+ try {
108
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)
109
+ const queryClient = useQueryClient()
110
+ return useMutation({
111
+ mutationFn: (productId: string) =>
112
+ token ? removeProductFromWishlist(cfg, token, productId) : Promise.reject('No access token'),
113
+ onSuccess: () => {
114
+ queryClient.invalidateQueries({ queryKey: wishlistKeys.all })
115
+ queryClient.invalidateQueries({ queryKey: ['wishlist', 'check'] })
116
+ },
117
+ })
118
+ } catch (error) {
119
+ console.warn('[useRemoveProductFromWishlist] Called outside QueryClientProvider, returning no-op mutation')
120
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
121
+ }
121
122
  }
package/src/index.ts CHANGED
@@ -2,22 +2,29 @@
2
2
  * @kasuvia/sdk
3
3
  *
4
4
  * Kasuvia SDK - Core data fetching, authentication, and API client layer
5
+ * Stateless architecture - no global state, all configuration passed as parameters.
5
6
  *
6
7
  * @example
7
8
  * ```ts
8
- * import { initSdk, useCart, useOrders } from '@kasuvia/sdk'
9
+ * import { getStorefrontProductBySlug } from '@kasuvia/sdk'
10
+ * import { useCart, useAuth } from '@kasuvia/sdk/hooks'
11
+ * import type { SdkConfig } from '@kasuvia/sdk'
9
12
  *
10
- * // Initialize SDK
11
- * initSdk({
13
+ * // Use in server components
14
+ * const config: SdkConfig = {
12
15
  * businessId: 'B-123ABC',
13
16
  * storefrontApiKey: 'your-api-key',
14
- * environment: 'production'
15
- * })
16
- *
17
- * // Configure HTTP clients after initialization
18
- * configureHttpClients()
17
+ * backendUrls: {
18
+ * auth: 'http://...',
19
+ * store: 'http://...',
20
+ * payment: 'http://...',
21
+ * notifications: 'http://...',
22
+ * cms: 'http://...',
23
+ * }
24
+ * }
25
+ * const product = await getStorefrontProductBySlug(config, 'slug')
19
26
  *
20
- * // Use in components
27
+ * // Use in client components
21
28
  * function Cart() {
22
29
  * const { cart, addToCart } = useCart()
23
30
  * // ...
@@ -26,33 +33,19 @@
26
33
  */
27
34
 
28
35
  // Configuration
29
- export { initSdk, getRuntimeConfig, getBackendUrl, isInitialized, resetConfig } from './config'
30
36
  export type { SdkConfig } from './config'
37
+ export { SdkProvider, useSdkContext } from './context/sdk-context'
38
+ export type { SdkContextValue, SdkProviderProps } from './context/sdk-context'
31
39
 
32
- // Client layer
33
- export {
34
- storeManagementAxios,
35
- publicStoreAxios,
36
- authAxios,
37
- paymentAxios,
38
- cmsAxios,
39
- getOrCreateGuestSessionId,
40
- getGuestSessionId,
41
- clearGuestSessionId,
42
- configureHttpClients,
43
- } from './client/httpClient'
44
- export { tokenStore, refreshToken } from './client/authClient'
45
- export { getGraphQLClient, resetGraphQLClient } from './client/graphqlClient'
40
+ // Constants
41
+ export { STORAGE_KEYS } from './constants'
46
42
 
47
43
  // Types
48
44
  export * from './types'
49
45
 
50
- // Services
46
+ // Services (for server components)
51
47
  export * from './services'
52
48
 
53
- // React Hooks
54
- export * from './hooks'
55
-
56
49
  // Utils
57
50
  export { getCacheTier, setEnvironmentMode } from './utils/cache'
58
51
  export { logger, setLoggerEnvironmentMode } from './utils/logger'
@@ -61,8 +54,11 @@ export * from './utils/customer'
61
54
  export * from './utils/checkout'
62
55
  export * from './utils/error-handler'
63
56
  export { sessionStore, localStore } from './utils/storage'
64
-
65
-
57
+ export * from './utils/validators'
66
58
 
67
59
  // App service functions
68
60
  export { googleTranslate } from './services/app.service'
61
+
62
+ // Note: React hooks are exported separately via '@kasuvia/sdk/hooks' to avoid
63
+ // importing them in server components. Use:
64
+ // import { useCart, useAuth } from '@kasuvia/sdk/hooks'