@symbo.ls/sdk 3.1.1 → 3.1.2

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 (55) hide show
  1. package/README.md +172 -11
  2. package/dist/cjs/config/environment.js +39 -33
  3. package/dist/cjs/index.js +35 -8
  4. package/dist/cjs/services/AuthService.js +44 -3
  5. package/dist/cjs/services/BasedService.js +530 -24
  6. package/dist/cjs/services/CoreService.js +1751 -0
  7. package/dist/cjs/services/SocketIOService.js +34 -36
  8. package/dist/cjs/services/SymstoryService.js +135 -49
  9. package/dist/cjs/services/index.js +4 -4
  10. package/dist/cjs/utils/TokenManager.js +374 -0
  11. package/dist/cjs/utils/basedQuerys.js +120 -0
  12. package/dist/cjs/utils/permission.js +4 -4
  13. package/dist/cjs/utils/services.js +32 -9
  14. package/dist/cjs/utils/symstoryClient.js +32 -1
  15. package/dist/esm/config/environment.js +39 -33
  16. package/dist/esm/index.js +8964 -11076
  17. package/dist/esm/services/AuthService.js +48 -7
  18. package/dist/esm/services/BasedService.js +683 -56
  19. package/dist/esm/services/CoreService.js +2264 -0
  20. package/dist/esm/services/SocketIOService.js +71 -68
  21. package/dist/esm/services/SymstoryService.js +293 -101
  22. package/dist/esm/services/index.js +8905 -11066
  23. package/dist/esm/utils/TokenManager.js +360 -0
  24. package/dist/esm/utils/basedQuerys.js +120 -0
  25. package/dist/esm/utils/permission.js +4 -4
  26. package/dist/esm/utils/services.js +32 -9
  27. package/dist/esm/utils/symstoryClient.js +69 -33
  28. package/dist/esm/utils/validation.js +89 -19
  29. package/dist/node/config/environment.js +39 -33
  30. package/dist/node/index.js +43 -10
  31. package/dist/node/services/AuthService.js +44 -3
  32. package/dist/node/services/BasedService.js +531 -25
  33. package/dist/node/services/CoreService.js +1722 -0
  34. package/dist/node/services/SocketIOService.js +34 -36
  35. package/dist/node/services/SymstoryService.js +135 -49
  36. package/dist/node/services/index.js +4 -4
  37. package/dist/node/utils/TokenManager.js +355 -0
  38. package/dist/node/utils/basedQuerys.js +120 -0
  39. package/dist/node/utils/permission.js +4 -4
  40. package/dist/node/utils/services.js +32 -9
  41. package/dist/node/utils/symstoryClient.js +32 -1
  42. package/package.json +16 -13
  43. package/src/config/environment.js +40 -35
  44. package/src/index.js +49 -10
  45. package/src/services/AuthService.js +52 -3
  46. package/src/services/BasedService.js +602 -23
  47. package/src/services/CoreService.js +1943 -0
  48. package/src/services/SocketIOService.js +49 -71
  49. package/src/services/SymstoryService.js +150 -64
  50. package/src/services/index.js +4 -4
  51. package/src/utils/TokenManager.js +424 -0
  52. package/src/utils/basedQuerys.js +123 -0
  53. package/src/utils/permission.js +4 -4
  54. package/src/utils/services.js +32 -9
  55. package/src/utils/symstoryClient.js +35 -1
