@symbo.ls/sdk 2.34.34 → 3.1.1

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 (187) hide show
  1. package/README.md +13 -315
  2. package/dist/cjs/config/environment.js +50 -124
  3. package/dist/cjs/index.js +22 -169
  4. package/dist/cjs/services/AIService.js +155 -0
  5. package/dist/cjs/services/AuthService.js +277 -751
  6. package/dist/cjs/services/BaseService.js +6 -158
  7. package/dist/cjs/services/BasedService.js +679 -0
  8. package/dist/cjs/services/SocketIOService.js +309 -0
  9. package/dist/cjs/services/SocketService.js +161 -0
  10. package/dist/cjs/services/SymstoryService.js +485 -0
  11. package/dist/cjs/services/index.js +16 -64
  12. package/dist/cjs/utils/basedQuerys.js +61 -0
  13. package/dist/cjs/utils/permission.js +4 -4
  14. package/dist/cjs/utils/services.js +80 -301
  15. package/dist/cjs/utils/symstoryClient.js +228 -0
  16. package/dist/cjs/utils/validation.js +3 -0
  17. package/dist/esm/config/environment.js +50 -124
  18. package/dist/esm/index.js +11282 -49789
  19. package/dist/esm/services/AIService.js +185 -0
  20. package/dist/esm/services/AuthService.js +358 -1506
  21. package/dist/esm/services/BaseService.js +6 -757
  22. package/dist/esm/services/BasedService.js +4651 -0
  23. package/dist/esm/services/SocketIOService.js +467 -0
  24. package/dist/esm/services/SocketService.js +191 -0
  25. package/dist/esm/services/SymstoryService.js +6849 -0
  26. package/dist/esm/services/index.js +11188 -49352
  27. package/dist/esm/utils/basedQuerys.js +43 -0
  28. package/dist/esm/utils/permission.js +4 -4
  29. package/dist/esm/utils/services.js +80 -301
  30. package/dist/esm/utils/symstoryClient.js +334 -0
  31. package/dist/esm/utils/validation.js +26 -93
  32. package/dist/node/config/environment.js +50 -124
  33. package/dist/node/index.js +26 -202
  34. package/dist/node/services/AIService.js +136 -0
  35. package/dist/node/services/AuthService.js +278 -751
  36. package/dist/node/services/BaseService.js +6 -148
  37. package/dist/node/services/BasedService.js +650 -0
  38. package/dist/node/services/SocketIOService.js +280 -0
  39. package/dist/node/services/SocketService.js +142 -0
  40. package/dist/node/services/SymstoryService.js +456 -0
  41. package/dist/node/services/index.js +16 -64
  42. package/dist/node/utils/basedQuerys.js +42 -0
  43. package/dist/node/utils/permission.js +4 -4
  44. package/dist/node/utils/services.js +80 -301
  45. package/dist/node/utils/symstoryClient.js +199 -0
  46. package/dist/node/utils/validation.js +3 -0
  47. package/package.json +21 -43
  48. package/src/config/environment.js +50 -126
  49. package/src/index.js +24 -208
  50. package/src/services/AIService.js +150 -0
  51. package/src/services/AuthService.js +298 -893
  52. package/src/services/BaseService.js +6 -166
  53. package/src/services/BasedService.js +722 -0
  54. package/src/services/SocketIOService.js +356 -0
  55. package/src/services/SocketService.js +168 -0
  56. package/src/services/SymstoryService.js +563 -0
  57. package/src/services/index.js +13 -80
  58. package/src/utils/basedQuerys.js +41 -0
  59. package/src/utils/permission.js +4 -4
  60. package/src/utils/services.js +83 -325
  61. package/src/utils/symstoryClient.js +218 -0
  62. package/src/utils/validation.js +3 -0
  63. package/dist/cjs/services/AdminService.js +0 -351
  64. package/dist/cjs/services/BranchService.js +0 -484
  65. package/dist/cjs/services/CollabService.js +0 -743
  66. package/dist/cjs/services/DnsService.js +0 -340
  67. package/dist/cjs/services/FeatureFlagService.js +0 -175
  68. package/dist/cjs/services/FileService.js +0 -201
  69. package/dist/cjs/services/IntegrationService.js +0 -538
  70. package/dist/cjs/services/MetricsService.js +0 -62
  71. package/dist/cjs/services/PaymentService.js +0 -271
  72. package/dist/cjs/services/PlanService.js +0 -426
  73. package/dist/cjs/services/ProjectService.js +0 -1207
  74. package/dist/cjs/services/PullRequestService.js +0 -503
  75. package/dist/cjs/services/ScreenshotService.js +0 -304
  76. package/dist/cjs/services/SubscriptionService.js +0 -396
  77. package/dist/cjs/services/TrackingService.js +0 -661
  78. package/dist/cjs/services/WaitlistService.js +0 -148
  79. package/dist/cjs/state/RootStateManager.js +0 -65
  80. package/dist/cjs/state/rootEventBus.js +0 -74
  81. package/dist/cjs/utils/CollabClient.js +0 -223
  82. package/dist/cjs/utils/TokenManager.js +0 -422
  83. package/dist/cjs/utils/changePreprocessor.js +0 -199
  84. package/dist/cjs/utils/jsonDiff.js +0 -145
  85. package/dist/cjs/utils/ordering.js +0 -309
  86. package/dist/esm/services/AdminService.js +0 -1132
  87. package/dist/esm/services/BranchService.js +0 -1265
  88. package/dist/esm/services/CollabService.js +0 -26838
  89. package/dist/esm/services/DnsService.js +0 -1121
  90. package/dist/esm/services/FeatureFlagService.js +0 -956
  91. package/dist/esm/services/FileService.js +0 -982
  92. package/dist/esm/services/IntegrationService.js +0 -1319
  93. package/dist/esm/services/MetricsService.js +0 -843
  94. package/dist/esm/services/PaymentService.js +0 -1052
  95. package/dist/esm/services/PlanService.js +0 -1207
  96. package/dist/esm/services/ProjectService.js +0 -2526
  97. package/dist/esm/services/PullRequestService.js +0 -1284
  98. package/dist/esm/services/ScreenshotService.js +0 -1085
  99. package/dist/esm/services/SubscriptionService.js +0 -1177
  100. package/dist/esm/services/TrackingService.js +0 -18343
  101. package/dist/esm/services/WaitlistService.js +0 -929
  102. package/dist/esm/state/RootStateManager.js +0 -90
  103. package/dist/esm/state/rootEventBus.js +0 -56
  104. package/dist/esm/utils/CollabClient.js +0 -18901
  105. package/dist/esm/utils/TokenManager.js +0 -408
  106. package/dist/esm/utils/changePreprocessor.js +0 -542
  107. package/dist/esm/utils/jsonDiff.js +0 -7011
  108. package/dist/esm/utils/ordering.js +0 -291
  109. package/dist/node/services/AdminService.js +0 -332
  110. package/dist/node/services/BranchService.js +0 -465
  111. package/dist/node/services/CollabService.js +0 -724
  112. package/dist/node/services/DnsService.js +0 -321
  113. package/dist/node/services/FeatureFlagService.js +0 -156
  114. package/dist/node/services/FileService.js +0 -182
  115. package/dist/node/services/IntegrationService.js +0 -519
  116. package/dist/node/services/MetricsService.js +0 -43
  117. package/dist/node/services/PaymentService.js +0 -252
  118. package/dist/node/services/PlanService.js +0 -407
  119. package/dist/node/services/ProjectService.js +0 -1188
  120. package/dist/node/services/PullRequestService.js +0 -484
  121. package/dist/node/services/ScreenshotService.js +0 -285
  122. package/dist/node/services/SubscriptionService.js +0 -377
  123. package/dist/node/services/TrackingService.js +0 -632
  124. package/dist/node/services/WaitlistService.js +0 -129
  125. package/dist/node/state/RootStateManager.js +0 -36
  126. package/dist/node/state/rootEventBus.js +0 -55
  127. package/dist/node/utils/CollabClient.js +0 -194
  128. package/dist/node/utils/TokenManager.js +0 -403
  129. package/dist/node/utils/changePreprocessor.js +0 -180
  130. package/dist/node/utils/jsonDiff.js +0 -116
  131. package/dist/node/utils/ordering.js +0 -290
  132. package/src/services/AdminService.js +0 -374
  133. package/src/services/BranchService.js +0 -536
  134. package/src/services/CollabService.js +0 -900
  135. package/src/services/DnsService.js +0 -366
  136. package/src/services/FeatureFlagService.js +0 -174
  137. package/src/services/FileService.js +0 -213
  138. package/src/services/IntegrationService.js +0 -548
  139. package/src/services/MetricsService.js +0 -40
  140. package/src/services/PaymentService.js +0 -287
  141. package/src/services/PlanService.js +0 -468
  142. package/src/services/ProjectService.js +0 -1366
  143. package/src/services/PullRequestService.js +0 -537
  144. package/src/services/ScreenshotService.js +0 -258
  145. package/src/services/SubscriptionService.js +0 -425
  146. package/src/services/TrackingService.js +0 -853
  147. package/src/services/WaitlistService.js +0 -130
  148. package/src/services/tests/BranchService/createBranch.test.js +0 -153
  149. package/src/services/tests/BranchService/deleteBranch.test.js +0 -173
  150. package/src/services/tests/BranchService/getBranchChanges.test.js +0 -146
  151. package/src/services/tests/BranchService/listBranches.test.js +0 -87
  152. package/src/services/tests/BranchService/mergeBranch.test.js +0 -210
  153. package/src/services/tests/BranchService/publishVersion.test.js +0 -183
  154. package/src/services/tests/BranchService/renameBranch.test.js +0 -240
  155. package/src/services/tests/BranchService/resetBranch.test.js +0 -152
  156. package/src/services/tests/FeatureFlagService/adminFeatureFlags.test.js +0 -67
  157. package/src/services/tests/FeatureFlagService/getFeatureFlags.test.js +0 -75
  158. package/src/services/tests/FileService/createFileFormData.test.js +0 -74
  159. package/src/services/tests/FileService/getFileUrl.test.js +0 -69
  160. package/src/services/tests/FileService/updateProjectIcon.test.js +0 -109
  161. package/src/services/tests/FileService/uploadDocument.test.js +0 -36
  162. package/src/services/tests/FileService/uploadFile.test.js +0 -78
  163. package/src/services/tests/FileService/uploadFileWithValidation.test.js +0 -114
  164. package/src/services/tests/FileService/uploadImage.test.js +0 -36
  165. package/src/services/tests/FileService/uploadMultipleFiles.test.js +0 -111
  166. package/src/services/tests/FileService/validateFile.test.js +0 -63
  167. package/src/services/tests/PlanService/createPlan.test.js +0 -104
  168. package/src/services/tests/PlanService/createPlanWithValidation.test.js +0 -523
  169. package/src/services/tests/PlanService/deletePlan.test.js +0 -92
  170. package/src/services/tests/PlanService/getActivePlans.test.js +0 -123
  171. package/src/services/tests/PlanService/getAdminPlans.test.js +0 -84
  172. package/src/services/tests/PlanService/getPlan.test.js +0 -50
  173. package/src/services/tests/PlanService/getPlanByKey.test.js +0 -109
  174. package/src/services/tests/PlanService/getPlanWithValidation.test.js +0 -85
  175. package/src/services/tests/PlanService/getPlans.test.js +0 -53
  176. package/src/services/tests/PlanService/getPlansByPriceRange.test.js +0 -109
  177. package/src/services/tests/PlanService/getPlansWithValidation.test.js +0 -48
  178. package/src/services/tests/PlanService/initializePlans.test.js +0 -75
  179. package/src/services/tests/PlanService/updatePlan.test.js +0 -111
  180. package/src/services/tests/PlanService/updatePlanWithValidation.test.js +0 -556
  181. package/src/state/RootStateManager.js +0 -76
  182. package/src/state/rootEventBus.js +0 -67
  183. package/src/utils/CollabClient.js +0 -248
  184. package/src/utils/TokenManager.js +0 -479
  185. package/src/utils/changePreprocessor.js +0 -239
  186. package/src/utils/jsonDiff.js +0 -144
  187. package/src/utils/ordering.js +0 -271
