@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,17 +1,10 @@
1
1
  /**
2
2
  * Customer auth service — all API calls scoped to this storefront's business tenant.
3
- * Every request automatically receives:
4
- * - Authorization: Bearer <accessToken> (from interceptors)
5
- * - X-Storefront-Key: <storefrontApiKey> (from interceptors)
6
- *
7
- * The `tenant_id` field in login/register payloads is the permanent business_id (B-XXXXXXXX).
3
+ * Stateless - all configuration passed as parameters.
8
4
  */
9
- import { authAxios, refreshAxios } from '../client/httpClient'
10
- import { tokenStore } from '../client/authClient'
11
- import { resetGraphQLClient } from '../client/graphqlClient'
12
- import { getRuntimeConfig } from '../config'
5
+ import type { SdkConfig } from '../config'
13
6
  import { logger } from '../utils/logger'
14
- import { mergeGuestCartIfPresent } from './cart.service'
7
+ import { STORAGE_KEYS } from '../constants'
15
8
  import type {
16
9
  CustomerAuthResponse,
17
10
  CustomerLoginPayload,
@@ -27,94 +20,127 @@ import type {
27
20
 
28
21
  /**
29
22
  * POST /auth/login/customer/
30
- * The `tenant_id` is automatically filled from config.api.storefrontBusinessId.
23
+ * The `tenant_id` is automatically filled from config.businessId.
31
24
  */
32
- export async function loginCustomer(payload: Omit<CustomerLoginPayload, 'tenant_id'>): Promise<CustomerAuthResponse> {
33
- const config = getRuntimeConfig()
34
- const { data } = await authAxios.post<CustomerAuthResponse>('/auth/login/customer/', {
35
- ...payload,
36
- tenant_id: config.businessId,
25
+ export async function loginCustomer(
26
+ config: SdkConfig,
27
+ payload: Omit<CustomerLoginPayload, 'tenant_id'>
28
+ ): Promise<CustomerAuthResponse> {
29
+ const response = await fetch(`${config.backendUrls.auth}/auth/login/customer/`, {
30
+ method: 'POST',
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ 'X-Storefront-Key': config.storefrontApiKey,
34
+ 'X-Business-ID': config.businessId,
35
+ },
36
+ body: JSON.stringify({
37
+ ...payload,
38
+ tenant_id: config.businessId,
39
+ }),
37
40
  })
38
41
 
39
- tokenStore.setAccessToken(data.access)
40
- tokenStore.setRefreshToken(data.refresh)
41
- resetGraphQLClient()
42
-
43
- // Merge guest cart after successful login
44
- await mergeGuestCartIfPresent()
42
+ if (!response.ok) {
43
+ throw new Error('Login failed')
44
+ }
45
45
 
46
+ const data = await response.json()
46
47
  return data
47
48
  }
48
49
 
49
50
  /**
50
51
  * POST /auth/register/customer/
51
- * The `tenant_id` is automatically filled from config.api.storefrontBusinessId.
52
+ * The `tenant_id` is automatically filled from config.businessId.
52
53
  */
53
- export async function registerCustomer(payload: Omit<CustomerRegisterPayload, 'tenant_id'>): Promise<CustomerAuthResponse> {
54
- const config = getRuntimeConfig()
55
- const { data } = await authAxios.post<CustomerAuthResponse>('/auth/register/customer/', {
56
- ...payload,
57
- tenant_id: config.businessId,
54
+ export async function registerCustomer(
55
+ config: SdkConfig,
56
+ payload: Omit<CustomerRegisterPayload, 'tenant_id'>
57
+ ): Promise<CustomerAuthResponse> {
58
+ const response = await fetch(`${config.backendUrls.auth}/auth/register/customer/`, {
59
+ method: 'POST',
60
+ headers: {
61
+ 'Content-Type': 'application/json',
62
+ 'X-Storefront-Key': config.storefrontApiKey,
63
+ 'X-Business-ID': config.businessId,
64
+ },
65
+ body: JSON.stringify({
66
+ ...payload,
67
+ tenant_id: config.businessId,
68
+ }),
58
69
  })
59
70
 
60
- tokenStore.setAccessToken(data.access)
61
- tokenStore.setRefreshToken(data.refresh)
62
- resetGraphQLClient()
63
-
64
- // Merge guest cart after successful registration
65
- await mergeGuestCartIfPresent()
71
+ if (!response.ok) {
72
+ throw new Error('Registration failed')
73
+ }
66
74
 
75
+ const data = await response.json()
67
76
  return data
68
77
  }
69
78
 
70
79
  /** POST /auth/token/refresh/
71
- * CRITICAL: Uses refreshAxios (no interceptors) to prevent interceptor recursion.
72
80
  */
73
- export async function refreshToken(refresh: string): Promise<RefreshTokenResponse> {
74
- const { data } = await refreshAxios.post<RefreshTokenResponse>('/auth/token/refresh/', { refresh })
81
+ export async function refreshToken(
82
+ config: SdkConfig,
83
+ refresh: string
84
+ ): Promise<RefreshTokenResponse> {
85
+ const response = await fetch(`${config.backendUrls.auth}/auth/token/refresh/`, {
86
+ method: 'POST',
87
+ headers: {
88
+ 'Content-Type': 'application/json',
89
+ },
90
+ body: JSON.stringify({ refresh }),
91
+ })
92
+
93
+ if (!response.ok) {
94
+ throw new Error('Token refresh failed')
95
+ }
96
+
97
+ const data = await response.json()
75
98
  return data
76
99
  }
77
100
 
78
- /** POST /auth/logout/ — revokes the server-side session. Always clears local tokens. */
79
- export async function logout(): Promise<void> {
80
- const refresh = tokenStore.getRefreshToken()
101
+ /** POST /auth/logout/ — revokes the server-side session. */
102
+ export async function logout(
103
+ config: SdkConfig,
104
+ refresh?: string
105
+ ): Promise<void> {
81
106
  const payload: LogoutPayload = refresh ? { refresh } : {}
82
107
  try {
83
- await authAxios.post('/auth/logout/', payload)
108
+ await fetch(`${config.backendUrls.auth}/auth/logout/`, {
109
+ method: 'POST',
110
+ headers: {
111
+ 'Content-Type': 'application/json',
112
+ },
113
+ body: JSON.stringify(payload),
114
+ })
84
115
  } catch {
85
- // Swallow server errors (401 = already expired, network = offline).
86
- // Local token cleanup in `finally` is what actually logs the user out.
87
- } finally {
88
- tokenStore.clearAll()
89
- resetGraphQLClient()
116
+ // Swallow server errors
90
117
  }
91
118
  }
92
119
 
93
120
  /**
94
121
  * Verify a social provider id_token (or access_token for Facebook) with the backend.
95
- * The backend validates the token with the provider and creates/finds the customer.
96
- *
97
- * Industry-standard "ID Token Exchange" pattern:
98
- * 1. Customer completes Google/Facebook sign-in on the storefront
99
- * 2. Storefront receives an id_token / access_token from the provider
100
- * 3. This function sends it to the backend for server-side verification
101
- * 4. Backend returns our JWT tokens
102
122
  */
103
123
  export async function verifySocialToken(
124
+ config: SdkConfig,
104
125
  provider: 'google' | 'facebook',
105
126
  token: string
106
127
  ): Promise<CustomerAuthResponse> {
107
- const config = getRuntimeConfig()
108
- const { data } = await authAxios.post<CustomerAuthResponse>(
109
- `/auth/social/verify/${provider}/`,
110
- {
128
+ const response = await fetch(`${config.backendUrls.auth}/auth/social/verify/${provider}/`, {
129
+ method: 'POST',
130
+ headers: {
131
+ 'Content-Type': 'application/json',
132
+ },
133
+ body: JSON.stringify({
111
134
  id_token: token,
112
135
  storefront_key: config.storefrontApiKey,
113
- }
114
- )
115
- tokenStore.setAccessToken(data.access)
116
- tokenStore.setRefreshToken(data.refresh)
117
- resetGraphQLClient()
136
+ }),
137
+ })
138
+
139
+ if (!response.ok) {
140
+ throw new Error('Social token verification failed')
141
+ }
142
+
143
+ const data = await response.json()
118
144
  return data
119
145
  }
120
146
 
@@ -143,7 +169,7 @@ function resolveAvatar(profilePicture: unknown): string | null {
143
169
  return null
144
170
  }
145
171
 
146
- /** Map luxe-auth UserSerializer response to storefront CustomerUser. */
172
+ /** Map kasuvia-auth UserSerializer response to storefront CustomerUser. */
147
173
  export function mapAuthUserToCustomer(data: AuthUserResponse, provider?: CustomerUser['provider']): CustomerUser {
148
174
  const firstname = data.profile?.firstname?.trim() || undefined
149
175
  const lastname = data.profile?.lastname?.trim() || undefined
@@ -184,130 +210,112 @@ function buildCustomerFromClaims(claims: Record<string, unknown>): CustomerUser
184
210
  }
185
211
 
186
212
  /** GET /auth/profile/ */
187
- export async function getMe(): Promise<CustomerUser> {
188
- const { data } = await authAxios.get<AuthUserResponse>('/auth/profile/')
213
+ export async function getMe(config?: SdkConfig, accessToken?: string): Promise<CustomerUser> {
214
+ if (!config || !accessToken) {
215
+ const response = await fetch('/api/auth/me', { credentials: 'include' })
216
+ if (!response.ok) throw new Error('Failed to fetch profile')
217
+ const data = await response.json()
218
+ return data.user
219
+ }
220
+
221
+ const response = await fetch(`${config.backendUrls.auth}/auth/profile/`, {
222
+ headers: {
223
+ 'Authorization': `Bearer ${accessToken}`,
224
+ },
225
+ })
226
+
227
+ if (!response.ok) {
228
+ throw new Error('Failed to fetch profile')
229
+ }
230
+
231
+ const data = await response.json()
189
232
  return mapAuthUserToCustomer(data)
190
233
  }
191
234
 
192
235
  /** GET /auth/profile/ - alias for getMe for backward compatibility */
193
- export async function getAuthUser(): Promise<AuthUserResponse> {
194
- const { data } = await authAxios.get<AuthUserResponse>('/auth/profile/')
236
+ export async function getAuthUser(config?: SdkConfig, accessToken?: string): Promise<AuthUserResponse> {
237
+ if (!config || !accessToken) {
238
+ const response = await fetch('/api/auth/me', { credentials: 'include' })
239
+ if (!response.ok) throw new Error('Failed to fetch auth user')
240
+ const data = await response.json()
241
+ return data.user
242
+ }
243
+
244
+ const response = await fetch(`${config.backendUrls.auth}/auth/profile/`, {
245
+ headers: {
246
+ 'Authorization': `Bearer ${accessToken}`,
247
+ },
248
+ })
249
+
250
+ if (!response.ok) {
251
+ throw new Error('Failed to fetch auth user')
252
+ }
253
+
254
+ const data = await response.json()
195
255
  return data
196
256
  }
197
257
 
198
258
  /** PUT /auth/profile/ — update firstname, lastname, phone */
199
- export async function updateProfile(payload: UpdateProfilePayload): Promise<CustomerUser> {
200
- const { data } = await authAxios.put<AuthUserResponse>('/auth/profile/', payload)
201
- return mapAuthUserToCustomer(data)
202
- }
203
-
204
- let refreshPromise: Promise<{ user: CustomerUser; provider: CustomerUser['provider'] } | null> | null = null;
259
+ export async function updateProfile(
260
+ configOrPayload: SdkConfig | UpdateProfilePayload,
261
+ accessTokenOrPayload?: string | UpdateProfilePayload,
262
+ payloadParam?: UpdateProfilePayload
263
+ ): Promise<CustomerUser> {
264
+ const isArg1Config = typeof configOrPayload === 'object' && configOrPayload !== null && 'businessId' in configOrPayload
265
+ if (!isArg1Config) {
266
+ const payload = configOrPayload as UpdateProfilePayload
267
+ const response = await fetch('/api/auth/profile', {
268
+ method: 'PUT',
269
+ headers: { 'Content-Type': 'application/json' },
270
+ credentials: 'include',
271
+ body: JSON.stringify(payload),
272
+ })
273
+ if (!response.ok) throw new Error('Failed to update profile')
274
+ return response.json()
275
+ }
205
276
 
206
- /**
207
- * Helper to classify error types for proper handling.
208
- */
209
- function classifyError(error: unknown): { isNetworkError: boolean; isAuthError: boolean; isBlacklistedError: boolean } {
210
- const err = error as { response?: { status?: number; data?: { detail?: string } }; code?: string; message?: string }
211
- const isNetworkError = !!(!err.response && (err.code === 'ECONNABORTED' || err.code === 'ERR_NETWORK' || err.message?.includes('Network Error')))
212
- const isAuthError = !!(err.response?.status === 401 || err.response?.status === 403)
213
- const isBlacklistedError = !!(err.response?.data?.detail === 'Token is blacklisted' || err.message === 'Token is blacklisted')
214
- return { isNetworkError, isAuthError, isBlacklistedError }
215
- }
277
+ const config = configOrPayload as SdkConfig
278
+ const accessToken = accessTokenOrPayload as string
279
+ const payload = payloadParam as UpdateProfilePayload
280
+
281
+ const response = await fetch(`${config.backendUrls.auth}/auth/profile/`, {
282
+ method: 'PUT',
283
+ headers: {
284
+ 'Content-Type': 'application/json',
285
+ 'Authorization': `Bearer ${accessToken}`,
286
+ },
287
+ body: JSON.stringify(payload),
288
+ })
216
289
 
217
- /**
218
- * Helper to retry with exponential backoff for transient network errors.
219
- */
220
- async function retryWithBackoff<T>(
221
- fn: () => Promise<T>,
222
- maxRetries: number = 2,
223
- baseDelay: number = 1000
224
- ): Promise<T> {
225
- let lastError: unknown
226
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
227
- try {
228
- return await fn()
229
- } catch (error) {
230
- lastError = error
231
- const { isNetworkError, isAuthError } = classifyError(error)
232
-
233
- // Only retry on network errors, not auth errors
234
- if (!isNetworkError || isAuthError) {
235
- throw error
236
- }
237
-
238
- // Don't retry on the last attempt
239
- if (attempt === maxRetries) {
240
- throw error
241
- }
242
-
243
- // Exponential backoff: 1s, 2s, 4s, etc.
244
- const delay = baseDelay * Math.pow(2, attempt)
245
- await new Promise(resolve => setTimeout(resolve, delay))
246
- }
290
+ if (!response.ok) {
291
+ throw new Error('Failed to update profile')
247
292
  }
248
- throw lastError
293
+
294
+ const data = await response.json()
295
+ return mapAuthUserToCustomer(data)
249
296
  }
250
297
 
251
298
  /**
252
299
  * Silently refresh the access token on app boot.
253
300
  * Builds CustomerUser from JWT claims — no additional profile API call needed.
254
301
  * Returns null if no refresh token exists or if the refresh fails.
255
- * Includes a concurrent lock to prevent React Strict Mode from double-firing and blacklisting the token.
256
- *
257
- * Network error resilience:
258
- * - Classifies errors as network vs auth
259
- * - Retries transient network errors with exponential backoff
260
- * - Immediately fails on auth errors (401/403) to avoid infinite loops
261
- * - Clears auth state on any failure to ensure consistent logout behavior
262
302
  */
263
- export async function silentRefresh(): Promise<{ user: CustomerUser; provider: CustomerUser['provider'] } | null> {
264
- const refresh = tokenStore.getRefreshToken()
265
- if (!refresh) return null
266
-
267
- if (refreshPromise) return refreshPromise;
268
-
269
- refreshPromise = (async () => {
270
- try {
271
- // Use retry with backoff for network resilience
272
- const tokens = await retryWithBackoff(() => refreshToken(refresh), 2, 1000)
273
-
274
- tokenStore.setAccessToken(tokens.access)
275
- if (tokens.refresh) tokenStore.setRefreshToken(tokens.refresh, tokenStore.isPersistent())
276
-
277
- // Decode user identity from the new access token — avoids a fragile /profile/ round-trip
278
- const claims = parseJwtPayload(tokens.access)
279
- const user = buildCustomerFromClaims(claims)
280
-
281
- return { user, provider: user.provider }
282
- } catch (error: unknown) {
283
- const { isNetworkError, isAuthError, isBlacklistedError } = classifyError(error)
284
-
285
- if (isNetworkError) {
286
- logger.warn('[silentRefresh] Network error during token refresh (offline/CORS), clearing session:', error)
287
- } else if (isBlacklistedError) {
288
- logger.log('[silentRefresh] Token was blacklisted (likely from logout), clearing session')
289
- } else if (isAuthError) {
290
- logger.log('[silentRefresh] Token refresh failed with auth error (expired/invalid), clearing session')
291
- } else {
292
- logger.error('[silentRefresh] Unexpected error during token refresh, clearing session:', error)
293
- }
294
-
295
- // CRITICAL: Clear auth state immediately on ANY refresh failure
296
- // This prevents partial session state and ensures consistent logout behavior
297
- tokenStore.clearAll()
303
+ export async function silentRefresh(
304
+ config: SdkConfig,
305
+ refresh: string
306
+ ): Promise<{ user: CustomerUser; provider: CustomerUser['provider']; newAccessToken: string; newRefreshToken: string } | null> {
307
+ try {
308
+ const data = await refreshToken(config, refresh)
309
+ if (!data.access || !data.refresh) {
298
310
  return null
299
- } finally {
300
- refreshPromise = null;
301
311
  }
302
- })();
303
-
304
- return refreshPromise;
305
- }
306
-
307
- /** Store tokens after OAuth callback or login/register response. */
308
- export function storeTokens(access: string, refresh: string, remember = true): void {
309
- tokenStore.setAccessToken(access)
310
- tokenStore.setRefreshToken(refresh, remember)
312
+ const claims = parseJwtPayload(data.access)
313
+ const user = buildCustomerFromClaims(claims)
314
+ const provider = user.provider
315
+ return { user, provider, newAccessToken: data.access, newRefreshToken: data.refresh }
316
+ } catch {
317
+ return null
318
+ }
311
319
  }
312
320
 
313
321
  // ── Helper Functions ─────────────────────────────────────────────────────────
@@ -330,3 +338,37 @@ function splitFullName(fullName?: string): { firstname: string; lastname: string
330
338
  lastname: parts.slice(1).join(' ')
331
339
  }
332
340
  }
341
+
342
+ /**
343
+ * Store tokens in localStorage (client-side utility)
344
+ * This is a convenience function for storefronts to store auth tokens
345
+ */
346
+ export function storeTokens(accessToken: string, refreshToken: string): void {
347
+ if (typeof window !== 'undefined') {
348
+ localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, accessToken)
349
+ localStorage.setItem(STORAGE_KEYS.REFRESH_TOKEN, refreshToken)
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Clear tokens from localStorage (client-side utility)
355
+ */
356
+ export function clearTokens(): void {
357
+ if (typeof window !== 'undefined') {
358
+ localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN)
359
+ localStorage.removeItem(STORAGE_KEYS.REFRESH_TOKEN)
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Get stored tokens from localStorage (client-side utility)
365
+ */
366
+ export function getStoredTokens(): { accessToken: string | null; refreshToken: string | null } {
367
+ if (typeof window === 'undefined') {
368
+ return { accessToken: null, refreshToken: null }
369
+ }
370
+ return {
371
+ accessToken: localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN),
372
+ refreshToken: localStorage.getItem(STORAGE_KEYS.REFRESH_TOKEN),
373
+ }
374
+ }
@@ -2,13 +2,21 @@
2
2
  * Bespoke Service
3
3
  *
4
4
  * Handles bespoke product availability checks.
5
+ * Stateless - all configuration passed as parameters.
5
6
  */
6
- import { storeManagementAxios } from '../client/httpClient'
7
+ import type { SdkConfig } from '../config'
7
8
  import type { BespokeAvailability } from '../types/consultations'
8
9
 
9
- export async function checkAvailability(product_id: string): Promise<BespokeAvailability> {
10
- const { data } = await storeManagementAxios.get<BespokeAvailability>(`bespoke/availability/${product_id}`)
11
- return data
10
+ export async function checkAvailability(config: SdkConfig, product_id: string): Promise<BespokeAvailability> {
11
+ const response = await fetch(`${config.backendUrls.store}/bespoke/availability/${product_id}`, {
12
+ headers: {
13
+ "X-Storefront-Key": config.storefrontApiKey,
14
+ "X-Business-ID": config.businessId,
15
+ },
16
+ })
17
+
18
+ if (!response.ok) throw new Error('Failed to check bespoke availability')
19
+ return response.json()
12
20
  }
13
21
 
14
22
  // Service object export for backward compatibility