@kasuvia/sdk 0.0.2 → 0.0.4

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 (67) hide show
  1. package/dist/hooks/index.d.mts +429 -0
  2. package/dist/hooks/index.d.ts +429 -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.d.mts +250 -1161
  8. package/dist/index.d.ts +250 -1161
  9. package/dist/index.js +1544 -2359
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +1527 -2258
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/sdk-context-Cy51kEKT.d.mts +565 -0
  14. package/dist/sdk-context-Cy51kEKT.d.ts +565 -0
  15. package/package.json +6 -2
  16. package/src/config/index.ts +10 -91
  17. package/src/constants.ts +13 -0
  18. package/src/context/sdk-context.tsx +70 -0
  19. package/src/graphql/collections.graphql.ts +2 -10
  20. package/src/graphql/products.graphql.ts +215 -65
  21. package/src/graphql/wishlist.graphql.ts +7 -6
  22. package/src/hooks/index.ts +2 -0
  23. package/src/hooks/use-auth.ts +82 -158
  24. package/src/hooks/use-bespoke-availability.ts +25 -8
  25. package/src/hooks/use-cart.ts +222 -119
  26. package/src/hooks/use-cms.ts +179 -86
  27. package/src/hooks/use-collections.ts +52 -15
  28. package/src/hooks/use-consultation-stream.ts +4 -3
  29. package/src/hooks/use-consultations.ts +120 -51
  30. package/src/hooks/use-countries.ts +35 -13
  31. package/src/hooks/use-currencies.ts +26 -13
  32. package/src/hooks/use-google-translate.ts +22 -8
  33. package/src/hooks/use-languages.ts +40 -20
  34. package/src/hooks/use-mobile.ts +23 -0
  35. package/src/hooks/use-order-stream.ts +9 -5
  36. package/src/hooks/use-orders.ts +99 -44
  37. package/src/hooks/use-payment.ts +108 -40
  38. package/src/hooks/use-products.ts +251 -53
  39. package/src/hooks/use-service-booking.ts +152 -90
  40. package/src/hooks/use-states.ts +35 -15
  41. package/src/hooks/use-wishlist.ts +92 -91
  42. package/src/index.ts +26 -30
  43. package/src/services/auth.service.ts +209 -167
  44. package/src/services/bespoke.service.ts +12 -4
  45. package/src/services/cart.service.ts +172 -70
  46. package/src/services/cms.service.ts +70 -35
  47. package/src/services/collection.service.ts +22 -5
  48. package/src/services/config.service.ts +34 -14
  49. package/src/services/consultation.service.ts +72 -18
  50. package/src/services/logistics.service.ts +35 -11
  51. package/src/services/notifications.service.ts +27 -12
  52. package/src/services/order.service.ts +74 -15
  53. package/src/services/payment.service.ts +86 -31
  54. package/src/services/product.service.ts +279 -39
  55. package/src/services/service-booking.service.ts +98 -26
  56. package/src/services/wishlist.service.ts +69 -25
  57. package/src/types/auth/index.ts +6 -4
  58. package/src/types/business/index.ts +7 -2
  59. package/src/types/payments/index.ts +3 -3
  60. package/src/types/services/index.ts +2 -5
  61. package/src/types/store/index.ts +38 -3
  62. package/src/utils/error-handler.ts +10 -5
  63. package/src/utils/storage.ts +48 -26
  64. package/src/utils/validators.ts +85 -0
  65. package/src/client/authClient.ts +0 -135
  66. package/src/client/graphqlClient.ts +0 -53
  67. package/src/client/httpClient.ts +0 -298
@@ -1,182 +1,106 @@
1
1
  /**
2
- * Auth Hook & Context Provider
2
+ * Auth Hooks
3
3
  *
4
- * Provides customer authentication state and modal controls across the storefront.
4
+ * Stateless React Query hooks for authentication operations.
5
+ * The storefront should provide its own AuthProvider context that manages
6
+ * tokens and passes config to these hooks.
5
7
  */