@@ -1,885 +1,173 @@
1
+ /* eslint-disable require-await */
1
2
  import { BaseService } from './BaseService.js'
2
3
  import {
4
+ PERMISSION_MAP,
3
5
  ROLE_PERMISSIONS,
4
6
  TIER_FEATURES,
5
7
  PROJECT_ROLE_PERMISSIONS
6
8
  } from '../utils/permission.js'
7
9
 
8
- const PLUGIN_SESSION_STORAGE_KEY = 'plugin_auth_session'
9
-
10
10
  export class AuthService extends BaseService {
11
- constructor(config) {
11
+ constructor (config) {
12
12
  super(config)
13
13
  this._userRoles = new Set(['guest', 'editor', 'admin', 'owner'])
14
14
  this._projectTiers = new Set([
15
15
  'ready',
16
- 'starter',
16
+ 'free',
17
17
  'pro1',
18
18
  'pro2',
19
19
  'enterprise'
20
20
  ])
21
- this._projectRoleCache = new Map() // Cache for project roles
22
- this._roleCacheExpiry = 5 * 60 * 1000 // 5 minutes cache expiry
23
- this._pluginSession = null
24
-
25
- this._resolvePluginSession(
26
- config?.session ||
27
- config?.pluginSession ||
28
- config?.options?.pluginSession ||
29
- config?.context?.pluginSession ||
30
- null
31
- )
32
- }
33
-
34
- // Use BaseService.init/_request/_requireReady implementations
35
-
36
- // ==================== AUTH METHODS ====================
37
-
38
- async register(userData, options = {}) {
39
- try {
40
- const { payload, session } = this._preparePluginPayload(
41
- { ...(userData || {}) },
42
- options.session
43
- )
44
-
45
- const response = await this._request('/auth/register', {
46
- method: 'POST',
47
- body: JSON.stringify(payload),
48
- methodName: 'register'
49
- })
50
- if (response.success) {
51
- if (session) {
52
- this._clearPluginSession(session)
53
- }
54
- return response.data
55
- }
56
- throw new Error(response.message)
57
- } catch (error) {
58
- throw new Error(`Registration failed: ${error.message}`, { cause: error })
59
- }
60
- }
61
-
62
- async login(email, password, options = {}) {
63
- try {
64
- const { payload, session } = this._preparePluginPayload(
65
- {
66
- email,
67
- password
68
- },
69
- options.session
70
- )
71
-
72
- const response = await this._request('/auth/login', {
73
- method: 'POST',
74
- body: JSON.stringify(payload),
75
- methodName: 'login'
76
- })
77
-
78
- // Handle new response format: response.data.tokens
79
- if (response.success && response.data && response.data.tokens) {
80
- const { tokens } = response.data
81
- const tokenData = {
82
- access_token: tokens.accessToken,
83
- refresh_token: tokens.refreshToken,
84
- expires_in: tokens.accessTokenExp?.expiresIn,
85
- token_type: 'Bearer'
86
- }
87
-
88
- // Set tokens in TokenManager (will handle persistence and refresh scheduling)
89
- if (this._tokenManager) {
90
- this._tokenManager.setTokens(tokenData)
91
- }
92
- }
93
-
94
- if (response.success) {
95
- if (session) {
96
- this._clearPluginSession(session)
97
- }
98
- return response.data
99
- }
100
- throw new Error(response.message)
101
- } catch (error) {
102
- throw new Error(`Login failed: ${error.message}`, { cause: error })
103
- }
104
- }
105
-
106
- async logout() {
107
- this._requireReady('logout')
108
- try {
109
- // Call the logout API endpoint
110
- await this._request('/auth/logout', {
111
- method: 'POST',
112
- methodName: 'logout'
113
- })
114
-
115
- // Clear tokens from TokenManager and context
116
- if (this._tokenManager) {
117
- this._tokenManager.clearTokens()
118
- }
119
- } catch (error) {
120
- // Even if the API call fails, clear local tokens
121
- if (this._tokenManager) {
122
- this._tokenManager.clearTokens()
123
- }
124
-
125
- throw new Error(`Logout failed: ${error.message}`, { cause: error })
126
- }
127
- }
128
-
129
- async refreshToken(refreshToken) {
130
- try {
131
- const response = await this._request('/auth/refresh', {
132
- method: 'POST',
133
- body: JSON.stringify({ refreshToken }),
134
- methodName: 'refreshToken'
135
- })
136
- if (response.success) {
137
- return response.data
138
- }
139
- throw new Error(response.message)
140
- } catch (error) {
141
- throw new Error(`Token refresh failed: ${error.message}`, { cause: error })
142
- }
21
+ this._initialized = false
143
22
  }
144
23
 
145
- async googleAuth(idToken, inviteToken = null, options = {}) {
24
+ // eslint-disable-next-line no-empty-pattern
25
+ init ({}) {
146
26
  try {
147
- const { payload, session } = this._preparePluginPayload({ idToken }, options.session)
148
- if (inviteToken) {
149
- payload.inviteToken = inviteToken
150
- }
151
-
152
- const response = await this._request('/auth/google', {
153
- method: 'POST',
154
- body: JSON.stringify(payload),
155
- methodName: 'googleAuth'
156
- })
157
-
158
- // Handle new response format: response.data.tokens
159
- if (response.success && response.data && response.data.tokens) {
160
- const { tokens } = response.data
161
- const tokenData = {
162
- access_token: tokens.accessToken,
163
- refresh_token: tokens.refreshToken,
164
- expires_in: tokens.accessTokenExp?.expiresIn,
165
- token_type: 'Bearer'
166
- }
167
-
168
- // Set tokens in TokenManager
169
- if (this._tokenManager) {
170
- this._tokenManager.setTokens(tokenData)
27
+ const { authToken, appKey } = this._context || {}
28
+
29
+ // Store masked configuration info
30
+ this._info = {
31
+ config: {
32
+ appKey: appKey
33
+ ? `${appKey.substr(0, 4)}...${appKey.substr(-4)}`
34
+ : undefined, // eslint-disable-line no-undefined
35
+ hasToken: Boolean(authToken)
171
36
  }
172
37
  }
173
38
 
174
- if (response.success) {
175
- if (session) {
176
- this._clearPluginSession(session)
177
- }
178
- return response.data
179
- }
180
- throw new Error(response.message)
39
+ this._initialized = true
40
+ this._setReady()
181
41
  } catch (error) {
182
- throw new Error(`Google auth failed: ${error.message}`, { cause: error })
183
- }
184
- }
185
-
186
- async githubAuth(code, inviteToken = null, options = {}) {
187
- try {
188
- const { payload, session } = this._preparePluginPayload({ code }, options.session)
189
- if (inviteToken) {
190
- payload.inviteToken = inviteToken
191
- }
192
-
193
- const response = await this._request('/auth/github', {
194
- method: 'POST',
195
- body: JSON.stringify(payload),
196
- methodName: 'githubAuth'
197
- })
198
-
199
- // Handle new response format: response.data.tokens
200
- if (response.success && response.data && response.data.tokens) {
201
- const { tokens } = response.data
202
- const tokenData = {
203
- access_token: tokens.accessToken,
204
- refresh_token: tokens.refreshToken,
205
- expires_in: tokens.accessTokenExp?.expiresIn,
206
- token_type: 'Bearer'
207
- }
208
-
209
- // Set tokens in TokenManager
210
- if (this._tokenManager) {
211
- this._tokenManager.setTokens(tokenData)
212
- }
213
- }
214
-
215
- if (response.success) {
216
- if (session) {
217
- this._clearPluginSession(session)
218
- }
219
- return response.data
220
- }
221
- throw new Error(response.message)
222
- } catch (error) {
223
- throw new Error(`GitHub auth failed: ${error.message}`, { cause: error })
224
- }
225
- }
226
-
227
- async googleAuthCallback (code, redirectUri, inviteToken = null, options = {}) {
228
- try {
229
- const { payload: body, session } = this._preparePluginPayload(
230
- { code, redirectUri },
231
- options.session
232
- )
233
- if (inviteToken) {
234
- body.inviteToken = inviteToken
235
- }
236
-
237
- const response = await this._request('/auth/google/callback', {
238
- method: 'POST',
239
- body: JSON.stringify(body),
240
- methodName: 'googleAuthCallback'
241
- })
242
-
243
- // Handle new response format: response.data.tokens
244
- if (response.success && response.data && response.data.tokens) {
245
- const { tokens } = response.data
246
- const tokenData = {
247
- access_token: tokens.accessToken,
248
- refresh_token: tokens.refreshToken,
249
- expires_in: tokens.accessTokenExp?.expiresIn,
250
- token_type: 'Bearer'
251
- }
252
-
253
- // Set tokens in TokenManager
254
- if (this._tokenManager) {
255
- this._tokenManager.setTokens(tokenData)
256
- }
257
- }
258
-
259
- if (response.success) {
260
- if (session) {
261
- this._clearPluginSession(session)
262
- }
263
- return response.data
264
- }
265
- throw new Error(response.message)
266
- } catch (error) {
267
- throw new Error(`Google auth callback failed: ${error.message}`, { cause: error })
268
- }
269
- }
270
-
271
- async requestPasswordReset(email) {
272
- try {
273
- const response = await this._request('/auth/request-password-reset', {
274
- method: 'POST',
275
- body: JSON.stringify({ email }),
276
- methodName: 'requestPasswordReset'
277
- })
278
- if (response.success) {
279
- return response.data
280
- }
281
- throw new Error(response.message)
282
- } catch (error) {
283
- throw new Error(`Password reset request failed: ${error.message}`, { cause: error })
42
+ this._setError(error)
43
+ throw error
284
44
  }
285
45
  }
286
46
 
287
- async confirmPasswordReset(token, password) {
288
- try {
289
- const response = await this._request('/auth/reset-password-confirm', {
290
- method: 'POST',
291
- body: JSON.stringify({ token, password }),
292
- methodName: 'confirmPasswordReset'
293
- })
294
- if (response.success) {
295
- return response.data
296
- }
297
- throw new Error(response.message)
298
- } catch (error) {
299
- throw new Error(`Password reset confirmation failed: ${error.message}`, { cause: error })
300
- }
47
+ _requiresInit (methodName) {
48
+ const noInitMethods = new Set([
49
+ 'users:login',
50
+ 'users:register',
51
+ 'users:request-password-reset',
52
+ 'users:reset-password',
53
+ 'users:reset-password-confirm',
54
+ 'users:register-confirmation',
55
+ 'users:google-auth',
56
+ 'users:github-auth'
57
+ ])
58
+ return !noInitMethods.has(methodName)
301
59
  }
302
60
 
303
- async confirmRegistration(token) {
304
- try {
305
- const response = await this._request('/auth/register-confirmation', {
306
- method: 'POST',
307
- body: JSON.stringify({ token }),
308
- methodName: 'confirmRegistration'
309
- })
310
- if (response.success) {
311
- return response.data
312
- }
313
- throw new Error(response.message)
314
- } catch (error) {
315
- throw new Error(`Registration confirmation failed: ${error.message}`, { cause: error })
61
+ _requireReady (methodName) {
62
+ if (this._requiresInit(methodName) && !this._initialized) {
63
+ throw new Error('Service not initialized')
316
64
  }
317
65
  }
318
66
 
319
- async requestPasswordChange() {
320
- this._requireReady('requestPasswordChange')
321
- try {
322
- const response = await this._request('/auth/request-password-change', {
323
- method: 'POST',
324
- methodName: 'requestPasswordChange'
325
- })
326
- if (response.success) {
327
- return response.data
328
- }
329
- throw new Error(response.message)
330
- } catch (error) {
331
- throw new Error(`Password change request failed: ${error.message}`, { cause: error })
67
+ _getBasedService (methodName) {
68
+ const based = this._context.services?.based
69
+ if (this._requiresInit(methodName) && !based) {
70
+ throw new Error('Based service not available')
332
71
  }
72
+ return based._client
333
73
  }
334
74
 
335
- async confirmPasswordChange(currentPassword, newPassword, code) {
336
- this._requireReady('confirmPasswordChange')
75
+ async login (identifier, password) {
337
76
  try {
338
- const response = await this._request('/auth/confirm-password-change', {
339
- method: 'POST',
340
- body: JSON.stringify({ currentPassword, newPassword, code }),
341
- methodName: 'confirmPasswordChange'
77
+ const based = this._getBasedService('login')
78
+ const response = await based.call('users:login', { identifier, password })
79
+
80
+ if (this._initialized) {
81
+ this.updateContext({ authToken: response.token })
82
+ }
83
+ based.setAuthState({
84
+ token: response.token,
85
+ userId: response.userId,
86
+ projectRoles: response.projectRoles,
87
+ globalRole: response.globalRole,
88
+ persistent: true
342
89
  })
343
- if (response.success) {
344
- return response.data
345
- }
346
- throw new Error(response.message)
347
- } catch (error) {
348
- throw new Error(`Password change confirmation failed: ${error.message}`, { cause: error })
349
- }
350
- }
351
-
352
- async getMe(options = {}) {
353
- this._requireReady('getMe')
354
- try {
355
- const session = this._resolvePluginSession(options.session)
356
- const endpoint = session
357
- ? `/auth/me?session=${encodeURIComponent(session)}`
358
- : '/auth/me'
359
90
 
360
- const response = await this._request(endpoint, {
361
- method: 'GET',
362
- methodName: 'getMe'
363
- })
364
- if (response.success) {
365
- return response.data
366
- }
367
- throw new Error(response.message)
91
+ return response
368
92
  } catch (error) {
369
- throw new Error(`Failed to get user profile: ${error.message}`, { cause: error })
370
- }
371
- }
372
-
373
- getAuthToken() {
374
- if (!this._tokenManager) {
375
- return null
93
+ throw new Error(`Login failed: ${error.message}`)
376
94
  }
377
- return this._tokenManager.getAccessToken()
378
95
  }
379
96
 
380
- /**
381
- * Get stored authentication state (backward compatibility method)
382
- * Replaces AuthService.getStoredAuthState()
383
- */
384
- async getStoredAuthState() {
97
+ async register (userData) {
385
98
  try {
386
- if (!this._tokenManager) {
387
- return {
388
- userId: false,
389
- authToken: false
390
- }
391
- }
392
-
393
- const tokenStatus = this._tokenManager.getTokenStatus()
394
-
395
- if (!tokenStatus.hasTokens) {
396
- return {
397
- userId: false,
398
- authToken: false
399
- }
400
- }
401
-
402
- // If tokens exist but are invalid, try to refresh
403
- if (!tokenStatus.isValid && tokenStatus.hasRefreshToken) {
404
- try {
405
- await this._tokenManager.ensureValidToken()
406
- } catch (error) {
407
- console.warn('[AuthService] Token refresh failed:', error.message)
408
- // Only clear tokens if it's definitely an auth error, not a network error
409
- if (
410
- error.message.includes('401') ||
411
- error.message.includes('403') ||
412
- error.message.includes('invalid') ||
413
- error.message.includes('expired')
414
- ) {
415
- this._tokenManager.clearTokens()
416
- return {
417
- userId: false,
418
- authToken: false,
419
- error: `Authentication failed: ${error.message}`
420
- }
421
- }
422
- // For network errors, keep tokens and return what we have
423
- return {
424
- userId: false,
425
- authToken: this._tokenManager.getAccessToken(),
426
- error: `Network error during token refresh: ${error.message}`,
427
- hasTokens: true
428
- }
429
- }
430
- }
431
-
432
- // Check if we have a valid token now
433
- const currentAccessToken = this._tokenManager.getAccessToken()
434
- if (!currentAccessToken) {
435
- return {
436
- userId: false,
437
- authToken: false
438
- }
439
- }
440
-
441
- // Get current user data if we have valid tokens
442
- // Be more lenient with API failures - don't immediately clear tokens
443
- try {
444
- const currentUser = await this.getMe()
445
-
446
- return {
447
- userId: currentUser.user.id,
448
- authToken: currentAccessToken,
449
- ...currentUser,
450
- error: null
451
- }
452
- } catch (error) {
453
- console.warn('[AuthService] Failed to get user data:', error.message)
454
-
455
- // Only clear tokens if it's an auth error (401, 403), not network errors
456
- if (error.message.includes('401') || error.message.includes('403')) {
457
- this._tokenManager.clearTokens()
458
- return {
459
- userId: false,
460
- authToken: false,
461
- error: `Authentication failed: ${error.message}`
462
- }
463
- }
464
-
465
- // For other errors (network, 500, etc.), keep tokens but return minimal state
466
- return {
467
- userId: false,
468
- authToken: currentAccessToken,
469
- error: `Failed to get user data: ${error.message}`,
470
- hasTokens: true
471
- }
472
- }
99
+ const based = this._getBasedService('register')
100
+ return await based.call('users:register', userData)
473
101
  } catch (error) {
474
- console.error(
475
- '[AuthService] Unexpected error in getStoredAuthState:',
476
- error
477
- )
478
- return {
479
- userId: false,
480
- authToken: false,
481
- error: `Failed to get stored auth state: ${error.message}`
482
- }
102
+ throw new Error(`Registration failed: ${error.message}`)
483
103
  }
484
104
  }
485
105
 
486
- // ==================== USER METHODS ====================
487
-
488
- async getUserProfile() {
489
- this._requireReady('getUserProfile')
106
+ async googleAuth (idToken) {
490
107
  try {
491
- const response = await this._request('/users/profile', {
492
- method: 'GET',
493
- methodName: 'getUserProfile'
494
- })
495
- if (response.success) {
496
- return response.data
497
- }
498
- throw new Error(response.message)
108
+ const based = this._getBasedService('googleAuth')
109
+ return await based.call('users:google-auth', { idToken })
499
110
  } catch (error) {
500
- throw new Error(`Failed to get user profile: ${error.message}`, { cause: error })
111
+ throw new Error(`Google auth failed: ${error.message}`)
501
112
  }
502
113
  }
503
114
 
504
- async updateUserProfile(profileData) {
505
- this._requireReady('updateUserProfile')
115
+ async githubAuth (code) {
506
116
  try {
507
- const response = await this._request('/users/profile', {
508
- method: 'PATCH',
509
- body: JSON.stringify(profileData),
510
- methodName: 'updateUserProfile'
511
- })
512
- if (response.success) {
513
- return response.data
514
- }
515
- throw new Error(response.message)
117
+ const based = this._getBasedService('githubAuth')
118
+ return await based.call('users:github-auth', { code })
516
119
  } catch (error) {
517
- throw new Error(`Failed to update user profile: ${error.message}`, { cause: error })
120
+ throw new Error(`GitHub auth failed: ${error.message}`)
518
121
  }
519
122
  }
520
123
 
521
- async getUserProjects() {
522
- this._requireReady('getUserProjects')
124
+ async logout () {
125
+ this._requireReady('logout')
523
126
  try {
524
- const response = await this._request('/users/projects', {
525
- method: 'GET',
526
- methodName: 'getUserProjects'
527
- })
528
- if (response.success) {
529
- return response.data.map((project) => ({
530
- ...project,
531
- ...(project.icon && {
532
- icon: {
533
- src: `${this._apiUrl}/core/files/public/${project.icon.id}/download`,
534
- ...project.icon
535
- }
536
- })
537
- }))
538
- }
539
- throw new Error(response.message)
127
+ const based = this._getBasedService('logout')
128
+ await based.call('logout')
129
+ this.updateContext({ authToken: null })
540
130
  } catch (error) {
541
- throw new Error(`Failed to get user projects: ${error.message}`, { cause: error })
131
+ throw new Error(`Logout failed: ${error.message}`)
542
132
  }
543
133
  }
544
134
 
545
- async getUser(userId) {
546
- this._requireReady('getUser')
135
+ async updateUserRole (userId, newRole) {
136
+ this._requireReady('updateUserRole')
547
137
  if (!userId) {
548
138
  throw new Error('User ID is required')
549
139
  }
550
- try {
551
- const response = await this._request(`/users/${userId}`, {
552
- method: 'GET',
553
- methodName: 'getUser'
554
- })
555
- if (response.success) {
556
- return response.data
557
- }
558
- throw new Error(response.message)
559
- } catch (error) {
560
- throw new Error(`Failed to get user: ${error.message}`, { cause: error })
561
- }
562
- }
563
-
564
- async getUserByEmail(email) {
565
- this._requireReady('getUserByEmail')
566
- if (!email) {
567
- throw new Error('Email is required')
140
+ if (!this._userRoles.has(newRole)) {
141
+ throw new Error(`Invalid role: ${newRole}`)
568
142
  }
569
143
  try {
570
- const response = await this._request(`/auth/user?email=${email}`, {
571
- method: 'GET',
572
- methodName: 'getUserByEmail'
573
- })
574
- if (response.success) {
575
- return response.data.user
576
- }
577
- throw new Error(response.message)
144
+ const based = this._getBasedService('updateUserRole')
145
+ return await based.call('users:update-role', { userId, role: newRole })
578
146
  } catch (error) {
579
- throw new Error(`Failed to get user by email: ${error.message}`, { cause: error })
147
+ throw new Error(`Failed to update user role: ${error.message}`)
580
148
  }
581
149
  }
582
150
 
583
- // ==================== PROJECT ROLE METHODS ====================
584
-
585
- /**
586
- * Get the current user's role for a specific project by project ID
587
- * Uses caching to avoid repeated API calls
588
- */
589
- async getMyProjectRole(projectId) {
590
- this._requireReady('getMyProjectRole')
151
+ async updateProjectTier (projectId, newTier) {
152
+ this._requireReady('updateProjectTier')
591
153
  if (!projectId) {
592
154
  throw new Error('Project ID is required')
593
155
  }
594
-
595
- // If there are no valid tokens, treat user as guest for public access
596
- if (!this.hasValidTokens()) {
597
- return 'guest'
156
+ if (!this._projectTiers.has(newTier)) {
157
+ throw new Error(`Invalid project tier: ${newTier}`)
598
158
  }
599
-
600
- // Check cache first
601
- const cacheKey = `role_${projectId}`
602
- const cached = this._projectRoleCache.get(cacheKey)
603
-
604
- if (cached && Date.now() - cached.timestamp < this._roleCacheExpiry) {
605
- return cached.role
606
- }
607
-
608
159
  try {
609
- const response = await this._request(`/projects/${projectId}/role`, {
610
- method: 'GET',
611
- methodName: 'getMyProjectRole'
160
+ const based = this._getBasedService('updateProjectTier')
161
+ return await based.call('projects:update-tier', {
162
+ projectId,
163
+ tier: newTier
612
164
  })
613
-
614
- if (response.success) {
615
- const { role } = response.data
616
- // Cache the result
617
- this._projectRoleCache.set(cacheKey, {
618
- role,
619
- timestamp: Date.now()
620
- })
621
- return role
622
- }
623
- throw new Error(response.message)
624
- } catch (error) {
625
- const message = error?.message || ''
626
- // If request failed due to missing/invalid auth, default to guest
627
- if (/401|403|unauthorized|no token|invalid token/iu.test(message)) {
628
- return 'guest'
629
- }
630
- throw new Error(`Failed to get project role: ${message}`, { cause: error })
631
- }
632
- }
633
-
634
- /**
635
- * Get the current user's role for a specific project by project key
636
- * Uses caching to avoid repeated API calls
637
- */
638
- async getMyProjectRoleByKey(projectKey) {
639
- this._requireReady('getMyProjectRoleByKey')
640
- if (!projectKey) {
641
- throw new Error('Project key is required')
642
- }
643
-
644
- // If there are no valid tokens, treat user as guest for public access
645
- if (!this.hasValidTokens()) {
646
- return 'guest'
647
- }
648
-
649
- // Check cache first
650
- const cacheKey = `role_key_${projectKey}`
651
- const cached = this._projectRoleCache.get(cacheKey)
652
-
653
- if (cached && Date.now() - cached.timestamp < this._roleCacheExpiry) {
654
- return cached.role
655
- }
656
-
657
- try {
658
- const response = await this._request(`/projects/key/${projectKey}/role`, {
659
- method: 'GET',
660
- methodName: 'getMyProjectRoleByKey'
661
- })
662
-
663
- if (response.success) {
664
- const { role } = response.data
665
- // Cache the result
666
- this._projectRoleCache.set(cacheKey, {
667
- role,
668
- timestamp: Date.now()
669
- })
670
- return role
671
- }
672
- throw new Error(response.message)
673
165
  } catch (error) {
674
- const message = error?.message || ''
675
- // If request failed due to missing/invalid auth, default to guest
676
- if (/401|403|unauthorized|no token|invalid token/iu.test(message)) {
677
- return 'guest'
678
- }
679
- throw new Error(`Failed to get project role by key: ${message}`, { cause: error })
680
- }
681
- }
682
-
683
- /**
684
- * Clear the project role cache for a specific project or all projects
685
- */
686
- clearProjectRoleCache(projectId = null) {
687
- if (projectId) {
688
- // Clear specific project cache
689
- this._projectRoleCache.delete(`role_${projectId}`)
690
- // Also clear by key if we have it cached
691
- for (const [key] of this._projectRoleCache) {
692
- if (key.startsWith('role_key_')) {
693
- this._projectRoleCache.delete(key)
694
- }
695
- }
696
- } else {
697
- // Clear all cache
698
- this._projectRoleCache.clear()
166
+ throw new Error(`Failed to update project tier: ${error.message}`)
699
167
  }
700
168
  }
701
169
 
702
- /**
703
- * Get project role with fallback to user projects list
704
- * This method tries to get the role from user projects first,
705
- * then falls back to API call if not found
706
- */
707
- async getProjectRoleWithFallback(projectId, userProjects = null) {
708
- this._requireReady('getProjectRoleWithFallback')
709
- if (!projectId) {
710
- throw new Error('Project ID is required')
711
- }
712
-
713
- // First try to find in user projects if provided
714
- if (userProjects && Array.isArray(userProjects)) {
715
- const userProject = userProjects.find(p => p.id === projectId)
716
- if (userProject && userProject.role) {
717
- return userProject.role
718
- }
719
- }
720
-
721
- // Fallback to API call
722
- return await this.getMyProjectRole(projectId)
723
- }
724
-
725
- /**
726
- * Get project role with fallback to user projects list (by project key)
727
- * This method tries to get the role from user projects first,
728
- * then falls back to API call if not found
729
- */
730
- async getProjectRoleByKeyWithFallback(projectKey, userProjects = null) {
731
- this._requireReady('getProjectRoleByKeyWithFallback')
732
- if (!projectKey) {
733
- throw new Error('Project key is required')
734
- }
735
-
736
- // First try to find in user projects if provided
737
- if (userProjects && Array.isArray(userProjects)) {
738
- const userProject = userProjects.find(p => p.key === projectKey)
739
- if (userProject && userProject.role) {
740
- return userProject.role
741
- }
742
- }
743
-
744
- // Fallback to API call
745
- return await this.getMyProjectRoleByKey(projectKey)
746
- }
747
-
748
- // ==================== AUTH HELPER METHODS ====================
749
-
750
- /**
751
- * Debug method to check token status
752
- */
753
- getTokenDebugInfo() {
754
- if (!this._tokenManager) {
755
- return {
756
- tokenManagerExists: false,
757
- error: 'TokenManager not initialized'
758
- }
759
- }
760
-
761
- const tokenStatus = this._tokenManager.getTokenStatus()
762
- const { tokens } = this._tokenManager
763
-
764
- return {
765
- tokenManagerExists: true,
766
- tokenStatus,
767
- hasAccessToken: Boolean(tokens.accessToken),
768
- hasRefreshToken: Boolean(tokens.refreshToken),
769
- accessTokenPreview: tokens.accessToken
770
- ? `${tokens.accessToken.substring(0, 20)}...`
771
- : null,
772
- expiresAt: tokens.expiresAt,
773
- timeToExpiry: tokenStatus.timeToExpiry,
774
- authHeader: this._tokenManager.getAuthHeader()
775
- }
776
- }
777
-
778
- /**
779
- * Helper method to check if user is authenticated
780
- */
781
- isAuthenticated() {
782
- if (!this._tokenManager) {
783
- return false
784
- }
785
- return this._tokenManager.hasTokens()
786
- }
787
-
788
- /**
789
- * Helper method to check if user has valid tokens
790
- */
791
- hasValidTokens() {
792
- if (!this._tokenManager) {
793
- return false
794
- }
795
- return (
796
- this._tokenManager.hasTokens() && this._tokenManager.isAccessTokenValid()
797
- )
798
- }
799
-
800
- /**
801
- * Helper method to get current user info
802
- */
803
- async getCurrentUser() {
804
- try {
805
- return await this.getMe()
806
- } catch (error) {
807
- throw new Error(`Failed to get current user: ${error.message}`, { cause: error })
808
- }
809
- }
810
-
811
- /**
812
- * Helper method to validate user data for registration
813
- */
814
- validateRegistrationData(userData) {
815
- const errors = []
816
-
817
- if (!userData.email || typeof userData.email !== 'string') {
818
- errors.push('Email is required and must be a string')
819
- } else if (!this._isValidEmail(userData.email)) {
820
- errors.push('Email must be a valid email address')
821
- }
822
-
823
- if (!userData.password || typeof userData.password !== 'string') {
824
- errors.push('Password is required and must be a string')
825
- } else if (userData.password.length < 8) {
826
- errors.push('Password must be at least 8 characters long')
827
- }
828
-
829
- if (userData.username && typeof userData.username !== 'string') {
830
- errors.push('Username must be a string')
831
- } else if (userData.username && userData.username.length < 3) {
832
- errors.push('Username must be at least 3 characters long')
833
- }
834
-
835
- return {
836
- isValid: errors.length === 0,
837
- errors
838
- }
839
- }
840
-
841
- /**
842
- * Helper method to register with validation
843
- */
844
- async registerWithValidation(userData, options = {}) {
845
- const validation = this.validateRegistrationData(userData)
846
- if (!validation.isValid) {
847
- throw new Error(`Validation failed: ${validation.errors.join(', ')}`)
848
- }
849
-
850
- return await this.register(userData, options)
851
- }
852
-
853
- /**
854
- * Helper method to login with validation
855
- */
856
- async loginWithValidation(email, password, options = {}) {
857
- if (!email || typeof email !== 'string') {
858
- throw new Error('Email is required and must be a string')
859
- }
860
-
861
- if (!password || typeof password !== 'string') {
862
- throw new Error('Password is required and must be a string')
863
- }
864
-
865
- if (!this._isValidEmail(email)) {
866
- throw new Error('Email must be a valid email address')
867
- }
868
-
869
- return await this.login(email, password, options)
870
- }
871
-
872
- /**
873
- * Private helper to validate email format
874
- */
875
- _isValidEmail(email) {
876
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u
877
- return emailRegex.test(email)
878
- }
879
-
880
- // ==================== PERMISSION METHODS (Existing) ====================
881
-
882
- hasPermission(requiredPermission) {
170
+ hasPermission (requiredPermission) {
883
171
  const authState = this._context?.state
884
172
  if (!authState) {
885
173
  return false
@@ -895,21 +183,21 @@ export class AuthService extends BaseService {
895
183
  )
896
184
  }
897
185
 
898
- hasGlobalPermission(globalRole, requiredPermission) {
186
+ hasGlobalPermission (globalRole, requiredPermission) {
899
187
  return ROLE_PERMISSIONS[globalRole]?.includes(requiredPermission) || false
900
188
  }
901
189
 
902
- checkProjectPermission(projectRole, requiredPermission) {
190
+ checkProjectPermission (projectRole, requiredPermission) {
903
191
  return (
904
192
  PROJECT_ROLE_PERMISSIONS[projectRole]?.includes(requiredPermission) ||
905
193
  false
906
194
  )
907
195
  }
908
196
 
909
- checkProjectFeature(projectTier, feature) {
197
+ checkProjectFeature (projectTier, feature) {
910
198
  if (feature.startsWith('aiCopilot') || feature.startsWith('aiChatbot')) {
911
199
  const [featureBase] = feature.split(':')
912
- const tierFeature = TIER_FEATURES[projectTier]?.find((f) =>
200
+ const tierFeature = TIER_FEATURES[projectTier]?.find(f =>
913
201
  f.startsWith(featureBase)
914
202
  )
915
203
  if (!tierFeature) {
@@ -923,7 +211,7 @@ export class AuthService extends BaseService {
923
211
  }
924
212
 
925
213
  // Operation checking
926
- async canPerformOperation(projectId, operation, options = {}) {
214
+ async canPerformOperation (projectId, operation, options = {}) {
927
215
  this._requireReady()
928
216
  if (!projectId) {
929
217
  throw new Error('Project ID is required')
@@ -935,14 +223,17 @@ export class AuthService extends BaseService {
935
223
  if (!operationConfig) {
936
224
  return false
937
225
  }
226
+ if (!operationConfig) {
227
+ return false
228
+ }
938
229
 
939
230
  const { permissions = [], features = [] } = operationConfig
940
231
 
941
232
  try {
942
233
  // Check permissions
943
234
  const permissionResults = await Promise.all(
944
- permissions.map((permission) =>
945
- this.checkProjectPermission(projectId, permission)
235
+ permissions.map(permission =>
236
+ this.hasProjectPermission(projectId, permission)
946
237
  )
947
238
  )
948
239
 
@@ -953,10 +244,13 @@ export class AuthService extends BaseService {
953
244
  if (!hasPermissions) {
954
245
  return false
955
246
  }
247
+ if (!hasPermissions) {
248
+ return false
249
+ }
956
250
 
957
251
  // Check features if required
958
252
  if (checkFeatures && features.length > 0) {
959
- const featureResults = features.map((feature) => {
253
+ const featureResults = features.map(feature => {
960
254
  const result = this.hasProjectFeature(projectId, feature)
961
255
  return feature.includes(':')
962
256
  ? typeof result === 'number' && result > 0
@@ -970,6 +264,9 @@ export class AuthService extends BaseService {
970
264
  if (!hasFeatures) {
971
265
  return false
972
266
  }
267
+ if (!hasFeatures) {
268
+ return false
269
+ }
973
270
  }
974
271
 
975
272
  return true
@@ -980,7 +277,7 @@ export class AuthService extends BaseService {
980
277
  }
981
278
 
982
279
  // Higher-level permission methods
983
- async withPermission(projectId, operation, action) {
280
+ async withPermission (projectId, operation, action) {
984
281
  this._requireReady()
985
282
  if (!projectId) {
986
283
  throw new Error('Project ID is required')
@@ -996,115 +293,223 @@ export class AuthService extends BaseService {
996
293
  return action()
997
294
  }
998
295
 
999
- // Cleanup
1000
- destroy() {
1001
- if (this._tokenManager) {
1002
- this._tokenManager.destroy()
1003
- this._tokenManager = null
296
+ // Project access information
297
+ async getProjectAccess (projectId) {
298
+ this._requireReady()
299
+ if (!projectId) {
300
+ throw new Error('Project ID is required')
1004
301
  }
1005
- // Clear project role cache
1006
- this._projectRoleCache.clear()
1007
- this._setReady(false)
1008
- }
1009
302
 
1010
- _preparePluginPayload(payload, sessionOverride = null) {
1011
- const target =
1012
- payload && typeof payload === 'object'
1013
- ? { ...payload }
1014
- : {}
303
+ const operations = Object.keys(PERMISSION_MAP)
1015
304
 
1016
- const session = this._resolvePluginSession(sessionOverride)
305
+ const access = await Promise.all(
306
+ operations.map(async operation => {
307
+ const allowed = await this.canPerformOperation(projectId, operation)
308
+ const config = PERMISSION_MAP[operation]
1017
309
 
1018
- if (session && !Object.hasOwn(target, 'session')) {
1019
- target.session = session
1020
- return { payload: target, session }
1021
- }
310
+ return {
311
+ operation,
312
+ allowed,
313
+ permissions: config.permissions,
314
+ features: config.features,
315
+ aiTokens: operation.startsWith('ai')
316
+ ? this._getAITokens(projectId, operation.replace('ai', ''))
317
+ : null
318
+ }
319
+ })
320
+ )
1022
321
 
1023
- return { payload: target, session: null }
322
+ return {
323
+ projectId,
324
+ permissions: access.reduce(
325
+ (acc, { operation, ...details }) => ({
326
+ ...acc,
327
+ [operation]: details
328
+ }),
329
+ {}
330
+ ),
331
+ timestamp: new Date().toISOString()
332
+ }
333
+ }
334
+
335
+ // AI token management
336
+ _getAITokens (projectId, featureType) {
337
+ const tokenFeatures = [
338
+ `ai${featureType}:3`,
339
+ `ai${featureType}:5`,
340
+ `ai${featureType}:15`
341
+ ]
342
+
343
+ return tokenFeatures.reduce((total, feature) => {
344
+ const tokens = this.hasProjectFeature(projectId, feature)
345
+ return total + (typeof tokens === 'number' ? tokens : 0)
346
+ }, 0)
347
+ }
348
+
349
+ async getProjectMembers (projectId) {
350
+ this._requireReady('getProjectMembers')
351
+ if (!projectId) {
352
+ throw new Error('Project ID is required')
353
+ }
354
+ try {
355
+ const based = this._getBasedService('getProjectMembers')
356
+ return await based.call('projects:get-members', { projectId })
357
+ } catch (error) {
358
+ throw new Error(`Failed to get project members: ${error.message}`)
359
+ }
1024
360
  }
1025
361
 
1026
- _resolvePluginSession(sessionOverride = null) {
1027
- if (sessionOverride) {
1028
- return this._cachePluginSession(sessionOverride)
362
+ async inviteMember (projectId, email, role, name, callbackUrl) {
363
+ this._requireReady('inviteMember')
364
+ if (!projectId) {
365
+ throw new Error('Project ID is required')
1029
366
  }
1030
-
1031
- if (this._pluginSession) {
1032
- return this._pluginSession
367
+ if (!email) {
368
+ throw new Error('Email is required')
1033
369
  }
1034
-
1035
- const optionSession = this._options?.pluginSession
1036
- if (optionSession) {
1037
- return this._cachePluginSession(optionSession)
370
+ if (!callbackUrl || Object.keys(callbackUrl).length === 0) {
371
+ throw new Error('Callback Url is required')
1038
372
  }
1039
-
1040
- const contextSession = this._context?.pluginSession
1041
- if (contextSession) {
1042
- return this._cachePluginSession(contextSession)
373
+ if (!role || !this._userRoles.has(role)) {
374
+ throw new Error(`Invalid role: ${role}`)
1043
375
  }
1044
-
1045
- if (typeof window !== 'undefined') {
1046
- try {
1047
- const sessionFromUrl = new URL(window.location.href).searchParams.get('session')
1048
- if (sessionFromUrl) {
1049
- return this._cachePluginSession(sessionFromUrl)
1050
- }
1051
- } catch {
1052
- // Ignore URL parsing errors
1053
- }
1054
-
1055
- try {
1056
- if (window.localStorage) {
1057
- const stored = window.localStorage.getItem(PLUGIN_SESSION_STORAGE_KEY)
1058
- if (stored) {
1059
- this._pluginSession = stored
1060
- return stored
1061
- }
1062
- }
1063
- } catch {
1064
- // Ignore storage access issues
1065
- }
376
+ try {
377
+ const based = this._getBasedService('inviteMember')
378
+ return await based.call('projects:invite-member', {
379
+ projectId,
380
+ email,
381
+ role,
382
+ name,
383
+ callbackUrl
384
+ })
385
+ } catch (error) {
386
+ throw new Error(`Failed to invite member: ${error.message}`)
1066
387
  }
1067
-
1068
- return null
1069
388
  }
1070
389
 
1071
- _cachePluginSession(session) {
1072
- if (!session) {
1073
- return null
390
+ async acceptInvite (token) {
391
+ this._requireReady('acceptInvite')
392
+ try {
393
+ const based = this._getBasedService('acceptInvite')
394
+ return await based.call('projects:accept-invite', { token })
395
+ } catch (error) {
396
+ throw new Error(`Failed to accept invite: ${error.message}`)
1074
397
  }
398
+ }
1075
399
 
1076
- this._pluginSession = session
400
+ async updateMemberRole (projectId, userId, role) {
401
+ this._requireReady('updateMemberRole')
402
+ if (!projectId) {
403
+ throw new Error('Project ID is required')
404
+ }
405
+ if (!userId) {
406
+ throw new Error('User ID is required')
407
+ }
408
+ if (!this._userRoles.has(role)) {
409
+ throw new Error(`Invalid role: ${role}`)
410
+ }
411
+ try {
412
+ const based = this._getBasedService('updateMemberRole')
413
+ return await based.call('projects:update-member-role', {
414
+ projectId,
415
+ userId,
416
+ role
417
+ })
418
+ } catch (error) {
419
+ throw new Error(`Failed to update member role: ${error.message}`)
420
+ }
421
+ }
1077
422
 
1078
- if (typeof window !== 'undefined') {
1079
- try {
1080
- if (window.localStorage) {
1081
- window.localStorage.setItem(PLUGIN_SESSION_STORAGE_KEY, session)
1082
- }
1083
- } catch {
1084
- // Ignore storage access issues
1085
- }
423
+ async removeMember (projectId, userId) {
424
+ this._requireReady('removeMember')
425
+ if (!projectId || !userId) {
426
+ throw new Error('Project ID and user ID are required')
1086
427
  }
428
+ try {
429
+ const based = this._getBasedService('removeMember')
430
+ return await based.call('projects:remove-member', { projectId, userId })
431
+ } catch (error) {
432
+ throw new Error(`Failed to remove member: ${error.message}`)
433
+ }
434
+ }
1087
435
 
1088
- return session
436
+ async confirmRegistration (token) {
437
+ try {
438
+ const based = this._getBasedService('confirmRegistration')
439
+ return await based.call('users:register-confirmation', { token })
440
+ } catch (error) {
441
+ throw new Error(`Registration confirmation failed: ${error.message}`)
442
+ }
1089
443
  }
1090
444
 
1091
- setPluginSession(session) {
1092
- this._cachePluginSession(session)
445
+ async requestPasswordReset (email, callbackUrl) {
446
+ try {
447
+ const based = this._getBasedService('requestPasswordReset')
448
+ return await based.call('users:reset-password', { email, callbackUrl })
449
+ } catch (error) {
450
+ throw new Error(`Password reset request failed: ${error.message}`)
451
+ }
1093
452
  }
1094
453
 
1095
- _clearPluginSession(session = null) {
1096
- if (!session || this._pluginSession === session) {
1097
- this._pluginSession = null
454
+ async confirmPasswordReset (token, newPassword) {
455
+ try {
456
+ const based = this._getBasedService('confirmPasswordReset')
457
+ return await based.call('users:reset-password-confirm', {
458
+ token,
459
+ newPassword
460
+ })
461
+ } catch (error) {
462
+ throw new Error(`Password reset confirmation failed: ${error.message}`)
1098
463
  }
464
+ }
465
+
466
+ async getStoredAuthState () {
467
+ try {
468
+ const based = this._getBasedService('getStoredAuthState')
469
+ const { authState } = based
1099
470
 
1100
- if (typeof window !== 'undefined') {
1101
- try {
1102
- if (window.localStorage) {
1103
- window.localStorage.removeItem(PLUGIN_SESSION_STORAGE_KEY)
471
+ if (authState?.token) {
472
+ return {
473
+ userId: authState.userId,
474
+ authToken: authState.token,
475
+ projectRoles: authState.projectRoles,
476
+ globalRole: authState.globalRole,
477
+ error: null
1104
478
  }
1105
- } catch {
1106
- // Ignore storage access issues
479
+ }
480
+
481
+ return {
482
+ userId: false,
483
+ authToken: false
484
+ }
485
+ } catch (error) {
486
+ this._setError(error)
487
+ return {
488
+ userId: false,
489
+ authToken: false,
490
+ error: `Failed to get stored auth state: ${error.message}`
1107
491
  }
1108
492
  }
1109
493
  }
494
+
495
+ async subscribeToAuthChanges (callback) {
496
+ const based = this._getBasedService('subscribeToAuthChanges')
497
+ based.on('authstate-change', async authState => {
498
+ const formattedState = authState?.token
499
+ ? {
500
+ userId: authState.userId,
501
+ authToken: authState.token,
502
+ projectRoles: authState.projectRoles,
503
+ globalRole: authState.globalRole,
504
+ error: null
505
+ }
506
+ : {
507
+ userId: false,
508
+ authToken: false
509
+ }
510
+ await callback(formattedState)
511
+ })
512
+
513
+ return () => based.off('authstate-change')
514
+ }
1110
515
  }