@gymspace/sdk 1.3.2 → 1.3.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gymspace/sdk",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "GymSpace TypeScript SDK for API integration",
5
5
  "author": "GymSpace Team",
6
6
  "license": "MIT",
package/src/client.ts CHANGED
@@ -66,13 +66,16 @@ export class ApiClient {
66
66
  // Request interceptor
67
67
  this.axiosInstance.interceptors.request.use(
68
68
  (config: InternalAxiosRequestConfig) => {
69
- // Add Authorization header if auth token is configured
70
- if (this.config.apiKey && config.headers) {
69
+ // Check if this request should skip auth
70
+ const skipAuth = (config as any).skipAuth;
71
+
72
+ // Add Authorization header if auth token is configured and not skipped
73
+ if (this.config.apiKey && config.headers && !skipAuth) {
71
74
  config.headers['Authorization'] = `Bearer ${this.config.apiKey}`;
72
75
  }
73
76
 
74
- // Add refresh token header for ALL authenticated requests
75
- if (this.refreshToken && this.config.apiKey && config.headers) {
77
+ // Add refresh token header for ALL authenticated requests (not skipped)
78
+ if (this.refreshToken && this.config.apiKey && config.headers && !skipAuth) {
76
79
  config.headers['X-Refresh-Token'] = this.refreshToken;
77
80
  }
78
81
 
@@ -174,13 +177,14 @@ export class ApiClient {
174
177
  data?: any,
175
178
  options?: RequestOptions & AxiosRequestConfig,
176
179
  ): Promise<T> {
177
- const { headers, ...axiosConfig } = options || {};
180
+ const { headers, skipAuth, ...axiosConfig } = options || {};
178
181
 
179
- const config: AxiosRequestConfig = {
182
+ const config: AxiosRequestConfig & { skipAuth?: boolean } = {
180
183
  method,
181
184
  url: path,
182
185
  ...this.mergeOptions({ headers }),
183
186
  ...axiosConfig,
187
+ skipAuth, // Pass skipAuth flag to interceptor
184
188
  };
185
189
 
186
190
  if (data && ['POST', 'PUT', 'PATCH'].includes(method.toUpperCase())) {
@@ -9,7 +9,15 @@ export interface StartOnboardingData {
9
9
  timezone: string;
10
10
  }
11
11
 
12
+ export enum OnboardingCacheStatus {
13
+ PROCESSING = 'PROCESSING',
14
+ COMPLETED = 'COMPLETED',
15
+ FAILED = 'FAILED',
16
+ }
17
+
12
18
  export interface StartOnboardingResponse {
19
+ status: OnboardingCacheStatus;
20
+ email: string;
13
21
  access_token: string;
14
22
  refresh_token: string;
15
23
  user: {
@@ -18,19 +26,34 @@ export interface StartOnboardingResponse {
18
26
  name: string;
19
27
  userType: string;
20
28
  };
21
- lastActiveGymId: string;
22
- lastActiveOrganizationId: string;
23
- // Additional fields for mobile app compatibility
24
- organization: {
29
+ }
30
+
31
+ export interface OnboardingStatusResponse {
32
+ status: OnboardingCacheStatus;
33
+ currentStep?: string; // 'setup-organization' | 'setup-gym' | 'setup-features' | 'finalizing'
34
+ progress?: number; // 0-100
35
+ message?: string; // Backend progress message
36
+ user?: {
37
+ id: string;
38
+ email: string;
39
+ name: string;
40
+ userType: string;
41
+ };
42
+ organization?: {
25
43
  id: string;
26
44
  name: string;
27
45
  organizationCode: string;
28
46
  };
29
- gym: {
47
+ gym?: {
30
48
  id: string;
31
49
  name: string;
32
50
  };
33
- onboardingStatus: OnboardingStatus;
51
+ access_token?: string;
52
+ refresh_token?: string;
53
+ lastActiveGymId?: string;
54
+ lastActiveOrganizationId?: string;
55
+ error?: string;
56
+ onboardingStatus?: OnboardingStatus;
34
57
  }
35
58
 
36
59
  export interface BusinessHours {
@@ -7,24 +7,87 @@ import {
7
7
  CompleteGuidedSetupData,
8
8
  OnboardingStatus,
9
9
  OnboardingResponse,
10
+ OnboardingStatusResponse,
11
+ OnboardingCacheStatus,
10
12
  } from '../models/onboarding';
11
13
 
12
14
  export class OnboardingResource extends BaseResource {
13
15
  /**
14
- * Creates owner account, organization, and initial gym
15
- * Now also sends verification and organization codes via email
16
+ * Start onboarding process (async)
17
+ * Creates user account and triggers background setup
18
+ * Returns immediately with PROCESSING status
19
+ * Use getStatusByEmail() to poll for completion
16
20
  */
17
21
  async start(data: StartOnboardingData): Promise<StartOnboardingResponse> {
18
- const response = await this.client.post<StartOnboardingResponse>('/onboarding/start', data);
19
-
22
+ const response = await this.client.post<StartOnboardingResponse>(
23
+ '/onboarding/start',
24
+ data,
25
+ { skipAuth: true }, // Public endpoint - no auth required
26
+ );
27
+
20
28
  // Store tokens after successful onboarding start
21
29
  if (response.access_token) {
22
30
  this.client.setAuthToken(response.access_token);
23
31
  }
24
-
32
+
25
33
  return response;
26
34
  }
27
35
 
36
+ /**
37
+ * Get onboarding status by email (polling endpoint)
38
+ * Use this to check if async onboarding setup is complete
39
+ *
40
+ * @param email - User email to check status
41
+ * @returns Status object with PROCESSING, COMPLETED, or FAILED
42
+ */
43
+ async getStatusByEmail(email: string): Promise<OnboardingStatusResponse> {
44
+ return this.client.get<OnboardingStatusResponse>(
45
+ '/onboarding/status',
46
+ { email },
47
+ { skipAuth: true },
48
+ );
49
+ }
50
+
51
+ /**
52
+ * Poll for onboarding completion
53
+ * Automatically polls until status is COMPLETED or FAILED
54
+ *
55
+ * @param email - User email
56
+ * @param options - Polling options
57
+ * @returns Final status when completed or failed
58
+ */
59
+ async waitForCompletion(
60
+ email: string,
61
+ options: {
62
+ maxAttempts?: number;
63
+ intervalMs?: number;
64
+ onProgress?: (attempt: number, status: OnboardingCacheStatus) => void;
65
+ } = {},
66
+ ): Promise<OnboardingStatusResponse> {
67
+ const { maxAttempts = 60, intervalMs = 1000, onProgress } = options;
68
+
69
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
70
+ const status = await this.getStatusByEmail(email);
71
+
72
+ if (onProgress) {
73
+ onProgress(attempt, status.status);
74
+ }
75
+
76
+ if (status.status === OnboardingCacheStatus.COMPLETED) {
77
+ return status;
78
+ }
79
+
80
+ if (status.status === OnboardingCacheStatus.FAILED) {
81
+ throw new Error(`Onboarding failed: ${status.error || 'Unknown error'}`);
82
+ }
83
+
84
+ // Wait before next poll
85
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
86
+ }
87
+
88
+ throw new Error('Onboarding timeout - process is still running in background');
89
+ }
90
+
28
91
  /**
29
92
  * Updates basic gym settings (step 2 of onboarding)
30
93
  * Configure basic gym information like name, address, etc.
@@ -49,10 +112,10 @@ export class OnboardingResource extends BaseResource {
49
112
  }
50
113
 
51
114
  /**
52
- * Get current onboarding status
115
+ * Get current onboarding status by gym ID
53
116
  * Check progress and next steps
54
117
  */
55
118
  async getStatus(gymId: string): Promise<OnboardingStatus> {
56
119
  return this.client.get<OnboardingStatus>(`/onboarding/status/${gymId}`);
57
120
  }
58
- }
121
+ }
package/src/types.ts CHANGED
@@ -9,6 +9,7 @@ export interface GymSpaceConfig {
9
9
  export interface RequestOptions {
10
10
  gymId?: string;
11
11
  headers?: Record<string, string>;
12
+ skipAuth?: boolean; // Skip Authorization header for public endpoints
12
13
  }
13
14
 
14
15
  export interface PaginationParams {