6
- import React, { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'
7
8
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
8
- import { getAuthUser, loginCustomer, registerCustomer, updateProfile, logout as logoutService, silentRefresh } from '../services/auth.service'
9
- import { tokenStore } from '../client/authClient'
10
- import { resetGraphQLClient } from '../client/graphqlClient'
9
+ import { getAuthUser, loginCustomer, registerCustomer, updateProfile, logout as logoutService } from '../services/auth.service'
11
10
  import { logger } from '../utils/logger'
12
11
  import type {
13
12
  CustomerLoginPayload,
14
13
  CustomerRegisterPayload,
15
14
  UpdateProfilePayload,
16
15
  CustomerUser,
17
- AuthContextValue,
18
16
  } from '../types/auth'
19
-
20
- // ── React Context ────────────────────────────────────────────────────────────
21
-
22
- export const AuthCtx = React.createContext ? React.createContext<AuthContextValue | null>(null) : null
23
-
24
- export function AuthProvider({ children }: { children: ReactNode }) {
25
- const [customer, setCustomer] = useState<CustomerUser | null>(null)
26
- const [isLoading, setIsLoading] = useState(true)
27
- const [isModalOpen, setIsModalOpen] = useState(false)
28
-
29
- // Rehydrate session from stored refresh token on app boot
30
- useEffect(() => {
31
- let voided = false
32
- ;(async () => {
33
- setIsLoading(true)
34
- try {
35
- const result = await silentRefresh()
36
- if (!voided && result) {
37
- setCustomer(result.user)
38
- resetGraphQLClient()
39
- }
40
- } catch (error: unknown) {
41
- logger.error('[AuthProvider] Session restore failed:', error)
42
- if (!voided) setCustomer(null)
43
- } finally {
44
- if (!voided) setIsLoading(false)
45
- }
46
- })()
47
- return () => {
48
- voided = true
49
- }
50
- }, [])
51
-
52
- const openModal = useCallback(() => setIsModalOpen(true), [])
53
- const closeModal = useCallback(() => {
54
- setIsModalOpen(false)
55
- if (typeof window !== 'undefined' && (window.location.hash === '#login' || window.location.hash === '#signup')) {
56
- window.history.replaceState(null, '', window.location.pathname + window.location.search)
57
- }
58
- }, [])
59
-
60
- // Listen for #login and #signup hashes to open modal automatically
61
- useEffect(() => {
62
- const handleHash = () => {
63
- if (typeof window === 'undefined') return
64
- if (window.location.hash === '#login' || window.location.hash === '#signup') {
65
- if (!isLoading && customer) {
66
- window.history.replaceState(null, '', window.location.pathname + window.location.search)
67
- } else if (!isLoading && !customer) {
68
- setIsModalOpen(true)
69
- }
70
- }
71
- }
72
- handleHash()
73
- window.addEventListener('hashchange', handleHash)
74
- return () => window.removeEventListener('hashchange', handleHash)
75
- }, [customer, isLoading])
76
-
77
- // Reset GraphQL client when customer state changes to ensure auth headers are included
78
- useEffect(() => {
79
- resetGraphQLClient()
80
- }, [customer])
81
-
82
- const handleLogout = useCallback(async () => {
83
- await logoutService()
84
- setCustomer(null)
85
- setIsModalOpen(false)
86
- resetGraphQLClient()
87
- }, [])
88
-
89
- const value: AuthContextValue = {
90
- customer,
91
- isLoading,
92
- isModalOpen,
93
- openModal,
94
- closeModal,
95
- setCustomer,
96
- logout: handleLogout,
97
- }
98
-
99
- if (!AuthCtx) return children
100
- return React.createElement(AuthCtx.Provider, { value }, children)
101
- }
17
+ import type { SdkConfig } from '../config'
18
+ import { useSdkContext, resolveHookArgs } from '../context/sdk-context'
102
19
 
103
20
  // ── Hooks ────────────────────────────────────────────────────────────────────
104
21
 
105
- export function useAuthUser() {
106
- return useQuery({
107
- queryKey: ['auth-user'],
108
- queryFn: getAuthUser,
109
- enabled: !!tokenStore.getAccessToken(),
110
- })
111
- }
112
-
113
- export function useLogin() {
114
- const queryClient = useQueryClient()
115
-
116
- return useMutation({
117
- mutationFn: loginCustomer,
118
- onSuccess: () => {
119
- queryClient.invalidateQueries({ queryKey: ['auth-user'] })
120
- queryClient.invalidateQueries({ queryKey: ['cart'] })
121
- },
122
- })
22
+ export function useAuthUser(config?: SdkConfig, accessToken?: string) {
23
+ const ctx = useSdkContext()
24
+ try {
25
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken ?? null, ctx)
26
+ return useQuery({
27
+ queryKey: ['auth-user'],
28
+ queryFn: () => getAuthUser(cfg, token ?? ''),
29
+ enabled: !!token,
30
+ })
31
+ } catch (error) {
32
+ logger.warn('[useAuthUser] Called outside QueryClientProvider, returning empty user')
33
+ return { data: undefined, isLoading: false, error: null, isFetching: false, refetch: () => Promise.resolve({ data: undefined }) } as any
34
+ }
123
35
  }
124
36
 
125
- export function useRegister() {
126
- const queryClient = useQueryClient()
127
-
128
- return useMutation({
129
- mutationFn: registerCustomer,
130
- onSuccess: () => {
131
- queryClient.invalidateQueries({ queryKey: ['auth-user'] })
132
- queryClient.invalidateQueries({ queryKey: ['cart'] })
133
- },
134
- })
37
+ export function useLogin(config?: SdkConfig) {
38
+ const ctx = useSdkContext()
39
+ try {
40
+ const { config: cfg } = resolveHookArgs(config, undefined, ctx)
41
+ const queryClient = useQueryClient()
42
+ return useMutation({
43
+ mutationFn: (payload: Omit<CustomerLoginPayload, 'tenant_id'>) => loginCustomer(cfg, payload),
44
+ onSuccess: () => {
45
+ queryClient.invalidateQueries({ queryKey: ['auth-user'] })
46
+ queryClient.invalidateQueries({ queryKey: ['cart'] })
47
+ },
48
+ })
49
+ } catch (error) {
50
+ logger.warn('[useLogin] Called outside QueryClientProvider, returning no-op mutation')
51
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
52
+ }
135
53
  }
136
54
 
137
- export function useUpdateProfile() {
138
- const queryClient = useQueryClient()
139
-
140
- return useMutation({
141
- mutationFn: updateProfile,
142
- onSuccess: () => {
143
- queryClient.invalidateQueries({ queryKey: ['auth-user'] })
144
- },
145
- })
55
+ export function useRegister(config?: SdkConfig) {
56
+ const ctx = useSdkContext()
57
+ try {
58
+ const { config: cfg } = resolveHookArgs(config, undefined, ctx)
59
+ const queryClient = useQueryClient()
60
+ return useMutation({
61
+ mutationFn: (payload: Omit<CustomerRegisterPayload, 'tenant_id'>) => registerCustomer(cfg, payload),
62
+ onSuccess: () => {
63
+ queryClient.invalidateQueries({ queryKey: ['auth-user'] })
64
+ queryClient.invalidateQueries({ queryKey: ['cart'] })
65
+ },
66
+ })
67
+ } catch (error) {
68
+ logger.warn('[useRegister] Called outside QueryClientProvider, returning no-op mutation')
69
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
70
+ }
146
71
  }
147
72
 
148
- export function useLogout() {
149
- const queryClient = useQueryClient()
150
-
151
- return useMutation({
152
- mutationFn: logoutService,
153
- onSuccess: () => {
154
- queryClient.clear()
155
- },
156
- })
73
+ export function useUpdateProfile(config?: SdkConfig, accessToken?: string | null) {
74
+ const ctx = useSdkContext()
75
+ try {
76
+ const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)
77
+ const queryClient = useQueryClient()
78
+ return useMutation({
79
+ mutationFn: (payload: UpdateProfilePayload) =>
80
+ token ? updateProfile(cfg, token, payload) : Promise.reject('No access token'),
81
+ onSuccess: () => {
82
+ queryClient.invalidateQueries({ queryKey: ['auth-user'] })
83
+ },
84
+ })
85
+ } catch (error) {
86
+ logger.warn('[useUpdateProfile] Called outside QueryClientProvider, returning no-op mutation')
87
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
88
+ }
157
89
  }
158
90
 
159
- /**
160
- * useAuth()
161
- * Consumes the AuthCtx provider value. Falls back to local hook query if no provider exists.
162
- */
163
- export function useAuth(): AuthContextValue {
164
- const context = AuthCtx ? useContext(AuthCtx) : null
165
- if (context) return context
166
-
167
- // Fallback if useAuth is called outside AuthProvider (e.g. simple SSR/unit tests)
168
- const authUserQuery = useAuthUser()
169
- const logoutMutation = useLogout()
170
-
171
- return {
172
- customer: authUserQuery.data as CustomerUser | null,
173
- isLoading: authUserQuery.isLoading,
174
- isModalOpen: false,
175
- openModal: () => {},
176
- closeModal: () => {},
177
- setCustomer: () => {},
178
- logout: async () => {
179
- await logoutMutation.mutateAsync()
180
- },
91
+ export function useLogout(config?: SdkConfig, refreshToken?: string) {
92
+ const ctx = useSdkContext()
93
+ try {
94
+ const { config: cfg } = resolveHookArgs(config, undefined, ctx)
95
+ const queryClient = useQueryClient()
96
+ return useMutation({
97
+ mutationFn: () => logoutService(cfg, refreshToken),
98
+ onSuccess: () => {
99
+ queryClient.clear()
100
+ },
101
+ })
102
+ } catch (error) {
103
+ logger.warn('[useLogout] Called outside QueryClientProvider, returning no-op mutation')
104
+ return { mutate: () => {}, mutateAsync: () => Promise.resolve(undefined) } as any
181
105
  }
182
106
  }
@@ -2,19 +2,36 @@
2
2
  * Bespoke Availability Hook
3
3
  *
4
4
  * React Query hook for checking bespoke service availability.
5
+ * Stateless - requires config parameter.
5
6
  */
6
7
  import { useQuery } from '@tanstack/react-query'
7
- import { bespokeService } from '../services/bespoke.service'
8
+ import { checkAvailability } from '../services/bespoke.service'
9
+ import type { SdkConfig } from '../config'
8
10
  import type { BespokeAvailability } from '../types/consultations'
11
+ import { logger } from '../utils'
12
+ import { useSdkContext, resolveHookArgs } from '../context/sdk-context'
9
13
 
10
14
  /**
11
15
  * Get bespoke availability for a product
12
16
  */
13
- export function useBespokeAvailability(product_id: string) {
14
- return useQuery({
15
- queryKey: ['bespoke-availability', product_id],
16
- queryFn: () => bespokeService.checkAvailability(product_id),
17
- enabled: !!product_id,
18
- retry: false, // Don't retry if no config exists (returns 404)
19
- })
17
+ export function useBespokeAvailability(product_id: string, config?: SdkConfig) {
18
+ const ctx = useSdkContext()
19
+ try {
20
+ const { config: cfg } = resolveHookArgs(config, undefined, ctx)
21
+ return useQuery({
22
+ queryKey: ['bespoke-availability', product_id],
23
+ queryFn: () => checkAvailability(cfg, product_id),
24
+ enabled: !!product_id,
25
+ retry: false,
26
+ })
27
+ } catch (error) {
28
+ logger.warn('[useBespokeAvailability] Called outside QueryClientProvider, returning undefined')
29
+ return {
30
+ data: undefined,
31
+ isLoading: false,
32
+ error: null,
33
+ isFetching: false,
34
+ refetch: () => Promise.resolve({ data: undefined }),
35
+ } as any
36
+ }
20
37
  }