@symbo.ls/sdk 3.1.2 → 3.2.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.
Files changed (65) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/config/environment.js +5 -21
  3. package/dist/cjs/index.js +6 -26
  4. package/dist/cjs/services/AIService.js +3 -3
  5. package/dist/cjs/services/CollabService.js +420 -0
  6. package/dist/cjs/services/CoreService.js +651 -107
  7. package/dist/cjs/services/SocketService.js +207 -59
  8. package/dist/cjs/services/index.js +5 -13
  9. package/dist/cjs/state/RootStateManager.js +86 -0
  10. package/dist/cjs/state/rootEventBus.js +65 -0
  11. package/dist/cjs/utils/CollabClient.js +157 -0
  12. package/dist/cjs/utils/TokenManager.js +62 -27
  13. package/dist/cjs/utils/jsonDiff.js +103 -0
  14. package/dist/cjs/utils/services.js +129 -88
  15. package/dist/cjs/utils/symstoryClient.js +5 -5
  16. package/dist/esm/config/environment.js +5 -21
  17. package/dist/esm/index.js +20459 -9286
  18. package/dist/esm/services/AIService.js +3 -3
  19. package/dist/esm/services/BasedService.js +5 -21
  20. package/dist/esm/services/CollabService.js +18028 -0
  21. package/dist/esm/services/CoreService.js +718 -155
  22. package/dist/esm/services/SocketService.js +323 -58
  23. package/dist/esm/services/SymstoryService.js +10 -26
  24. package/dist/esm/services/index.js +20305 -9158
  25. package/dist/esm/state/RootStateManager.js +102 -0
  26. package/dist/esm/state/rootEventBus.js +47 -0
  27. package/dist/esm/utils/CollabClient.js +17483 -0
  28. package/dist/esm/utils/TokenManager.js +62 -27
  29. package/dist/esm/utils/jsonDiff.js +6096 -0
  30. package/dist/esm/utils/services.js +129 -88
  31. package/dist/esm/utils/symstoryClient.js +10 -26
  32. package/dist/node/config/environment.js +5 -21
  33. package/dist/node/index.js +10 -34
  34. package/dist/node/services/AIService.js +3 -3
  35. package/dist/node/services/CollabService.js +401 -0
  36. package/dist/node/services/CoreService.js +651 -107
  37. package/dist/node/services/SocketService.js +197 -59
  38. package/dist/node/services/index.js +5 -13
  39. package/dist/node/state/RootStateManager.js +57 -0
  40. package/dist/node/state/rootEventBus.js +46 -0
  41. package/dist/node/utils/CollabClient.js +128 -0
  42. package/dist/node/utils/TokenManager.js +62 -27
  43. package/dist/node/utils/jsonDiff.js +74 -0
  44. package/dist/node/utils/services.js +129 -88
  45. package/dist/node/utils/symstoryClient.js +5 -5
  46. package/package.json +12 -6
  47. package/src/config/environment.js +5 -19
  48. package/src/index.js +9 -31
  49. package/src/services/AIService.js +3 -3
  50. package/src/services/BasedService.js +1 -0
  51. package/src/services/CollabService.js +491 -0
  52. package/src/services/CoreService.js +715 -110
  53. package/src/services/SocketService.js +227 -59
  54. package/src/services/index.js +6 -13
  55. package/src/state/RootStateManager.js +71 -0
  56. package/src/state/rootEventBus.js +48 -0
  57. package/src/utils/CollabClient.js +161 -0
  58. package/src/utils/TokenManager.js +68 -30
  59. package/src/utils/jsonDiff.js +109 -0
  60. package/src/utils/services.js +140 -88
  61. package/src/utils/symstoryClient.js +5 -5
  62. package/dist/cjs/services/SocketIOService.js +0 -307
  63. package/dist/esm/services/SocketIOService.js +0 -470
  64. package/dist/node/services/SocketIOService.js +0 -278
  65. package/src/services/SocketIOService.js +0 -334
@@ -7,7 +7,7 @@ export class CoreService extends BaseService {
7
7
  super(config)
8
8
  this._client = null
9
9
  this._initialized = false
10
- this._baseUrl = null
10
+ this._apiUrl = null
11
11
  this._tokenManager = null
12
12
  }
13
13
 