@@ -0,0 +1,424 @@
1
+ /**
2
+ * TokenManager - Handles access and refresh token management
3
+ * Provides persistence, automatic refresh, and token lifecycle management
4
+ */
5
+ export class TokenManager {
6
+ constructor (options = {}) {
7
+ this.config = {
8
+ storagePrefix: 'symbols_',
9
+ storageType: 'localStorage', // 'localStorage' | 'sessionStorage' | 'memory'
10
+ refreshBuffer: 60 * 1000, // Refresh 1 minute before expiry
11
+ maxRetries: 3,
12
+ apiUrl: options.apiUrl || '/api',
13
+ onTokenRefresh: options.onTokenRefresh || null,
14
+ onTokenExpired: options.onTokenExpired || null,
15
+ onTokenError: options.onTokenError || null,
16
+ ...options
17
+ }
18
+
19
+ this.tokens = {
20
+ accessToken: null,
21
+ refreshToken: null,
22
+ expiresAt: null,
23
+ expiresIn: null
24
+ }
25
+
26
+ this.refreshPromise = null
27
+ this.refreshTimeout = null
28
+ this.retryCount = 0
29
+
30
+ // Load tokens from storage on initialization
31
+ this.loadTokens()
32
+ }
33
+
34
+ /**
35
+ * Storage keys
36
+ */
37
+ get storageKeys () {
38
+ return {
39
+ accessToken: `${this.config.storagePrefix}access_token`,
40
+ refreshToken: `${this.config.storagePrefix}refresh_token`,
41
+ expiresAt: `${this.config.storagePrefix}expires_at`,
42
+ expiresIn: `${this.config.storagePrefix}expires_in`
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Get storage instance based on configuration
48
+ */
49
+ get storage () {
50
+ if (typeof window === 'undefined') {
51
+ // Node.js environment - use memory storage
52
+ return this._memoryStorage
53
+ }
54
+
55
+ switch (this.config.storageType) {
56
+ case 'sessionStorage':
57
+ return window.sessionStorage
58
+ case 'memory':
59
+ return this._memoryStorage
60
+ default:
61
+ return window.localStorage
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Memory storage fallback for server-side rendering
67
+ */
68
+ _memoryStorage = {
69
+ _data: {},
70
+ getItem: (key) => this._memoryStorage._data[key] || null,
71
+ setItem: (key, value) => { this._memoryStorage._data[key] = value },
72
+ removeItem: (key) => { delete this._memoryStorage._data[key] },
73
+ clear: () => { this._memoryStorage._data = {} }
74
+ }
75
+
76
+ /**
77
+ * Set tokens and persist to storage
78
+ */
79
+ setTokens (tokenData) {
80
+ const {
81
+ access_token: accessToken,
82
+ refresh_token: refreshToken,
83
+ expires_in: expiresIn,
84
+ token_type: tokenType = 'Bearer'
85
+ } = tokenData
86
+
87
+ if (!accessToken) {
88
+ throw new Error('Access token is required')
89
+ }
90
+
91
+ // Calculate expiry time
92
+ const now = Date.now()
93
+ const expiresAt = expiresIn ? now + (expiresIn * 1000) : null
94
+
95
+ // Update internal state
96
+ this.tokens = {
97
+ accessToken,
98
+ refreshToken: refreshToken || this.tokens.refreshToken,
99
+ expiresAt,
100
+ expiresIn,
101
+ tokenType
102
+ }
103
+
104
+ // Persist to storage
105
+ this.saveTokens()
106
+
107
+ // Schedule automatic refresh
108
+ this.scheduleRefresh()
109
+
110
+ // Trigger callback
111
+ if (this.config.onTokenRefresh) {
112
+ this.config.onTokenRefresh(this.tokens)
113
+ }
114
+
115
+ return this.tokens
116
+ }
117
+
118
+ /**
119
+ * Get current access token
120
+ */
121
+ getAccessToken () {
122
+ return this.tokens.accessToken
123
+ }
124
+
125
+ /**
126
+ * Get current refresh token
127
+ */
128
+ getRefreshToken () {
129
+ return this.tokens.refreshToken
130
+ }
131
+
132
+ /**
133
+ * Get authorization header value
134
+ */
135
+ getAuthHeader () {
136
+ const token = this.getAccessToken()
137
+ if (!token) {return null}
138
+
139
+ return `${this.tokens.tokenType || 'Bearer'} ${token}`
140
+ }
141
+
142
+ /**
143
+ * Check if access token is valid and not expired
144
+ */
145
+ isAccessTokenValid () {
146
+ if (!this.tokens.accessToken) {return false}
147
+ if (!this.tokens.expiresAt) {return true} // No expiry info, assume valid
148
+
149
+ const now = Date.now()
150
+ return now < (this.tokens.expiresAt - this.config.refreshBuffer)
151
+ }
152
+
153
+ /**
154
+ * Check if tokens exist (regardless of expiry)
155
+ */
156
+ hasTokens () {
157
+ return Boolean(this.tokens.accessToken)
158
+ }
159
+
160
+ /**
161
+ * Check if refresh token exists
162
+ */
163
+ hasRefreshToken () {
164
+ return Boolean(this.tokens.refreshToken)
165
+ }
166
+
167
+ /**
168
+ * Automatically refresh tokens if needed
169
+ */
170
+ async ensureValidToken () {
171
+ // If no tokens, return null
172
+ if (!this.hasTokens()) {
173
+ return null
174
+ }
175
+
176
+ // If token is still valid, return it
177
+ if (this.isAccessTokenValid()) {
178
+ return this.getAccessToken()
179
+ }
180
+
181
+ // If no refresh token, clear tokens and return null
182
+ if (!this.hasRefreshToken()) {
183
+ this.clearTokens()
184
+ if (this.config.onTokenExpired) {
185
+ this.config.onTokenExpired()
186
+ }
187
+ return null
188
+ }
189
+
190
+ // Attempt to refresh token
191
+ try {
192
+ await this.refreshTokens()
193
+ return this.getAccessToken()
194
+ } catch (error) {
195
+ this.clearTokens()
196
+ if (this.config.onTokenError) {
197
+ this.config.onTokenError(error)
198
+ }
199
+ throw error
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Refresh access token using refresh token
205
+ */
206
+ async refreshTokens () {
207
+ // Prevent multiple simultaneous refresh requests
208
+ if (this.refreshPromise) {
209
+ return this.refreshPromise
210
+ }
211
+
212
+ if (!this.hasRefreshToken()) {
213
+ throw new Error('No refresh token available')
214
+ }
215
+
216
+ if (this.retryCount >= this.config.maxRetries) {
217
+ throw new Error('Max refresh retries exceeded')
218
+ }
219
+
220
+ this.refreshPromise = this._performRefresh()
221
+
222
+ try {
223
+ const result = await this.refreshPromise
224
+ this.retryCount = 0 // Reset retry count on success
225
+ return result
226
+ } catch (error) {
227
+ this.retryCount++
228
+ throw error
229
+ } finally {
230
+ this.refreshPromise = null
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Perform the actual token refresh request
236
+ */
237
+ async _performRefresh () {
238
+ const refreshToken = this.getRefreshToken()
239
+
240
+ const response = await fetch(`${this.config.apiUrl}/core/auth/refresh`, {
241
+ method: 'POST',
242
+ headers: {
243
+ 'Content-Type': 'application/json'
244
+ },
245
+ body: JSON.stringify({ refreshToken })
246
+ })
247
+
248
+ if (!response.ok) {
249
+ const errorData = await response.json().catch(() => ({}))
250
+ throw new Error(errorData.message || `Token refresh failed: ${response.status}`)
251
+ }
252
+
253
+ const responseData = await response.json()
254
+
255
+ // Handle new response format: responseData.data.tokens
256
+ if (responseData.success && responseData.data && responseData.data.tokens) {
257
+ const { tokens } = responseData.data
258
+ const tokenData = {
259
+ access_token: tokens.accessToken,
260
+ refresh_token: tokens.refreshToken,
261
+ expires_in: tokens.accessTokenExp?.expiresIn,
262
+ token_type: 'Bearer'
263
+ }
264
+ return this.setTokens(tokenData)
265
+ }
266
+ // Fallback to old format for backward compatibility
267
+ return this.setTokens(responseData)
268
+
269
+ }
270
+
271
+ /**
272
+ * Schedule automatic token refresh
273
+ */
274
+ scheduleRefresh () {
275
+ // Clear existing timeout
276
+ if (this.refreshTimeout) {
277
+ clearTimeout(this.refreshTimeout)
278
+ this.refreshTimeout = null
279
+ }
280
+
281
+ // Don't schedule if no expiry info or no refresh token
282
+ if (!this.tokens.expiresAt || !this.hasRefreshToken()) {
283
+ return
284
+ }
285
+
286
+ const now = Date.now()
287
+ const refreshTime = this.tokens.expiresAt - this.config.refreshBuffer
288
+ const delay = Math.max(0, refreshTime - now)
289
+
290
+ this.refreshTimeout = setTimeout(async () => {
291
+ try {
292
+ await this.refreshTokens()
293
+ } catch (error) {
294
+ console.error('Automatic token refresh failed:', error)
295
+ if (this.config.onTokenError) {
296
+ this.config.onTokenError(error)
297
+ }
298
+ }
299
+ }, delay)
300
+ }
301
+
302
+ /**
303
+ * Save tokens to storage
304
+ */
305
+ saveTokens () {
306
+ const {storage} = this
307
+ const keys = this.storageKeys
308
+
309
+ if (this.tokens.accessToken) {
310
+ storage.setItem(keys.accessToken, this.tokens.accessToken)
311
+ }
312
+
313
+ if (this.tokens.refreshToken) {
314
+ storage.setItem(keys.refreshToken, this.tokens.refreshToken)
315
+ }
316
+
317
+ if (this.tokens.expiresAt) {
318
+ storage.setItem(keys.expiresAt, this.tokens.expiresAt.toString())
319
+ }
320
+
321
+ if (this.tokens.expiresIn) {
322
+ storage.setItem(keys.expiresIn, this.tokens.expiresIn.toString())
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Load tokens from storage
328
+ */
329
+ loadTokens () {
330
+ const {storage} = this
331
+ const keys = this.storageKeys
332
+
333
+ const accessToken = storage.getItem(keys.accessToken)
334
+ const refreshToken = storage.getItem(keys.refreshToken)
335
+ const expiresAt = storage.getItem(keys.expiresAt)
336
+ const expiresIn = storage.getItem(keys.expiresIn)
337
+
338
+ if (accessToken) {
339
+ this.tokens = {
340
+ accessToken,
341
+ refreshToken,
342
+ expiresAt: expiresAt ? parseInt(expiresAt, 10) : null,
343
+ expiresIn: expiresIn ? parseInt(expiresIn, 10) : null,
344
+ tokenType: 'Bearer'
345
+ }
346
+
347
+ // Schedule refresh for loaded tokens
348
+ this.scheduleRefresh()
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Clear all tokens
354
+ */
355
+ clearTokens () {
356
+ // Clear memory
357
+ this.tokens = {
358
+ accessToken: null,
359
+ refreshToken: null,
360
+ expiresAt: null,
361
+ expiresIn: null
362
+ }
363
+
364
+ // Clear storage
365
+ const {storage} = this
366
+ const keys = this.storageKeys
367
+
368
+ Object.values(keys).forEach(key => {
369
+ storage.removeItem(key)
370
+ })
371
+
372
+ // Clear scheduled refresh
373
+ if (this.refreshTimeout) {
374
+ clearTimeout(this.refreshTimeout)
375
+ this.refreshTimeout = null
376
+ }
377
+
378
+ // Reset retry count
379
+ this.retryCount = 0
380
+ }
381
+
382
+ /**
383
+ * Get token status information
384
+ */
385
+ getTokenStatus () {
386
+ const hasTokens = this.hasTokens()
387
+ const isValid = this.isAccessTokenValid()
388
+ const {expiresAt} = this.tokens
389
+ const timeToExpiry = expiresAt ? expiresAt - Date.now() : null
390
+
391
+ return {
392
+ hasTokens,
393
+ isValid,
394
+ hasRefreshToken: this.hasRefreshToken(),
395
+ expiresAt,
396
+ timeToExpiry,
397
+ willExpireSoon: timeToExpiry ? timeToExpiry < this.config.refreshBuffer : false
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Cleanup resources
403
+ */
404
+ destroy () {
405
+ if (this.refreshTimeout) {
406
+ clearTimeout(this.refreshTimeout)
407
+ this.refreshTimeout = null
408
+ }
409
+
410
+ this.refreshPromise = null
411
+ }
412
+ }
413
+
414
+ // Export singleton instance
415
+ let defaultTokenManager = null
416
+
417
+ export const getTokenManager = (options) => {
418
+ if (!defaultTokenManager) {
419
+ defaultTokenManager = new TokenManager(options)
420
+ }
421
+ return defaultTokenManager
422
+ }
423
+
424
+ export const createTokenManager = (options) => new TokenManager(options)
@@ -39,3 +39,126 @@ export const buildUserQuery = userId => ({
39
39
  $all: true
40
40
  }
41
41
  })
42
+
43
+ export const buildGetUserDataQuery = userId => ({
44
+ $id: userId,
45
+ id: true,
46
+ name: true,
47
+ email: true,
48
+ username: true,
49
+ globalRole: true,
50
+ updatedAt: true,
51
+ createdAt: true,
52
+ memberProjects: {
53
+ $list: true,
54
+ id: true,
55
+ role: true,
56
+ createdAt: true,
57
+ updatedAt: true,
58
+ project: {
59
+ id: true,
60
+ key: true,
61
+ name: true,
62
+ thumbnail: true,
63
+ icon: true,
64
+ tier: true,
65
+ visibility: true,
66
+ access: true,
67
+ members: {
68
+ $list: true,
69
+ user: {
70
+ id: true,
71
+ name: true,
72
+ email: true,
73
+ globalRole: true
74
+ },
75
+ role: true,
76
+ updatedAt: true,
77
+ createdAt: true
78
+ }
79
+ }
80
+ }
81
+ })
82
+
83
+ export const buildGetProjectsByKeysQuery = keys => (
84
+ {
85
+ projects: {
86
+ id: true,
87
+ key: true,
88
+ name: true,
89
+ thumbnail: true,
90
+ icon: true,
91
+ tier: true,
92
+ visibility: true,
93
+ access: true,
94
+ members: {
95
+ $list: true,
96
+ user: {
97
+ id: true,
98
+ name: true,
99
+ email: true,
100
+ globalRole: true
101
+ },
102
+ role: true,
103
+ updatedAt: true,
104
+ createdAt: true
105
+ },
106
+ $list: {
107
+ $find: {
108
+ $traverse: "children",
109
+ $filter: [
110
+ { $field: "type", $operator: "=", $value: "project" },
111
+ { $field: "key", $operator: "=", $value: keys }
112
+ ]
113
+ }
114
+ }
115
+ }
116
+ }
117
+ )
118
+
119
+ const GetProjectFields = {
120
+ id: true,
121
+ name: true,
122
+ key: true,
123
+ tier: true,
124
+ projectType: true,
125
+ icon: true,
126
+ package: true,
127
+ seats: true,
128
+ projectPassword: true,
129
+ stripe: true,
130
+ payments:{
131
+ $list: true,
132
+ id: true,
133
+ name: true
134
+ },
135
+ access: true,
136
+ isSharedLibrary: true,
137
+ framework: true,
138
+ designTool: true,
139
+ language: true,
140
+ visibility: true,
141
+ domains: true,
142
+ subscription:{id: true},
143
+ members: {
144
+ $list: true,
145
+ user: {id: true, name: true, email: true},
146
+ role: true,
147
+ }
148
+ }
149
+
150
+ export const buildGetProjectDataQuery = projectId => ({
151
+ $id: projectId,
152
+ ...GetProjectFields
153
+ })
154
+
155
+ export const buildGetProjectByKeyDataQuery = key => ({
156
+ ...GetProjectFields,
157
+ $find: {
158
+ $traverse: 'children',
159
+ $filter: [
160
+ { $field: 'type', $operator: '=', $value: 'project' },
161
+ { $field: 'key', $operator: '=', $value: key }
162
+ ]
163
+ }
164
+ })
@@ -84,12 +84,11 @@ export const PERMISSION_MAP = {
84
84
 
85
85
  export const ROLE_PERMISSIONS = {
86
86
  guest: ['viewPublicProjects'],
87
- user: ['viewPublicProjects', 'createProject'],
88
- admin: ['viewPublicProjects', 'createProject', 'manageUsers'],
87
+ user: ['viewPublicProjects'],
88
+ admin: ['viewPublicProjects', 'governance'],
89
89
  superAdmin: [
90
90
  'viewPublicProjects',
91
- 'createProject',
92
- 'manageUsers',
91
+ 'governance',
93
92
  'managePlatform'
94
93
  ]
95
94
  }
@@ -187,6 +186,7 @@ export const TIER_FEATURES = {
187
186
  }
188
187
 
189
188
  export const PROJECT_ROLE_PERMISSIONS = {
189
+ unauthenticated: ['platformSettings', 'showContent'],
190
190
  guest: ['platformSettings', 'showContent'],
191
191
  editor: [
192
192
  'platformSettings',
@@ -1,24 +1,24 @@
1
1
  export const SERVICE_METHODS = {
2
- // Auth service methods
2
+ // Auth service methods (legacy - keeping for backward compatibility)
3
3
  auth: 'auth',
4
4
  login: 'auth',
5
5
  register: 'auth',
6
6
  googleAuth: 'auth',
7
+ googleAuthCallback: 'auth',
7
8
  githubAuth: 'auth',
8
9
  confirmRegistration: 'auth',
9
10
  logout: 'auth',
10
11
  updateUserRole: 'auth',
11
12
  hasPermission: 'auth',
13
+ hasGlobalPermission: 'auth',
12
14
  getProjectMembers: 'auth',
13
15
  inviteMember: 'auth',
14
16
  acceptInvite: 'auth',
15
17
  updateMemberRole: 'auth',
16
18
  removeMember: 'auth',
17
19
  updateProjectTier: 'auth',
18
- requestPasswordReset: 'auth',
19
- confirmPasswordReset: 'auth',
20
20
  subscribeToAuthChanges: 'auth',
21
- getStoredAuthState: 'auth',
21
+ getStoredAuthState: 'core',
22
22
 
23
23
  // AI service methods
24
24
  prompt: 'ai',
@@ -27,6 +27,7 @@ export const SERVICE_METHODS = {
27
27
  query: 'based',
28
28
  setProject: 'based',
29
29
  setUser: 'based',
30
+ setUserForced: 'based',
30
31
  subscribe: 'based',
31
32
  call: 'based',
32
33
  getProject: 'based',
@@ -62,30 +63,52 @@ export const SERVICE_METHODS = {
62
63
  confirmPasswordChange: 'based',
63
64
  updateUserProfile: 'based',
64
65
  duplicateProject: 'based',
66
+ listPlans: 'based',
67
+ subscribeToPlan: 'based',
68
+ getSubscriptionDetails: 'based',
69
+ checkSubscriptionStatus: 'based',
70
+ upgradeSubscription: 'based',
71
+ downgradeSubscription: 'based',
72
+ cancelSubscription: 'based',
73
+ reactivateSubscription: 'based',
74
+ generateInvoice: 'based',
75
+ getUsageReport: 'based',
76
+ inviteAccountOwner: 'based',
77
+ acceptOwnerInvitation: 'based',
78
+ removeAccountOwner: 'based',
79
+ checkResourceLimit: 'based',
80
+ checkFeatureAccess: 'based',
81
+ getUserFeatures: 'based',
82
+ getAvailableFeatures: 'based',
83
+ listFeatureFlags: 'based',
84
+ updateFeatureFlag: 'based',
85
+ removeFeatureFlag: 'based',
86
+ batchUpdateFeatureFlags: 'based',
87
+ updatePlanDetails: 'based',
88
+ updatePlanStatus: 'based',
65
89
 
66
90
  // Symstory service methods
67
91
  set: 'symstory',
68
92
  getData: 'symstory',
69
93
  updateData: 'symstory',
70
94
  getBranches: 'symstory',
71
- createBranch: 'symstory',
72
95
  editBranch: 'symstory',
73
- deleteBranch: 'symstory',
74
- mergeBranch: 'symstory',
75
96
  restoreVersion: 'symstory',
76
97
  getItem: 'symstory',
77
98
  addItem: 'symstory',
99
+ addMultipleItems: 'symstory',
78
100
  updateItem: 'symstory',
79
101
  deleteItem: 'symstory',
80
102
  switchVersion: 'symstory',
81
103
  switchBranch: 'symstory',
82
104
  undo: 'symstory',
83
105
  redo: 'symstory',
106
+ publish: 'symstory',
107
+ getChanges: 'symstory',
84
108
 
85
109
  // Socket service methods
86
110
  send: 'socket',
87
111
  subscribeChannel: 'socket',
88
112
  connect: 'socket',
89
- reconnect: 'socket',
90
- destroy: 'socket'
113
+ reconnect: 'socket'
91
114
  }
@@ -30,7 +30,7 @@ class SymstoryClient {
30
30
  * @returns {Promise<any>} - The response data.
31
31
  */
32
32
  async request (path = '', options = {}) {
33
- const url = `${this.options.baseUrl}/${this.appKey}${path}`
33
+ const url = `${this.options.baseUrl}/symstory/${this.appKey}${path}`
34
34
  const response = await fetch(url, {
35
35
  ...options,
36
36
  headers: { ...this.headers, ...options.headers }
@@ -194,6 +194,40 @@ class SymstoryClient {
194
194
  body: JSON.stringify({ branch, version, type, message })
195
195
  })
196
196
  }
197
+
198
+ /**
199
+ * Publishes an existing version of the project.
200
+ * @param {string} version - The version ID/number to publish.
201
+ * @param {object} [options={}] - The publishing options.
202
+ * @param {string} [options.branch='main'] - The branch name. (Only if version number is provided)
203
+ * @returns {Promise<any>} - The response data.
204
+ */
205
+ publishVersion (version, { branch = 'main' } = {}) {
206
+ return this.request('/publish', {
207
+ method: 'POST',
208
+ body: JSON.stringify({ version })
209
+ })
210
+ }
211
+
212
+ /**
213
+ * Retrieves all changes after a specific version.
214
+ * @param {object} [options={}] - The changes options.
215
+ * @param {string} [options.versionId] - The version ID to publish.
216
+ * @param {string} [options.versionValue] - The version ID to publish. (alternative to versionId)
217
+ * @param {string} [options.branch] - The branch to publish (Only in combination to versionValue)
218
+ * @returns {Promise<any>} - The changes data.
219
+ */
220
+ getChanges ({ versionId, versionValue, branch } = {}) {
221
+ return this.request(
222
+ '/changes?' +
223
+ new URLSearchParams({
224
+ ...(versionId ? { versionId } : {}),
225
+ ...(versionValue ? { versionValue } : {}),
226
+ ...(branch ? { branch } : {})
227
+ }).toString(),
228
+ {}
229
+ )
230
+ }
197
231
  }
198
232
 
199
233
  export default {