@@ -16,15 +16,15 @@ export class CoreService extends BaseService {
16
16
  const { appKey, authToken } = context || this._context
17
17
 
18
18
  // Get base URL from environment config
19
- this._baseUrl = environment.apiUrl || environment.baseUrl
19
+ this._apiUrl = environment.apiUrl
20
20
 
21
- if (!this._baseUrl) {
21
+ if (!this._apiUrl) {
22
22
  throw new Error('Core service base URL not configured')
23
23
  }
24
24
 
25
25
  // Initialize token manager
26
26
  this._tokenManager = getTokenManager({
27
- apiUrl: this._baseUrl,
27
+ apiUrl: this._apiUrl,
28
28
  onTokenRefresh: (tokens) => {
29
29
  // Update context with new token
30
30
  this.updateContext({ authToken: tokens.accessToken })
@@ -38,15 +38,14 @@ export class CoreService extends BaseService {
38
38
  }
39
39
  })
40
40
 
41
- // If we have an authToken, set it in the token manager
42
- if (authToken) {
41
+ if (authToken && !this._tokenManager.hasTokens()) {
43
42
  this._tokenManager.setTokens({ access_token: authToken })
44
43
  }
45
44
 
46
45
  // Store masked configuration info
47
46
  this._info = {
48
47
  config: {
49
- baseUrl: this._baseUrl,
48
+ apiUrl: this._apiUrl,
50
49
  appKey: appKey ? `${appKey.substr(0, 4)}...${appKey.substr(-4)}` : null,
51
50
  hasToken: Boolean(authToken)
52
51
  }
@@ -66,11 +65,15 @@ export class CoreService extends BaseService {
66
65
  'register',
67
66
  'login',
68
67
  'googleAuth',
68
+ 'googleAuthCallback',
69
69
  'githubAuth',
70
70
  'requestPasswordReset',
71
71
  'confirmPasswordReset',
72
72
  'confirmRegistration',
73
- 'verifyEmail'
73
+ 'verifyEmail',
74
+ 'listPublicProjects',
75
+ 'getPublicProject',
76
+ 'getHealthStatus'
74
77
  ])
75
78
  return !noInitMethods.has(methodName)
76
79
  }
@@ -106,12 +109,31 @@ export class CoreService extends BaseService {
106
109
  }
107
110
  }
108
111
 
112
+ // Helper method to check if user is authenticated
113
+ isAuthenticated () {
114
+ if (!this._tokenManager) {
115
+ return false
116
+ }
117
+ return this._tokenManager.hasTokens()
118
+ }
119
+
120
+ // Helper method to check if user has valid tokens
121
+ hasValidTokens () {
122
+ if (!this._tokenManager) {
123
+ return false
124
+ }
125
+ return this._tokenManager.hasTokens() && this._tokenManager.isAccessTokenValid()
126
+ }
127
+
109
128
  // Helper method to make HTTP requests
110
129
  async _request (endpoint, options = {}) {
111
- const url = `${this._baseUrl}/core${endpoint}`
130
+ const url = `${this._apiUrl}/core${endpoint}`
131
+
132
+ const defaultHeaders = {}
112
133
 
113
- const defaultHeaders = {
114
- 'Content-Type': 'application/json'
134
+ // Only set Content-Type for JSON requests, not for FormData
135
+ if (!(options.body instanceof FormData)) {
136
+ defaultHeaders['Content-Type'] = 'application/json'
115
137
  }
116
138
 
117
139
  // Use TokenManager for automatic token management
@@ -119,16 +141,11 @@ export class CoreService extends BaseService {
119
141
  try {
120
142
  // Ensure we have a valid token (will refresh if needed)
121
143
  const validToken = await this._tokenManager.ensureValidToken()
122
- console.log(`[CoreService] Token check for ${options.methodName}:`, {
123
- hasValidToken: Boolean(validToken),
124
- tokenPreview: validToken ? `${validToken.substring(0, 20)}...` : null
125
- })
126
144
 
127
145
  if (validToken) {
128
146
  const authHeader = this._tokenManager.getAuthHeader()
129
147
  if (authHeader) {
130
148
  defaultHeaders.Authorization = authHeader
131
- console.log(`[CoreService] Added auth header for ${options.methodName}`)
132
149
  }
133
150
  }
134
151
  } catch (error) {
@@ -139,7 +156,6 @@ export class CoreService extends BaseService {
139
156
  const { authToken } = this._context
140
157
  if (authToken) {
141
158
  defaultHeaders.Authorization = `Bearer ${authToken}`
142
- console.log(`[CoreService] Using context token for ${options.methodName}`)
143
159
  }
144
160
  }
145
161
 
@@ -172,11 +188,15 @@ export class CoreService extends BaseService {
172
188
 
173
189
  async register (userData) {
174
190
  try {
175
- return await this._request('/auth/register', {
191
+ const response = await this._request('/auth/register', {
176
192
  method: 'POST',
177
193
  body: JSON.stringify(userData),
178
194
  methodName: 'register'
179
195
  })
196
+ if (response.success) {
197
+ return response.data
198
+ }
199
+ throw new Error(response.message)
180
200
  } catch (error) {
181
201
  throw new Error(`Registration failed: ${error.message}`)
182
202
  }
@@ -209,7 +229,10 @@ export class CoreService extends BaseService {
209
229
  this.updateContext({ authToken: tokens.accessToken })
210
230
  }
211
231
 
212
- return response
232
+ if (response.success) {
233
+ return response.data
234
+ }
235
+ throw new Error(response.message)
213
236
  } catch (error) {
214
237
  throw new Error(`Login failed: ${error.message}`)
215
238
  }
@@ -242,11 +265,15 @@ export class CoreService extends BaseService {
242
265
 
243
266
  async refreshToken (refreshToken) {
244
267
  try {
245
- return await this._request('/auth/refresh', {
268
+ const response = await this._request('/auth/refresh', {
246
269
  method: 'POST',
247
270
  body: JSON.stringify({ refreshToken }),
248
271
  methodName: 'refreshToken'
249
272
  })
273
+ if (response.success) {
274
+ return response.data
275
+ }
276
+ throw new Error(response.message)
250
277
  } catch (error) {
251
278
  throw new Error(`Token refresh failed: ${error.message}`)
252
279
  }
@@ -279,7 +306,10 @@ export class CoreService extends BaseService {
279
306
  this.updateContext({ authToken: tokens.accessToken })
280
307
  }
281
308
 
282
- return response
309
+ if (response.success) {
310
+ return response.data
311
+ }
312
+ throw new Error(response.message)
283
313
  } catch (error) {
284
314
  throw new Error(`Google auth failed: ${error.message}`)
285
315
  }
@@ -312,19 +342,62 @@ export class CoreService extends BaseService {
312
342
  this.updateContext({ authToken: tokens.accessToken })
313
343
  }
314
344
 
315
- return response
345
+ if (response.success) {
346
+ return response.data
347
+ }
348
+ throw new Error(response.message)
316
349
  } catch (error) {
317
350
  throw new Error(`GitHub auth failed: ${error.message}`)
318
351
  }
319
352
  }
320
353
 
354
+ async googleAuthCallback (code, redirectUri) {
355
+ try {
356
+ const response = await this._request('/auth/google/callback', {
357
+ method: 'POST',
358
+ body: JSON.stringify({ code, redirectUri }),
359
+ methodName: 'googleAuthCallback'
360
+ })
361
+
362
+ // Handle new response format: response.data.tokens
363
+ if (response.success && response.data && response.data.tokens) {
364
+ const { tokens } = response.data
365
+ const tokenData = {
366
+ access_token: tokens.accessToken,
367
+ refresh_token: tokens.refreshToken,
368
+ expires_in: tokens.accessTokenExp?.expiresIn,
369
+ token_type: 'Bearer'
370
+ }
371
+
372
+ // Set tokens in TokenManager
373
+ if (this._tokenManager) {
374
+ this._tokenManager.setTokens(tokenData)
375
+ }
376
+
377
+ // Update context for backward compatibility
378
+ this.updateContext({ authToken: tokens.accessToken })
379
+ }
380
+
381
+ if (response.success) {
382
+ return response.data
383
+ }
384
+ throw new Error(response.message)
385
+ } catch (error) {
386
+ throw new Error(`Google auth callback failed: ${error.message}`)
387
+ }
388
+ }
389
+
321
390
  async requestPasswordReset (email) {
322
391
  try {
323
- return await this._request('/auth/request-password-reset', {
392
+ const response = await this._request('/auth/request-password-reset', {
324
393
  method: 'POST',
325
394
  body: JSON.stringify({ email }),
326
395
  methodName: 'requestPasswordReset'
327
396
  })
397
+ if (response.success) {
398
+ return response.data
399
+ }
400
+ throw new Error(response.message)
328
401
  } catch (error) {
329
402
  throw new Error(`Password reset request failed: ${error.message}`)
330
403
  }
@@ -332,11 +405,15 @@ export class CoreService extends BaseService {
332
405
 
333
406
  async confirmPasswordReset (token, password) {
334
407
  try {
335
- return await this._request('/auth/reset-password-confirm', {
408
+ const response = await this._request('/auth/reset-password-confirm', {
336
409
  method: 'POST',
337
410
  body: JSON.stringify({ token, password }),
338
411
  methodName: 'confirmPasswordReset'
339
412
  })
413
+ if (response.success) {
414
+ return response.data
415
+ }
416
+ throw new Error(response.message)
340
417
  } catch (error) {
341
418
  throw new Error(`Password reset confirmation failed: ${error.message}`)
342
419
  }
@@ -344,11 +421,15 @@ export class CoreService extends BaseService {
344
421
 
345
422
  async confirmRegistration (token) {
346
423
  try {
347
- return await this._request('/auth/register-confirmation', {
424
+ const response = await this._request('/auth/register-confirmation', {
348
425
  method: 'POST',
349
426
  body: JSON.stringify({ token }),
350
427
  methodName: 'confirmRegistration'
351
428
  })
429
+ if (response.success) {
430
+ return response.data
431
+ }
432
+ throw new Error(response.message)
352
433
  } catch (error) {
353
434
  throw new Error(`Registration confirmation failed: ${error.message}`)
354
435
  }
@@ -357,10 +438,14 @@ export class CoreService extends BaseService {
357
438
  async requestPasswordChange () {
358
439
  this._requireReady('requestPasswordChange')
359
440
  try {
360
- return await this._request('/auth/request-password-change', {
441
+ const response = await this._request('/auth/request-password-change', {
361
442
  method: 'POST',
362
443
  methodName: 'requestPasswordChange'
363
444
  })
445
+ if (response.success) {
446
+ return response.data
447
+ }
448
+ throw new Error(response.message)
364
449
  } catch (error) {
365
450
  throw new Error(`Password change request failed: ${error.message}`)
366
451
  }
@@ -369,11 +454,15 @@ export class CoreService extends BaseService {
369
454
  async confirmPasswordChange (currentPassword, newPassword, code) {
370
455
  this._requireReady('confirmPasswordChange')
371
456
  try {
372
- return await this._request('/auth/confirm-password-change', {
457
+ const response = await this._request('/auth/confirm-password-change', {
373
458
  method: 'POST',
374
459
  body: JSON.stringify({ currentPassword, newPassword, code }),
375
460
  methodName: 'confirmPasswordChange'
376
461
  })
462
+ if (response.success) {
463
+ return response.data
464
+ }
465
+ throw new Error(response.message)
377
466
  } catch (error) {
378
467
  throw new Error(`Password change confirmation failed: ${error.message}`)
379
468
  }
@@ -382,10 +471,14 @@ export class CoreService extends BaseService {
382
471
  async getMe () {
383
472
  this._requireReady('getMe')
384
473
  try {
385
- return await this._request('/auth/me', {
474
+ const response = await this._request('/auth/me', {
386
475
  method: 'GET',
387
476
  methodName: 'getMe'
388
477
  })
478
+ if (response.success) {
479
+ return response.data
480
+ }
481
+ throw new Error(response.message)
389
482
  } catch (error) {
390
483
  throw new Error(`Failed to get user profile: ${error.message}`)
391
484
  }
@@ -418,40 +511,70 @@ export class CoreService extends BaseService {
418
511
  try {
419
512
  await this._tokenManager.ensureValidToken()
420
513
  } catch (error) {
421
- // If refresh fails, clear tokens and return empty state
422
- this._tokenManager.clearTokens()
514
+ console.warn('[CoreService] Token refresh failed:', error.message)
515
+ // Only clear tokens if it's definitely an auth error, not a network error
516
+ if (error.message.includes('401') || error.message.includes('403') ||
517
+ error.message.includes('invalid') || error.message.includes('expired')) {
518
+ this._tokenManager.clearTokens()
519
+ return {
520
+ userId: false,
521
+ authToken: false,
522
+ error: `Authentication failed: ${error.message}`
523
+ }
524
+ }
525
+ // For network errors, keep tokens and return what we have
423
526
  return {
424
527
  userId: false,
425
- authToken: false,
426
- error: `Token refresh failed: ${error.message}`
528
+ authToken: this._tokenManager.getAccessToken(),
529
+ error: `Network error during token refresh: ${error.message}`,
530
+ hasTokens: true
427
531
  }
428
532
  }
429
533
  }
430
534
 
535
+ // Check if we have a valid token now
536
+ const currentAccessToken = this._tokenManager.getAccessToken()
537
+ if (!currentAccessToken) {
538
+ return {
539
+ userId: false,
540
+ authToken: false
541
+ }
542
+ }
543
+
431
544
  // Get current user data if we have valid tokens
545
+ // Be more lenient with API failures - don't immediately clear tokens
432
546
  try {
433
547
  const currentUser = await this.getMe()
434
- const userProjects = await this.getUserProjects()
435
548
 
436
549
  return {
437
- userId: currentUser.data.id,
438
- authToken: this._tokenManager.getAccessToken(),
439
- user: {
440
- ...currentUser.data,
441
- projects: userProjects?.data || []
442
- },
550
+ userId: currentUser.user.id,
551
+ authToken: currentAccessToken,
552
+ ...currentUser,
443
553
  error: null
444
554
  }
445
555
  } catch (error) {
446
- // If API calls fail, clear invalid tokens
447
- this._tokenManager.clearTokens()
556
+ console.warn('[CoreService] Failed to get user data:', error.message)
557
+
558
+ // Only clear tokens if it's an auth error (401, 403), not network errors
559
+ if (error.message.includes('401') || error.message.includes('403')) {
560
+ this._tokenManager.clearTokens()
561
+ return {
562
+ userId: false,
563
+ authToken: false,
564
+ error: `Authentication failed: ${error.message}`
565
+ }
566
+ }
567
+
568
+ // For other errors (network, 500, etc.), keep tokens but return minimal state
448
569
  return {
449
570
  userId: false,
450
- authToken: false,
451
- error: `Failed to get user data: ${error.message}`
571
+ authToken: currentAccessToken,
572
+ error: `Failed to get user data: ${error.message}`,
573
+ hasTokens: true
452
574
  }
453
575
  }
454
576
  } catch (error) {
577
+ console.error('[CoreService] Unexpected error in getStoredAuthState:', error)
455
578
  return {
456
579
  userId: false,
457
580
  authToken: false,
@@ -465,10 +588,14 @@ export class CoreService extends BaseService {
465
588
  async getUserProfile () {
466
589
  this._requireReady('getUserProfile')
467
590
  try {
468
- return await this._request('/users/profile', {
591
+ const response = await this._request('/users/profile', {
469
592
  method: 'GET',
470
593
  methodName: 'getUserProfile'
471
594
  })
595
+ if (response.success) {
596
+ return response.data
597
+ }
598
+ throw new Error(response.message)
472
599
  } catch (error) {
473
600
  throw new Error(`Failed to get user profile: ${error.message}`)
474
601
  }
@@ -477,11 +604,15 @@ export class CoreService extends BaseService {
477
604
  async updateUserProfile (profileData) {
478
605
  this._requireReady('updateUserProfile')
479
606
  try {
480
- return await this._request('/users/profile', {
607
+ const response = await this._request('/users/profile', {
481
608
  method: 'PATCH',
482
609
  body: JSON.stringify(profileData),
483
610
  methodName: 'updateUserProfile'
484
611
  })
612
+ if (response.success) {
613
+ return response.data
614
+ }
615
+ throw new Error(response.message)
485
616
  } catch (error) {
486
617
  throw new Error(`Failed to update user profile: ${error.message}`)
487
618
  }
@@ -490,10 +621,17 @@ export class CoreService extends BaseService {
490
621
  async getUserProjects () {
491
622
  this._requireReady('getUserProjects')
492
623
  try {
493
- return await this._request('/users/projects', {
624
+ const response = await this._request('/users/projects', {
494
625
  method: 'GET',
495
626
  methodName: 'getUserProjects'
496
627
  })
628
+ if (response.success) {
629
+ return response.data.map(project => ({
630
+ ...project,
631
+ ...(project.icon && { icon: { src: `${this._apiUrl}/core/files/public/${project.icon.id}/download`, ...project.icon } })
632
+ }))
633
+ }
634
+ throw new Error(response.message)
497
635
  } catch (error) {
498
636
  throw new Error(`Failed to get user projects: ${error.message}`)
499
637
  }
@@ -505,10 +643,14 @@ export class CoreService extends BaseService {
505
643
  throw new Error('User ID is required')
506
644
  }
507
645
  try {
508
- return await this._request(`/users/${userId}`, {
646
+ const response = await this._request(`/users/${userId}`, {
509
647
  method: 'GET',
510
648
  methodName: 'getUser'
511
649
  })
650
+ if (response.success) {
651
+ return response.data
652
+ }
653
+ throw new Error(response.message)
512
654
  } catch (error) {
513
655
  throw new Error(`Failed to get user: ${error.message}`)
514
656
  }
@@ -520,13 +662,17 @@ export class CoreService extends BaseService {
520
662
  throw new Error('Email is required')
521
663
  }
522
664
  try {
523
- return await this._request('/auth/user', {
665
+ const response = await this._request('/auth/user', {
524
666
  method: 'GET',
525
667
  headers: {
526
668
  'X-User-Email': email
527
669
  },
528
670
  methodName: 'getUserByEmail'
529
671
  })
672
+ if (response.success) {
673
+ return response.data
674
+ }
675
+ throw new Error(response.message)
530
676
  } catch (error) {
531
677
  throw new Error(`Failed to get user by email: ${error.message}`)
532
678
  }
@@ -537,53 +683,148 @@ export class CoreService extends BaseService {
537
683
  async createProject (projectData) {
538
684
  this._requireReady('createProject')
539
685
  try {
540
- return await this._request('/projects', {
686
+ const response = await this._request('/projects', {
541
687
  method: 'POST',
542
688
  body: JSON.stringify(projectData),
543
689
  methodName: 'createProject'
544
690
  })
691
+ if (response.success) {
692
+ return response.data
693
+ }
694
+ throw new Error(response.message)
545
695
  } catch (error) {
546
696
  throw new Error(`Failed to create project: ${error.message}`)
547
697
  }
548
698
  }
549
699
 
550
- async getProjects () {
700
+ async getProjects (params = {}) {
551
701
  this._requireReady('getProjects')
552
702
  try {
553
- return await this._request('/projects', {
703
+ const queryParams = new URLSearchParams()
704
+
705
+ // Add query parameters
706
+ Object.keys(params).forEach(key => {
707
+ if (params[key] != null) {
708
+ queryParams.append(key, params[key])
709
+ }
710
+ })
711
+
712
+ const queryString = queryParams.toString()
713
+ const url = `/projects${queryString ? `?${queryString}` : ''}`
714
+
715
+ const response = await this._request(url, {
554
716
  method: 'GET',
555
717
  methodName: 'getProjects'
556
718
  })
719
+ if (response.success) {
720
+ return response
721
+ }
722
+ throw new Error(response.message)
557
723
  } catch (error) {
558
724
  throw new Error(`Failed to get projects: ${error.message}`)
559
725
  }
560
726
  }
561
727
 
728
+ /**
729
+ * Alias for getProjects for consistency with API naming
730
+ */
731
+ async listProjects (params = {}) {
732
+ return await this.getProjects(params)
733
+ }
734
+
735
+ /**
736
+ * List only public projects (no authentication required)
737
+ */
738
+ async listPublicProjects (params = {}) {
739
+ try {
740
+ const queryParams = new URLSearchParams()
741
+
742
+ // Add query parameters
743
+ Object.keys(params).forEach(key => {
744
+ if (params[key] != null) {
745
+ queryParams.append(key, params[key])
746
+ }
747
+ })
748
+
749
+ const queryString = queryParams.toString()
750
+ const url = `/projects/public${queryString ? `?${queryString}` : ''}`
751
+
752
+ const response = await this._request(url, {
753
+ method: 'GET',
754
+ methodName: 'listPublicProjects'
755
+ })
756
+ if (response.success) {
757
+ return response.data
758
+ }
759
+ throw new Error(response.message)
760
+ } catch (error) {
761
+ throw new Error(`Failed to list public projects: ${error.message}`)
762
+ }
763
+ }
764
+
562
765
  async getProject (projectId) {
563
766
  this._requireReady('getProject')
564
767
  if (!projectId) {
565
768
  throw new Error('Project ID is required')
566
769
  }
567
770
  try {
568
- return await this._request(`/projects/${projectId}`, {
771
+ const response = await this._request(`/projects/${projectId}`, {
569
772
  method: 'GET',
570
773
  methodName: 'getProject'
571
774
  })
775
+ if (response.success) {
776
+ const iconSrc = response.data.icon ? `${this._apiUrl}/core/files/public/${response.data.icon.id}/download` : null
777
+ return {
778
+ ...response.data,
779
+ icon: { src: iconSrc, ...response.data.icon }
780
+ }
781
+ }
782
+ throw new Error(response.message)
572
783
  } catch (error) {
573
784
  throw new Error(`Failed to get project: ${error.message}`)
574
785
  }
575
786
  }
576
787
 
788
+ /**
789
+ * Get a public project by ID (no authentication required)
790
+ * Corresponds to router.get('/public/:projectId', ProjectController.getPublicProject)
791
+ */
792
+ async getPublicProject (projectId) {
793
+ if (!projectId) {
794
+ throw new Error('Project ID is required')
795
+ }
796
+ try {
797
+ const response = await this._request(`/projects/public/${projectId}`, {
798
+ method: 'GET',
799
+ methodName: 'getPublicProject'
800
+ })
801
+ if (response.success) {
802
+ const iconSrc = response.data.icon ? `${this._apiUrl}/core/files/public/${response.data.icon.id}/download` : null
803
+ return {
804
+ ...response.data,
805
+ icon: { src: iconSrc, ...response.data.icon }
806
+ }
807
+ }
808
+ throw new Error(response.message)
809
+ } catch (error) {
810
+ throw new Error(`Failed to get public project: ${error.message}`)
811
+ }
812
+ }
813
+
577
814
  async getProjectByKey (key) {
578
815
  this._requireReady('getProjectByKey')
579
816
  if (!key) {
580
817
  throw new Error('Project key is required')
581
818
  }
582
819
  try {
583
- return await this._request(`/projects/check-key/${key}`, {
820
+ const response = await this._request(`/projects/check-key/${key}`, {
584
821
  method: 'GET',
585
822
  methodName: 'getProjectByKey'
586
823
  })
824
+ if (response.success) {
825
+ return response.data
826
+ }
827
+ throw new Error(response.message)
587
828
  } catch (error) {
588
829
  throw new Error(`Failed to get project by key: ${error.message}`)
589
830
  }
@@ -595,11 +836,15 @@ export class CoreService extends BaseService {
595
836
  throw new Error('Project ID is required')
596
837
  }
597
838
  try {
598
- return await this._request(`/projects/${projectId}`, {
839
+ const response = await this._request(`/projects/${projectId}`, {
599
840
  method: 'PATCH',
600
841
  body: JSON.stringify(data),
601
842
  methodName: 'updateProject'
602
843
  })
844
+ if (response.success) {
845
+ return response.data
846
+ }
847
+ throw new Error(response.message)
603
848
  } catch (error) {
604
849
  throw new Error(`Failed to update project: ${error.message}`)
605
850
  }
@@ -611,11 +856,15 @@ export class CoreService extends BaseService {
611
856
  throw new Error('Project ID is required')
612
857
  }
613
858
  try {
614
- return await this._request(`/projects/${projectId}/components`, {
859
+ const response = await this._request(`/projects/${projectId}/components`, {
615
860
  method: 'PATCH',
616
861
  body: JSON.stringify({ components }),
617
862
  methodName: 'updateProjectComponents'
618
863
  })
864
+ if (response.success) {
865
+ return response.data
866
+ }
867
+ throw new Error(response.message)
619
868
  } catch (error) {
620
869
  throw new Error(`Failed to update project components: ${error.message}`)
621
870
  }
@@ -627,11 +876,15 @@ export class CoreService extends BaseService {
627
876
  throw new Error('Project ID is required')
628
877
  }
629
878
  try {
630
- return await this._request(`/projects/${projectId}/settings`, {
879
+ const response = await this._request(`/projects/${projectId}/settings`, {
631
880
  method: 'PATCH',
632
881
  body: JSON.stringify({ settings }),
633
882
  methodName: 'updateProjectSettings'
634
883
  })
884
+ if (response.success) {
885
+ return response.data
886
+ }
887
+ throw new Error(response.message)
635
888
  } catch (error) {
636
889
  throw new Error(`Failed to update project settings: ${error.message}`)
637
890
  }
@@ -643,11 +896,15 @@ export class CoreService extends BaseService {
643
896
  throw new Error('Project ID is required')
644
897
  }
645
898
  try {
646
- return await this._request(`/projects/${projectId}`, {
899
+ const response = await this._request(`/projects/${projectId}`, {
647
900
  method: 'PATCH',
648
901
  body: JSON.stringify({ name }),
649
902
  methodName: 'updateProjectName'
650
903
  })
904
+ if (response.success) {
905
+ return response.data
906
+ }
907
+ throw new Error(response.message)
651
908
  } catch (error) {
652
909
  throw new Error(`Failed to update project name: ${error.message}`)
653
910
  }
@@ -659,27 +916,35 @@ export class CoreService extends BaseService {
659
916
  throw new Error('Project ID is required')
660
917
  }
661
918
  try {
662
- return await this._request(`/projects/${projectId}/tier`, {
919
+ const response = await this._request(`/projects/${projectId}/tier`, {
663
920
  method: 'PATCH',
664
921
  body: JSON.stringify({ tier: pkg }),
665
922
  methodName: 'updateProjectPackage'
666
923
  })
924
+ if (response.success) {
925
+ return response.data
926
+ }
927
+ throw new Error(response.message)
667
928
  } catch (error) {
668
929
  throw new Error(`Failed to update project package: ${error.message}`)
669
930
  }
670
931
  }
671
932
 
672
- async duplicateProject (projectId, newName, newKey) {
933
+ async duplicateProject (projectId, newName, newKey, targetUserId) {
673
934
  this._requireReady('duplicateProject')
674
935
  if (!projectId) {
675
936
  throw new Error('Project ID is required')
676
937
  }
677
938
  try {
678
- return await this._request(`/projects/${projectId}/duplicate`, {
939
+ const response = await this._request(`/projects/${projectId}/duplicate`, {
679
940
  method: 'POST',
680
- body: JSON.stringify({ name: newName, key: newKey }),
941
+ body: JSON.stringify({ name: newName, key: newKey, targetUserId }),
681
942
  methodName: 'duplicateProject'
682
943
  })
944
+ if (response.success) {
945
+ return response.data
946
+ }
947
+ throw new Error(response.message)
683
948
  } catch (error) {
684
949
  throw new Error(`Failed to duplicate project: ${error.message}`)
685
950
  }
@@ -691,10 +956,14 @@ export class CoreService extends BaseService {
691
956
  throw new Error('Project ID is required')
692
957
  }
693
958
  try {
694
- return await this._request(`/projects/${projectId}`, {
959
+ const response = await this._request(`/projects/${projectId}`, {
695
960
  method: 'DELETE',
696
961
  methodName: 'removeProject'
697
962
  })
963
+ if (response.success) {
964
+ return response.data
965
+ }
966
+ throw new Error(response.message)
698
967
  } catch (error) {
699
968
  throw new Error(`Failed to remove project: ${error.message}`)
700
969
  }
@@ -706,10 +975,14 @@ export class CoreService extends BaseService {
706
975
  throw new Error('Project key is required')
707
976
  }
708
977
  try {
709
- return await this._request(`/projects/check-key/${key}`, {
978
+ const response = await this._request(`/projects/check-key/${key}`, {
710
979
  method: 'GET',
711
980
  methodName: 'checkProjectKeyAvailability'
712
981
  })
982
+ if (response.success) {
983
+ return response.data
984
+ }
985
+ throw new Error(response.message)
713
986
  } catch (error) {
714
987
  throw new Error(`Failed to check project key availability: ${error.message}`)
715
988
  }
@@ -723,42 +996,73 @@ export class CoreService extends BaseService {
723
996
  throw new Error('Project ID is required')
724
997
  }
725
998
  try {
726
- return await this._request(`/projects/${projectId}/members`, {
999
+ const response = await this._request(`/projects/${projectId}/members`, {
727
1000
  method: 'GET',
728
1001
  methodName: 'getProjectMembers'
729
1002
  })
1003
+ if (response.success) {
1004
+ return response.data
1005
+ }
1006
+ throw new Error(response.message)
730
1007
  } catch (error) {
731
1008
  throw new Error(`Failed to get project members: ${error.message}`)
732
1009
  }
733
1010
  }
734
1011
 
735
- async inviteMember (projectId, email, message, role = 'guest') {
1012
+ async inviteMember (projectId, email, role = 'guest', options = {}) {
736
1013
  this._requireReady('inviteMember')
737
- if (!projectId || !email) {
738
- throw new Error('Project ID and email are required')
1014
+ if (!projectId || !email || !role) {
1015
+ throw new Error('Project ID, email, and role are required')
739
1016
  }
1017
+
1018
+ const { name, callbackUrl } = options
1019
+
1020
+ // Default callbackUrl if not provided
1021
+ const defaultCallbackUrl = typeof window === 'undefined'
1022
+ ? 'https://app.symbols.com/accept-invite'
1023
+ : `${window.location.origin}/accept-invite`
1024
+
740
1025
  try {
741
- return await this._request(`/projects/${projectId}/invite`, {
1026
+ const requestBody = {
1027
+ email,
1028
+ role,
1029
+ callbackUrl: callbackUrl || defaultCallbackUrl
1030
+ }
1031
+
1032
+ // Add optional name if provided
1033
+ if (name) {
1034
+ requestBody.name = name
1035
+ }
1036
+
1037
+ const response = await this._request(`/projects/${projectId}/invite`, {
742
1038
  method: 'POST',
743
- body: JSON.stringify({ email, role, message }),
1039
+ body: JSON.stringify(requestBody),
744
1040
  methodName: 'inviteMember'
745
1041
  })
1042
+ if (response.success) {
1043
+ return response.data
1044
+ }
1045
+ throw new Error(response.message)
746
1046
  } catch (error) {
747
1047
  throw new Error(`Failed to invite member: ${error.message}`)
748
1048
  }
749
1049
  }
750
1050
 
751
- async acceptInvite (projectId, token) {
1051
+ async acceptInvite (token) {
752
1052
  this._requireReady('acceptInvite')
753
- if (!projectId || !token) {
754
- throw new Error('Project ID and token are required')
1053
+ if (!token) {
1054
+ throw new Error('Invitation token is required')
755
1055
  }
756
1056
  try {
757
- return await this._request(`/projects/${projectId}/accept-invite`, {
1057
+ const response = await this._request('/projects/accept-invite', {
758
1058
  method: 'POST',
759
1059
  body: JSON.stringify({ token }),
760
1060
  methodName: 'acceptInvite'
761
1061
  })
1062
+ if (response.success) {
1063
+ return response.data
1064
+ }
1065
+ throw new Error(response.message)
762
1066
  } catch (error) {
763
1067
  throw new Error(`Failed to accept invite: ${error.message}`)
764
1068
  }
@@ -770,11 +1074,15 @@ export class CoreService extends BaseService {
770
1074
  throw new Error('Project ID, member ID, and role are required')
771
1075
  }
772
1076
  try {
773
- return await this._request(`/projects/${projectId}/members/${memberId}`, {
1077
+ const response = await this._request(`/projects/${projectId}/members/${memberId}`, {
774
1078
  method: 'PATCH',
775
1079
  body: JSON.stringify({ role }),
776
1080
  methodName: 'updateMemberRole'
777
1081
  })
1082
+ if (response.success) {
1083
+ return response.data
1084
+ }
1085
+ throw new Error(response.message)
778
1086
  } catch (error) {
779
1087
  throw new Error(`Failed to update member role: ${error.message}`)
780
1088
  }
@@ -786,10 +1094,14 @@ export class CoreService extends BaseService {
786
1094
  throw new Error('Project ID and member ID are required')
787
1095
  }
788
1096
  try {
789
- return await this._request(`/projects/${projectId}/members/${memberId}`, {
1097
+ const response = await this._request(`/projects/${projectId}/members/${memberId}`, {
790
1098
  method: 'DELETE',
791
1099
  methodName: 'removeMember'
792
1100
  })
1101
+ if (response.success) {
1102
+ return response.data
1103
+ }
1104
+ throw new Error(response.message)
793
1105
  } catch (error) {
794
1106
  throw new Error(`Failed to remove member: ${error.message}`)
795
1107
  }
@@ -801,10 +1113,14 @@ export class CoreService extends BaseService {
801
1113
  this._requireReady('getAvailableLibraries')
802
1114
  const queryParams = new URLSearchParams(params).toString()
803
1115
  try {
804
- return await this._request(`/projects/libraries/available?${queryParams}`, {
1116
+ const response = await this._request(`/projects/libraries/available?${queryParams}`, {
805
1117
  method: 'GET',
806
1118
  methodName: 'getAvailableLibraries'
807
1119
  })
1120
+ if (response.success) {
1121
+ return response.data
1122
+ }
1123
+ throw new Error(response.message)
808
1124
  } catch (error) {
809
1125
  throw new Error(`Failed to get available libraries: ${error.message}`)
810
1126
  }
@@ -816,10 +1132,14 @@ export class CoreService extends BaseService {
816
1132
  throw new Error('Project ID is required')
817
1133
  }
818
1134
  try {
819
- return await this._request(`/projects/${projectId}/libraries`, {
1135
+ const response = await this._request(`/projects/${projectId}/libraries`, {
820
1136
  method: 'GET',
821
1137
  methodName: 'getProjectLibraries'
822
1138
  })
1139
+ if (response.success) {
1140
+ return response.data
1141
+ }
1142
+ throw new Error(response.message)
823
1143
  } catch (error) {
824
1144
  throw new Error(`Failed to get project libraries: ${error.message}`)
825
1145
  }
@@ -831,11 +1151,15 @@ export class CoreService extends BaseService {
831
1151
  throw new Error('Project ID and library IDs are required')
832
1152
  }
833
1153
  try {
834
- return await this._request(`/projects/${projectId}/libraries`, {
1154
+ const response = await this._request(`/projects/${projectId}/libraries`, {
835
1155
  method: 'POST',
836
1156
  body: JSON.stringify({ libraryIds }),
837
1157
  methodName: 'addProjectLibraries'
838
1158
  })
1159
+ if (response.success) {
1160
+ return response.data
1161
+ }
1162
+ throw new Error(response.message)
839
1163
  } catch (error) {
840
1164
  throw new Error(`Failed to add project libraries: ${error.message}`)
841
1165
  }
@@ -847,11 +1171,15 @@ export class CoreService extends BaseService {
847
1171
  throw new Error('Project ID and library IDs are required')
848
1172
  }
849
1173
  try {
850
- return await this._request(`/projects/${projectId}/libraries`, {
1174
+ const response = await this._request(`/projects/${projectId}/libraries`, {
851
1175
  method: 'DELETE',
852
1176
  body: JSON.stringify({ libraryIds }),
853
1177
  methodName: 'removeProjectLibraries'
854
1178
  })
1179
+ if (response.success) {
1180
+ return response.data
1181
+ }
1182
+ throw new Error(response.message)
855
1183
  } catch (error) {
856
1184
  throw new Error(`Failed to remove project libraries: ${error.message}`)
857
1185
  }
@@ -868,19 +1196,30 @@ export class CoreService extends BaseService {
868
1196
  const formData = new FormData()
869
1197
  formData.append('file', file)
870
1198
 
871
- // Add optional parameters
1199
+ // Add optional parameters only if they exist
872
1200
  if (options.projectId) {formData.append('projectId', options.projectId)}
873
1201
  if (options.tags) {formData.append('tags', JSON.stringify(options.tags))}
874
- if (options.visibility) {formData.append('visibility', options.visibility)}
1202
+ if (options.visibility) {formData.append('visibility', options.visibility || 'public')}
875
1203
  if (options.metadata) {formData.append('metadata', JSON.stringify(options.metadata))}
876
1204
 
877
1205
  try {
878
- return await this._request('/files/upload', {
1206
+ const response = await this._request('/files/upload', {
879
1207
  method: 'POST',
880
1208
  body: formData,
881
1209
  headers: {}, // Let browser set Content-Type for FormData
882
1210
  methodName: 'uploadFile'
883
1211
  })
1212
+
1213
+ if (!response.success) {
1214
+ throw new Error(response.message)
1215
+ }
1216
+
1217
+ return {
1218
+ id: response.data.id,
1219
+ src: `${this._apiUrl}/core/files/public/${response.data.id}/download`,
1220
+ success: true,
1221
+ message: response.message
1222
+ }
884
1223
  } catch (error) {
885
1224
  throw new Error(`File upload failed: ${error.message}`)
886
1225
  }
@@ -897,12 +1236,16 @@ export class CoreService extends BaseService {
897
1236
  formData.append('projectId', projectId)
898
1237
 
899
1238
  try {
900
- return await this._request('/files/upload-project-icon', {
1239
+ const response = await this._request('/files/upload-project-icon', {
901
1240
  method: 'POST',
902
1241
  body: formData,
903
1242
  headers: {}, // Let browser set Content-Type for FormData
904
1243
  methodName: 'updateProjectIcon'
905
1244
  })
1245
+ if (response.success) {
1246
+ return response.data
1247
+ }
1248
+ throw new Error(response.message)
906
1249
  } catch (error) {
907
1250
  throw new Error(`Failed to update project icon: ${error.message}`)
908
1251
  }
@@ -925,7 +1268,7 @@ export class CoreService extends BaseService {
925
1268
  }
926
1269
 
927
1270
  try {
928
- return await this._request('/payments/checkout', {
1271
+ const response = await this._request('/payments/checkout', {
929
1272
  method: 'POST',
930
1273
  body: JSON.stringify({
931
1274
  projectId,
@@ -936,6 +1279,10 @@ export class CoreService extends BaseService {
936
1279
  }),
937
1280
  methodName: 'checkout'
938
1281
  })
1282
+ if (response.success) {
1283
+ return response.data
1284
+ }
1285
+ throw new Error(response.message)
939
1286
  } catch (error) {
940
1287
  throw new Error(`Failed to checkout: ${error.message}`)
941
1288
  }
@@ -947,10 +1294,14 @@ export class CoreService extends BaseService {
947
1294
  throw new Error('Project ID is required')
948
1295
  }
949
1296
  try {
950
- return await this._request(`/payments/subscription/${projectId}`, {
1297
+ const response = await this._request(`/payments/subscription/${projectId}`, {
951
1298
  method: 'GET',
952
1299
  methodName: 'getSubscriptionStatus'
953
1300
  })
1301
+ if (response.success) {
1302
+ return response.data
1303
+ }
1304
+ throw new Error(response.message)
954
1305
  } catch (error) {
955
1306
  throw new Error(`Failed to get subscription status: ${error.message}`)
956
1307
  }
@@ -964,11 +1315,15 @@ export class CoreService extends BaseService {
964
1315
  throw new Error('Domain is required')
965
1316
  }
966
1317
  try {
967
- return await this._request('/dns/records', {
1318
+ const response = await this._request('/dns/records', {
968
1319
  method: 'POST',
969
1320
  body: JSON.stringify({ domain, ...options }),
970
1321
  methodName: 'createDnsRecord'
971
1322
  })
1323
+ if (response.success) {
1324
+ return response.data
1325
+ }
1326
+ throw new Error(response.message)
972
1327
  } catch (error) {
973
1328
  throw new Error(`Failed to create DNS record: ${error.message}`)
974
1329
  }
@@ -980,10 +1335,14 @@ export class CoreService extends BaseService {
980
1335
  throw new Error('Domain is required')
981
1336
  }
982
1337
  try {
983
- return await this._request(`/dns/records/${domain}`, {
1338
+ const response = await this._request(`/dns/records/${domain}`, {
984
1339
  method: 'GET',
985
1340
  methodName: 'getDnsRecord'
986
1341
  })
1342
+ if (response.success) {
1343
+ return response.data
1344
+ }
1345
+ throw new Error(response.message)
987
1346
  } catch (error) {
988
1347
  throw new Error(`Failed to get DNS record: ${error.message}`)
989
1348
  }
@@ -995,10 +1354,14 @@ export class CoreService extends BaseService {
995
1354
  throw new Error('Domain is required')
996
1355
  }
997
1356
  try {
998
- return await this._request(`/dns/records/${domain}`, {
1357
+ const response = await this._request(`/dns/records/${domain}`, {
999
1358
  method: 'DELETE',
1000
1359
  methodName: 'removeDnsRecord'
1001
1360
  })
1361
+ if (response.success) {
1362
+ return response.data
1363
+ }
1364
+ throw new Error(response.message)
1002
1365
  } catch (error) {
1003
1366
  throw new Error(`Failed to remove DNS record: ${error.message}`)
1004
1367
  }
@@ -1010,7 +1373,7 @@ export class CoreService extends BaseService {
1010
1373
  throw new Error('Project key is required')
1011
1374
  }
1012
1375
  try {
1013
- return await this._request('/dns/project-domains', {
1376
+ const response = await this._request('/dns/project-domains', {
1014
1377
  method: 'POST',
1015
1378
  body: JSON.stringify({
1016
1379
  projectKey,
@@ -1019,6 +1382,10 @@ export class CoreService extends BaseService {
1019
1382
  }),
1020
1383
  methodName: 'setProjectDomains'
1021
1384
  })
1385
+ if (response.success) {
1386
+ return response.data
1387
+ }
1388
+ throw new Error(response.message)
1022
1389
  } catch (error) {
1023
1390
  throw new Error(`Failed to set project domains: ${error.message}`)
1024
1391
  }
@@ -1028,10 +1395,14 @@ export class CoreService extends BaseService {
1028
1395
 
1029
1396
  async getHealthStatus () {
1030
1397
  try {
1031
- return await this._request('/health', {
1398
+ const response = await this._request('/health', {
1032
1399
  method: 'GET',
1033
1400
  methodName: 'getHealthStatus'
1034
1401
  })
1402
+ if (response.success) {
1403
+ return response.data
1404
+ }
1405
+ throw new Error(response.message)
1035
1406
  } catch (error) {
1036
1407
  throw new Error(`Failed to get health status: ${error.message}`)
1037
1408
  }
@@ -1070,7 +1441,10 @@ export class CoreService extends BaseService {
1070
1441
  methodName: 'applyProjectChanges'
1071
1442
  })
1072
1443
 
1073
- return response
1444
+ if (response.success) {
1445
+ return response.data
1446
+ }
1447
+ throw new Error(response.message)
1074
1448
  } catch (error) {
1075
1449
  throw new Error(`Failed to apply project changes: ${error.message}`)
1076
1450
  }
@@ -1088,19 +1462,25 @@ export class CoreService extends BaseService {
1088
1462
 
1089
1463
  const {
1090
1464
  branch = 'main',
1465
+ version = 'latest',
1091
1466
  includeHistory = false
1092
1467
  } = options
1093
1468
 
1094
1469
  const queryParams = new URLSearchParams({
1095
1470
  branch,
1471
+ version,
1096
1472
  includeHistory: includeHistory.toString()
1097
1473
  }).toString()
1098
1474
 
1099
1475
  try {
1100
- return await this._request(`/projects/${projectId}/data?${queryParams}`, {
1476
+ const response = await this._request(`/projects/${projectId}/data?${queryParams}`, {
1101
1477
  method: 'GET',
1102
1478
  methodName: 'getProjectData'
1103
1479
  })
1480
+ if (response.success) {
1481
+ return response.data
1482
+ }
1483
+ throw new Error(response.message)
1104
1484
  } catch (error) {
1105
1485
  throw new Error(`Failed to get project data: ${error.message}`)
1106
1486
  }
@@ -1128,10 +1508,14 @@ export class CoreService extends BaseService {
1128
1508
  }).toString()
1129
1509
 
1130
1510
  try {
1131
- return await this._request(`/projects/${projectId}/versions?${queryParams}`, {
1511
+ const response = await this._request(`/projects/${projectId}/versions?${queryParams}`, {
1132
1512
  method: 'GET',
1133
1513
  methodName: 'getProjectVersions'
1134
1514
  })
1515
+ if (response.success) {
1516
+ return response.data
1517
+ }
1518
+ throw new Error(response.message)
1135
1519
  } catch (error) {
1136
1520
  throw new Error(`Failed to get project versions: ${error.message}`)
1137
1521
  }
@@ -1157,7 +1541,7 @@ export class CoreService extends BaseService {
1157
1541
  } = options
1158
1542
 
1159
1543
  try {
1160
- return await this._request(`/projects/${projectId}/restore`, {
1544
+ const response = await this._request(`/projects/${projectId}/restore`, {
1161
1545
  method: 'POST',
1162
1546
  body: JSON.stringify({
1163
1547
  version,
@@ -1167,6 +1551,10 @@ export class CoreService extends BaseService {
1167
1551
  }),
1168
1552
  methodName: 'restoreProjectVersion'
1169
1553
  })
1554
+ if (response.success) {
1555
+ return response.data
1556
+ }
1557
+ throw new Error(response.message)
1170
1558
  } catch (error) {
1171
1559
  throw new Error(`Failed to restore project version: ${error.message}`)
1172
1560
  }
@@ -1277,11 +1665,15 @@ export class CoreService extends BaseService {
1277
1665
  }
1278
1666
 
1279
1667
  try {
1280
- return await this._request(`/projects/${projectId}/pull-requests`, {
1668
+ const response = await this._request(`/projects/${projectId}/pull-requests`, {
1281
1669
  method: 'POST',
1282
1670
  body: JSON.stringify(pullRequestData),
1283
1671
  methodName: 'createPullRequest'
1284
1672
  })
1673
+ if (response.success) {
1674
+ return response.data
1675
+ }
1676
+ throw new Error(response.message)
1285
1677
  } catch (error) {
1286
1678
  throw new Error(`Failed to create pull request: ${error.message}`)
1287
1679
  }
@@ -1314,10 +1706,14 @@ export class CoreService extends BaseService {
1314
1706
  if (target) {queryParams.append('target', target)}
1315
1707
 
1316
1708
  try {
1317
- return await this._request(`/projects/${projectId}/pull-requests?${queryParams.toString()}`, {
1709
+ const response = await this._request(`/projects/${projectId}/pull-requests?${queryParams.toString()}`, {
1318
1710
  method: 'GET',
1319
1711
  methodName: 'listPullRequests'
1320
1712
  })
1713
+ if (response.success) {
1714
+ return response.data
1715
+ }
1716
+ throw new Error(response.message)
1321
1717
  } catch (error) {
1322
1718
  throw new Error(`Failed to list pull requests: ${error.message}`)
1323
1719
  }
@@ -1336,10 +1732,14 @@ export class CoreService extends BaseService {
1336
1732
  }
1337
1733
 
1338
1734
  try {
1339
- return await this._request(`/projects/${projectId}/pull-requests/${prId}`, {
1735
+ const response = await this._request(`/projects/${projectId}/pull-requests/${prId}`, {
1340
1736
  method: 'GET',
1341
1737
  methodName: 'getPullRequest'
1342
1738
  })
1739
+ if (response.success) {
1740
+ return response.data
1741
+ }
1742
+ throw new Error(response.message)
1343
1743
  } catch (error) {
1344
1744
  throw new Error(`Failed to get pull request: ${error.message}`)
1345
1745
  }
@@ -1363,11 +1763,15 @@ export class CoreService extends BaseService {
1363
1763
  }
1364
1764
 
1365
1765
  try {
1366
- return await this._request(`/projects/${projectId}/pull-requests/${prId}/review`, {
1766
+ const response = await this._request(`/projects/${projectId}/pull-requests/${prId}/review`, {
1367
1767
  method: 'POST',
1368
1768
  body: JSON.stringify(reviewData),
1369
1769
  methodName: 'reviewPullRequest'
1370
1770
  })
1771
+ if (response.success) {
1772
+ return response.data
1773
+ }
1774
+ throw new Error(response.message)
1371
1775
  } catch (error) {
1372
1776
  throw new Error(`Failed to review pull request: ${error.message}`)
1373
1777
  }
@@ -1389,11 +1793,15 @@ export class CoreService extends BaseService {
1389
1793
  }
1390
1794
 
1391
1795
  try {
1392
- return await this._request(`/projects/${projectId}/pull-requests/${prId}/comment`, {
1796
+ const response = await this._request(`/projects/${projectId}/pull-requests/${prId}/comment`, {
1393
1797
  method: 'POST',
1394
1798
  body: JSON.stringify(commentData),
1395
1799
  methodName: 'addPullRequestComment'
1396
1800
  })
1801
+ if (response.success) {
1802
+ return response.data
1803
+ }
1804
+ throw new Error(response.message)
1397
1805
  } catch (error) {
1398
1806
  throw new Error(`Failed to add pull request comment: ${error.message}`)
1399
1807
  }
@@ -1417,7 +1825,10 @@ export class CoreService extends BaseService {
1417
1825
  methodName: 'mergePullRequest'
1418
1826
  })
1419
1827
 
1420
- return response
1828
+ if (response.success) {
1829
+ return response.data
1830
+ }
1831
+ throw new Error(response.message)
1421
1832
  } catch (error) {
1422
1833
  // Handle specific merge conflict errors
1423
1834
  if (error.message.includes('conflicts') || error.message.includes('409')) {
@@ -1440,10 +1851,14 @@ export class CoreService extends BaseService {
1440
1851
  }
1441
1852
 
1442
1853
  try {
1443
- return await this._request(`/projects/${projectId}/pull-requests/${prId}/diff`, {
1854
+ const response = await this._request(`/projects/${projectId}/pull-requests/${prId}/diff`, {
1444
1855
  method: 'GET',
1445
1856
  methodName: 'getPullRequestDiff'
1446
1857
  })
1858
+ if (response.success) {
1859
+ return response.data
1860
+ }
1861
+ throw new Error(response.message)
1447
1862
  } catch (error) {
1448
1863
  throw new Error(`Failed to get pull request diff: ${error.message}`)
1449
1864
  }
@@ -1577,10 +1992,14 @@ export class CoreService extends BaseService {
1577
1992
  }
1578
1993
 
1579
1994
  try {
1580
- return await this._request(`/projects/${projectId}/branches`, {
1995
+ const response = await this._request(`/projects/${projectId}/branches`, {
1581
1996
  method: 'GET',
1582
1997
  methodName: 'listBranches'
1583
1998
  })
1999
+ if (response.success) {
2000
+ return response.data
2001
+ }
2002
+ throw new Error(response.message)
1584
2003
  } catch (error) {
1585
2004
  throw new Error(`Failed to list branches: ${error.message}`)
1586
2005
  }
@@ -1601,11 +2020,15 @@ export class CoreService extends BaseService {
1601
2020
  const { name, source = 'main' } = branchData
1602
2021
 
1603
2022
  try {
1604
- return await this._request(`/projects/${projectId}/branches`, {
2023
+ const response = await this._request(`/projects/${projectId}/branches`, {
1605
2024
  method: 'POST',
1606
2025
  body: JSON.stringify({ name, source }),
1607
2026
  methodName: 'createBranch'
1608
2027
  })
2028
+ if (response.success) {
2029
+ return response.data
2030
+ }
2031
+ throw new Error(response.message)
1609
2032
  } catch (error) {
1610
2033
  throw new Error(`Failed to create branch: ${error.message}`)
1611
2034
  }
@@ -1627,10 +2050,14 @@ export class CoreService extends BaseService {
1627
2050
  }
1628
2051
 
1629
2052
  try {
1630
- return await this._request(`/projects/${projectId}/branches/${encodeURIComponent(branchName)}`, {
2053
+ const response = await this._request(`/projects/${projectId}/branches/${encodeURIComponent(branchName)}`, {
1631
2054
  method: 'DELETE',
1632
2055
  methodName: 'deleteBranch'
1633
2056
  })
2057
+ if (response.success) {
2058
+ return response.data
2059
+ }
2060
+ throw new Error(response.message)
1634
2061
  } catch (error) {
1635
2062
  throw new Error(`Failed to delete branch: ${error.message}`)
1636
2063
  }
@@ -1655,11 +2082,15 @@ export class CoreService extends BaseService {
1655
2082
  }
1656
2083
 
1657
2084
  try {
1658
- return await this._request(`/projects/${projectId}/branches/${encodeURIComponent(branchName)}/rename`, {
2085
+ const response = await this._request(`/projects/${projectId}/branches/${encodeURIComponent(branchName)}/rename`, {
1659
2086
  method: 'POST',
1660
2087
  body: JSON.stringify({ newName }),
1661
2088
  methodName: 'renameBranch'
1662
2089
  })
2090
+ if (response.success) {
2091
+ return response.data
2092
+ }
2093
+ throw new Error(response.message)
1663
2094
  } catch (error) {
1664
2095
  throw new Error(`Failed to rename branch: ${error.message}`)
1665
2096
  }
@@ -1668,7 +2099,7 @@ export class CoreService extends BaseService {
1668
2099
  /**
1669
2100
  * Get changes/diff for a branch compared to another version
1670
2101
  */
1671
- async getBranchChanges (projectId, branchName, options = {}) {
2102
+ async getBranchChanges (projectId, branchName = 'main', options = {}) {
1672
2103
  this._requireReady('getBranchChanges')
1673
2104
  if (!projectId) {
1674
2105
  throw new Error('Project ID is required')
@@ -1688,10 +2119,14 @@ export class CoreService extends BaseService {
1688
2119
  const url = `/projects/${projectId}/branches/${encodeURIComponent(branchName)}/changes${queryString ? `?${queryString}` : ''}`
1689
2120
 
1690
2121
  try {
1691
- return await this._request(url, {
2122
+ const response = await this._request(url, {
1692
2123
  method: 'GET',
1693
2124
  methodName: 'getBranchChanges'
1694
2125
  })
2126
+ if (response.success) {
2127
+ return response.data
2128
+ }
2129
+ throw new Error(response.message)
1695
2130
  } catch (error) {
1696
2131
  throw new Error(`Failed to get branch changes: ${error.message}`)
1697
2132
  }
@@ -1726,11 +2161,15 @@ export class CoreService extends BaseService {
1726
2161
  }
1727
2162
 
1728
2163
  try {
1729
- return await this._request(`/projects/${projectId}/branches/${encodeURIComponent(branchName)}/merge`, {
2164
+ const response = await this._request(`/projects/${projectId}/branches/${encodeURIComponent(branchName)}/merge`, {
1730
2165
  method: 'POST',
1731
2166
  body: JSON.stringify(requestBody),
1732
2167
  methodName: 'mergeBranch'
1733
2168
  })
2169
+ if (response.success) {
2170
+ return response.data
2171
+ }
2172
+ throw new Error(response.message)
1734
2173
  } catch (error) {
1735
2174
  // Handle merge conflicts specifically
1736
2175
  if (error.message.includes('conflicts') || error.message.includes('409')) {
@@ -1753,10 +2192,14 @@ export class CoreService extends BaseService {
1753
2192
  }
1754
2193
 
1755
2194
  try {
1756
- return await this._request(`/projects/${projectId}/branches/${encodeURIComponent(branchName)}/reset`, {
2195
+ const response = await this._request(`/projects/${projectId}/branches/${encodeURIComponent(branchName)}/reset`, {
1757
2196
  method: 'POST',
1758
2197
  methodName: 'resetBranch'
1759
2198
  })
2199
+ if (response.success) {
2200
+ return response.data
2201
+ }
2202
+ throw new Error(response.message)
1760
2203
  } catch (error) {
1761
2204
  throw new Error(`Failed to reset branch: ${error.message}`)
1762
2205
  }
@@ -1777,11 +2220,15 @@ export class CoreService extends BaseService {
1777
2220
  const { version, branch = 'main' } = publishData
1778
2221
 
1779
2222
  try {
1780
- return await this._request(`/projects/${projectId}/publish`, {
2223
+ const response = await this._request(`/projects/${projectId}/publish`, {
1781
2224
  method: 'POST',
1782
2225
  body: JSON.stringify({ version, branch }),
1783
2226
  methodName: 'publishVersion'
1784
2227
  })
2228
+ if (response.success) {
2229
+ return response.data
2230
+ }
2231
+ throw new Error(response.message)
1785
2232
  } catch (error) {
1786
2233
  throw new Error(`Failed to publish version: ${error.message}`)
1787
2234
  }
@@ -1930,6 +2377,164 @@ export class CoreService extends BaseService {
1930
2377
  return await this.deleteBranch(projectId, branchName)
1931
2378
  }
1932
2379
 
2380
+ // ==================== ADMIN METHODS ====================
2381
+
2382
+ /**
2383
+ * Get admin users list with comprehensive filtering and search capabilities
2384
+ * Requires admin or super_admin global role
2385
+ */
2386
+ async getAdminUsers (params = {}) {
2387
+ this._requireReady('getAdminUsers')
2388
+
2389
+ const {
2390
+ emails,
2391
+ ids,
2392
+ query,
2393
+ status,
2394
+ page = 1,
2395
+ limit = 50,
2396
+ sort = { field: 'createdAt', order: 'desc' }
2397
+ } = params
2398
+
2399
+ const queryParams = new URLSearchParams()
2400
+
2401
+ // Add query parameters
2402
+ if (emails) {
2403
+ queryParams.append('emails', emails)
2404
+ }
2405
+ if (ids) {
2406
+ queryParams.append('ids', ids)
2407
+ }
2408
+ if (query) {
2409
+ queryParams.append('query', query)
2410
+ }
2411
+ if (status) {
2412
+ queryParams.append('status', status)
2413
+ }
2414
+ if (page) {
2415
+ queryParams.append('page', page.toString())
2416
+ }
2417
+ if (limit) {
2418
+ queryParams.append('limit', limit.toString())
2419
+ }
2420
+ if (sort && sort.field) {
2421
+ queryParams.append('sort[field]', sort.field)
2422
+ queryParams.append('sort[order]', sort.order || 'desc')
2423
+ }
2424
+
2425
+ const queryString = queryParams.toString()
2426
+ const url = `/users/admin/users${queryString ? `?${queryString}` : ''}`
2427
+
2428
+ try {
2429
+ const response = await this._request(url, {
2430
+ method: 'GET',
2431
+ methodName: 'getAdminUsers'
2432
+ })
2433
+ if (response.success) {
2434
+ return response.data
2435
+ }
2436
+ throw new Error(response.message)
2437
+ } catch (error) {
2438
+ throw new Error(`Failed to get admin users: ${error.message}`)
2439
+ }
2440
+ }
2441
+
2442
+ /**
2443
+ * Assign projects to a specific user
2444
+ * Requires admin or super_admin global role
2445
+ */
2446
+ async assignProjectsToUser (userId, options = {}) {
2447
+ this._requireReady('assignProjectsToUser')
2448
+
2449
+ if (!userId) {
2450
+ throw new Error('User ID is required')
2451
+ }
2452
+
2453
+ const {
2454
+ projectIds,
2455
+ role = 'guest'
2456
+ } = options
2457
+
2458
+ const requestBody = {
2459
+ userId,
2460
+ role
2461
+ }
2462
+
2463
+ // Only include projectIds if provided (otherwise assigns all projects)
2464
+ if (projectIds && Array.isArray(projectIds)) {
2465
+ requestBody.projectIds = projectIds
2466
+ }
2467
+
2468
+ try {
2469
+ const response = await this._request('/assign-projects', {
2470
+ method: 'POST',
2471
+ body: JSON.stringify(requestBody),
2472
+ methodName: 'assignProjectsToUser'
2473
+ })
2474
+ if (response.success) {
2475
+ return response.data
2476
+ }
2477
+ throw new Error(response.message)
2478
+ } catch (error) {
2479
+ throw new Error(`Failed to assign projects to user: ${error.message}`)
2480
+ }
2481
+ }
2482
+
2483
+ /**
2484
+ * Helper method for admin users search
2485
+ */
2486
+ async searchAdminUsers (searchQuery, options = {}) {
2487
+ return await this.getAdminUsers({
2488
+ query: searchQuery,
2489
+ ...options
2490
+ })
2491
+ }
2492
+
2493
+ /**
2494
+ * Helper method to get admin users by email list
2495
+ */
2496
+ async getAdminUsersByEmails (emails, options = {}) {
2497
+ const emailList = Array.isArray(emails) ? emails.join(',') : emails
2498
+ return await this.getAdminUsers({
2499
+ emails: emailList,
2500
+ ...options
2501
+ })
2502
+ }
2503
+
2504
+ /**
2505
+ * Helper method to get admin users by ID list
2506
+ */
2507
+ async getAdminUsersByIds (ids, options = {}) {
2508
+ const idList = Array.isArray(ids) ? ids.join(',') : ids
2509
+ return await this.getAdminUsers({
2510
+ ids: idList,
2511
+ ...options
2512
+ })
2513
+ }
2514
+
2515
+ /**
2516
+ * Helper method to assign specific projects to a user with a specific role
2517
+ */
2518
+ async assignSpecificProjectsToUser (userId, projectIds, role = 'guest') {
2519
+ if (!Array.isArray(projectIds) || projectIds.length === 0) {
2520
+ throw new Error('Project IDs must be a non-empty array')
2521
+ }
2522
+
2523
+ return await this.assignProjectsToUser(userId, {
2524
+ projectIds,
2525
+ role
2526
+ })
2527
+ }
2528
+
2529
+ /**
2530
+ * Helper method to assign all projects to a user with a specific role
2531
+ */
2532
+ async assignAllProjectsToUser (userId, role = 'guest') {
2533
+ return await this.assignProjectsToUser(userId, {
2534
+ role
2535
+ })
2536
+ }
2537
+
1933
2538
  // Cleanup
1934
2539
  destroy () {
1935
2540
  if (this._tokenManager) {