@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
@@ -147,7 +147,28 @@ export class TokenManager {
147
147
  if (!this.tokens.expiresAt) {return true} // No expiry info, assume valid
148
148
 
149
149
  const now = Date.now()
150
- return now < (this.tokens.expiresAt - this.config.refreshBuffer)
150
+ const isValid = now < (this.tokens.expiresAt - this.config.refreshBuffer)
151
+
152
+ if (!isValid) {
153
+ console.log('[TokenManager] Access token is expired or near expiry:', {
154
+ now: new Date(now).toISOString(),
155
+ expiresAt: new Date(this.tokens.expiresAt).toISOString(),
156
+ refreshBuffer: this.config.refreshBuffer
157
+ })
158
+ }
159
+
160
+ return isValid
161
+ }
162
+
163
+ /**
164
+ * Check if access token exists and is not expired (without refresh buffer)
165
+ */
166
+ isAccessTokenActuallyValid () {
167
+ if (!this.tokens.accessToken) {return false}
168
+ if (!this.tokens.expiresAt) {return true} // No expiry info, assume valid
169
+
170
+ const now = Date.now()
171
+ return now < this.tokens.expiresAt
151
172
  }
152
173
 
153
174
  /**
@@ -303,23 +324,30 @@ export class TokenManager {
303
324
  * Save tokens to storage
304
325
  */
305
326
  saveTokens () {
306
- const {storage} = this
307
- const keys = this.storageKeys
327
+ try {
328
+ const {storage} = this
329
+ const keys = this.storageKeys
308
330
 
309
- if (this.tokens.accessToken) {
310
- storage.setItem(keys.accessToken, this.tokens.accessToken)
311
- }
331
+ if (this.tokens.accessToken) {
332
+ storage.setItem(keys.accessToken, this.tokens.accessToken)
333
+ }
312
334
 
313
- if (this.tokens.refreshToken) {
314
- storage.setItem(keys.refreshToken, this.tokens.refreshToken)
315
- }
335
+ if (this.tokens.refreshToken) {
336
+ storage.setItem(keys.refreshToken, this.tokens.refreshToken)
337
+ }
316
338
 
317
- if (this.tokens.expiresAt) {
318
- storage.setItem(keys.expiresAt, this.tokens.expiresAt.toString())
319
- }
339
+ if (this.tokens.expiresAt) {
340
+ storage.setItem(keys.expiresAt, this.tokens.expiresAt.toString())
341
+ }
320
342
 
321
- if (this.tokens.expiresIn) {
322
- storage.setItem(keys.expiresIn, this.tokens.expiresIn.toString())
343
+ if (this.tokens.expiresIn) {
344
+ storage.setItem(keys.expiresIn, this.tokens.expiresIn.toString())
345
+ }
346
+
347
+ } catch (error) {
348
+ console.error('[TokenManager] Error saving tokens to storage:', error)
349
+ // Don't throw here as it would break the token setting flow
350
+ // but log the error for debugging
323
351
  }
324
352
  }
325
353
 
@@ -327,25 +355,35 @@ export class TokenManager {
327
355
  * Load tokens from storage
328
356
  */
329
357
  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)
358
+ try {
359
+ const {storage} = this
360
+ const keys = this.storageKeys
361
+
362
+ const accessToken = storage.getItem(keys.accessToken)
363
+ const refreshToken = storage.getItem(keys.refreshToken)
364
+ const expiresAt = storage.getItem(keys.expiresAt)
365
+ const expiresIn = storage.getItem(keys.expiresIn)
366
+
367
+ if (accessToken) {
368
+ this.tokens = {
369
+ accessToken,
370
+ refreshToken,
371
+ expiresAt: expiresAt ? parseInt(expiresAt, 10) : null,
372
+ expiresIn: expiresIn ? parseInt(expiresIn, 10) : null,
373
+ tokenType: 'Bearer'
374
+ }
337
375
 
338
- if (accessToken) {
376
+ // Schedule refresh for loaded tokens
377
+ this.scheduleRefresh()
378
+ }
379
+ } catch (error) {
380
+ console.error('[TokenManager] Error loading tokens from storage:', error)
339
381
  this.tokens = {
340
- accessToken,
341
- refreshToken,
342
- expiresAt: expiresAt ? parseInt(expiresAt, 10) : null,
343
- expiresIn: expiresIn ? parseInt(expiresIn, 10) : null,
344
- tokenType: 'Bearer'
382
+ accessToken: null,
383
+ refreshToken: null,
384
+ expiresAt: null,
385
+ expiresIn: null
345
386
  }
346
-
347
- // Schedule refresh for loaded tokens
348
- this.scheduleRefresh()
349
387
  }
350
388
  }
351
389
 
@@ -0,0 +1,109 @@
1
+ // Lightweight JSON diff & patch helpers for CollabClient
2
+ // Each op: { action: 'set' | 'del', path: [...string], value?: any }
3
+
4
+ // helper functions
5
+ function isPlainObject (o) {
6
+ return o && typeof o === 'object' && !Array.isArray(o)
7
+ }
8
+
9
+ function deepEqual (a, b) {
10
+ try {
11
+ return JSON.stringify(a) === JSON.stringify(b)
12
+ } catch (err) {
13
+ console.warn('deepEqual error', err)
14
+ return false
15
+ }
16
+ }
17
+
18
+ import * as Y from 'yjs'
19
+
20
+ // Retrieve the shared root map. We deliberately avoid creating a nested
21
+ // "root -> root" structure that previously caused an ever-growing tree.
22
+ function getRootMap (ydoc) {
23
+ // `getMap()` lazily initialises the map if it does not yet exist, so the
24
+ // returned instance is always defined.
25
+ return ydoc.getMap('root')
26
+ }
27
+
28
+ // diff algorithm
29
+ export function diffJson (prev, next, prefix = []) {
30
+ const ops = []
31
+ const _prefix = Array.isArray(prefix) ? prefix : []
32
+
33
+ // deletions
34
+ for (const key in prev) {
35
+ if (
36
+ Object.hasOwn(prev, key) &&
37
+ !(key in next)
38
+ ) {
39
+ ops.push({ action: 'del', path: [..._prefix, key] })
40
+ }
41
+ }
42
+
43
+ // additions / updates
44
+ for (const key in next) {
45
+ if (Object.hasOwn(next, key)) {
46
+ const pVal = prev?.[key]
47
+ const nVal = next[key]
48
+
49
+ if (isPlainObject(pVal) && isPlainObject(nVal)) {
50
+ ops.push(...diffJson(pVal, nVal, [..._prefix, key]))
51
+ } else if (!deepEqual(pVal, nVal)) {
52
+ ops.push({ action: 'set', path: [..._prefix, key], value: nVal })
53
+ }
54
+ }
55
+ }
56
+
57
+ return ops
58
+ }
59
+
60
+ // apply ops to Yjs
61
+ export function applyOpsToJson (ops, ydoc) {
62
+ if (!ydoc || !Array.isArray(ops) || !ops.length) { return }
63
+
64
+ // Wrap modifications in a transaction so that we can tag them with the
65
+ // special "remote" origin. This ensures that our local change listener
66
+ // (`afterTransaction`) can safely ignore these updates and prevents
67
+ // feedback loops where we would echo remote changes back to the server.
68
+ ydoc.transact(() => {
69
+ const root = getRootMap(ydoc)
70
+
71
+ ops.forEach(op => {
72
+ const { action, path = [], value } = op || {}
73
+ if (!path.length) { return }
74
+
75
+ let target = root
76
+
77
+ // Traverse (or lazily create) intermediate maps.
78
+ for (let i = 0; i < path.length - 1; i++) {
79
+ const key = path[i]
80
+ let next = target.get(key)
81
+
82
+ if (!(next instanceof Y.Map)) {
83
+ // If the key is missing or not a Y.Map, replace it with a new map so
84
+ // we have a consistent structure for nested updates.
85
+ const fresh = new Y.Map()
86
+
87
+ // Preserve any plain object that may have existed previously.
88
+ if (isPlainObject(next)) {
89
+ Object.entries(next).forEach(([k, v]) => fresh.set(k, v))
90
+ }
91
+
92
+ target.set(key, fresh)
93
+ next = fresh
94
+ }
95
+
96
+ target = next
97
+ }
98
+
99
+ const last = path[path.length - 1]
100
+
101
+ // Apply the leaf operation.
102
+ if (action === 'set') {
103
+ target.set(last, value)
104
+ } else if (action === 'del') {
105
+ target.delete(last)
106
+ }
107
+ })
108
+ }, 'remote')
109
+ }
@@ -20,95 +20,147 @@ export const SERVICE_METHODS = {
20
20
  subscribeToAuthChanges: 'auth',
21
21
  getStoredAuthState: 'core',
22
22
 
23
- // AI service methods
24
- prompt: 'ai',
23
+ // Collab service methods
24
+ connect: 'collab',
25
+ disconnect: 'collab',
26
+ isConnected: 'collab',
27
+ updateData: 'collab',
28
+ addItem: 'collab',
29
+ addMultipleItems: 'collab',
30
+ updateItem: 'collab',
31
+ deleteItem: 'collab',
32
+ undo: 'collab',
33
+ redo: 'collab',
34
+ checkpoint: 'collab',
25
35
 
26
- // Based service methods
27
- query: 'based',
28
- setProject: 'based',
29
- setUser: 'based',
30
- setUserForced: 'based',
31
- subscribe: 'based',
32
- call: 'based',
33
- getProject: 'based',
34
- getProjectByKey: 'based',
35
- createProject: 'based',
36
- fetchUser: 'based',
37
- fetchProject: 'based',
38
- setBucket: 'based',
39
- getUser: 'based',
40
- getUserByEmail: 'based',
41
- setProjectDomains: 'based',
42
- checkProjectKeyAvailability: 'based',
43
- removeProject: 'based',
44
- getAvailableLibraries: 'based',
45
- addProjectLibraries: 'based',
46
- removeProjectLibraries: 'based',
47
- getProjectLibraries: 'based',
48
- checkout: 'based',
49
- uploadFile: 'based',
50
- updateProjectIcon: 'based',
51
- updateProject: 'based',
52
- updateProjectSettings: 'based',
53
- updateProjectComponents: 'based',
54
- updateProjectName: 'based',
55
- updateProjectPackage: 'based',
56
- createDnsRecord: 'based',
57
- getDnsRecord: 'based',
58
- removeDnsRecord: 'based',
59
- createStorageBucket: 'based',
60
- getStorageBucket: 'based',
61
- removeStorageBucket: 'based',
62
- requestPasswordChange: 'based',
63
- confirmPasswordChange: 'based',
64
- updateUserProfile: 'based',
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',
36
+ // Realtime collaboration helper methods
37
+ sendCursor: 'collab',
38
+ sendPresence: 'collab',
39
+ toggleLive: 'collab',
89
40
 
90
- // Symstory service methods
91
- set: 'symstory',
92
- getData: 'symstory',
93
- updateData: 'symstory',
94
- getBranches: 'symstory',
95
- editBranch: 'symstory',
96
- restoreVersion: 'symstory',
97
- getItem: 'symstory',
98
- addItem: 'symstory',
99
- addMultipleItems: 'symstory',
100
- updateItem: 'symstory',
101
- deleteItem: 'symstory',
102
- switchVersion: 'symstory',
103
- switchBranch: 'symstory',
104
- undo: 'symstory',
105
- redo: 'symstory',
106
- publish: 'symstory',
107
- getChanges: 'symstory',
41
+ // Core service methods (new - replaces most based/auth functionality)
42
+ // Auth methods
43
+ register: 'core',
44
+ login: 'core',
45
+ logout: 'core',
46
+ refreshToken: 'core',
47
+ googleAuth: 'core',
48
+ googleAuthCallback: 'core',
49
+ githubAuth: 'core',
50
+ requestPasswordReset: 'core',
51
+ confirmPasswordReset: 'core',
52
+ confirmRegistration: 'core',
53
+ requestPasswordChange: 'core',
54
+ confirmPasswordChange: 'core',
55
+ getMe: 'core',
108
56
 
109
- // Socket service methods
110
- send: 'socket',
111
- subscribeChannel: 'socket',
112
- connect: 'socket',
113
- reconnect: 'socket'
57
+ // User methods
58
+ getUserProfile: 'core',
59
+ updateUserProfile: 'core',
60
+ getUserProjects: 'core',
61
+ getUser: 'core',
62
+ getUserByEmail: 'core',
63
+
64
+ // Project methods
65
+ createProject: 'core',
66
+ getProjects: 'core',
67
+ getProject: 'core',
68
+ getProjectByKey: 'core',
69
+ getPublicProject: 'core',
70
+ listPublicProjects: 'core',
71
+ listProjects: 'core',
72
+ updateProject: 'core',
73
+ updateProjectComponents: 'core',
74
+ updateProjectSettings: 'core',
75
+ updateProjectName: 'core',
76
+ updateProjectPackage: 'core',
77
+ duplicateProject: 'core',
78
+ removeProject: 'core',
79
+ checkProjectKeyAvailability: 'core',
80
+
81
+ // Project member methods
82
+ getProjectMembers: 'core',
83
+ inviteMember: 'core',
84
+ acceptInvite: 'core',
85
+ updateMemberRole: 'core',
86
+ removeMember: 'core',
87
+
88
+ // Project library methods
89
+ getAvailableLibraries: 'core',
90
+ getProjectLibraries: 'core',
91
+ addProjectLibraries: 'core',
92
+ removeProjectLibraries: 'core',
93
+
94
+ // File methods
95
+ uploadFile: 'core',
96
+ updateProjectIcon: 'core',
97
+
98
+ // Payment methods
99
+ checkout: 'core',
100
+ getSubscriptionStatus: 'core',
101
+
102
+ // DNS methods
103
+ createDnsRecord: 'core',
104
+ getDnsRecord: 'core',
105
+ removeDnsRecord: 'core',
106
+ setProjectDomains: 'core',
107
+
108
+ // Utility methods
109
+ getHealthStatus: 'core',
110
+ getTokenDebugInfo: 'core',
111
+
112
+ // Project Data methods (Symstory replacement)
113
+ applyProjectChanges: 'core',
114
+ getProjectData: 'core',
115
+ getProjectVersions: 'core',
116
+ restoreProjectVersion: 'core',
117
+ updateProjectItem: 'core',
118
+ deleteProjectItem: 'core',
119
+ setProjectValue: 'core',
120
+ addProjectItems: 'core',
121
+ getProjectItemByPath: 'core',
122
+
123
+ // Pull Request methods
124
+ createPullRequest: 'core',
125
+ listPullRequests: 'core',
126
+ getPullRequest: 'core',
127
+ reviewPullRequest: 'core',
128
+ addPullRequestComment: 'core',
129
+ mergePullRequest: 'core',
130
+ getPullRequestDiff: 'core',
131
+ createPullRequestWithValidation: 'core',
132
+ approvePullRequest: 'core',
133
+ requestPullRequestChanges: 'core',
134
+ getOpenPullRequests: 'core',
135
+ getClosedPullRequests: 'core',
136
+ getMergedPullRequests: 'core',
137
+ isPullRequestMergeable: 'core',
138
+ getPullRequestStatusSummary: 'core',
139
+
140
+ // Branch Management methods
141
+ listBranches: 'core',
142
+ createBranch: 'core',
143
+ deleteBranch: 'core',
144
+ renameBranch: 'core',
145
+ getBranchChanges: 'core',
146
+ mergeBranch: 'core',
147
+ resetBranch: 'core',
148
+ publishVersion: 'core',
149
+ createBranchWithValidation: 'core',
150
+ branchExists: 'core',
151
+ previewMerge: 'core',
152
+ commitMerge: 'core',
153
+ createFeatureBranch: 'core',
154
+ createHotfixBranch: 'core',
155
+ getBranchStatus: 'core',
156
+ deleteBranchSafely: 'core',
157
+
158
+ // Admin methods
159
+ getAdminUsers: 'core',
160
+ assignProjectsToUser: 'core',
161
+ searchAdminUsers: 'core',
162
+ getAdminUsersByEmails: 'core',
163
+ getAdminUsersByIds: 'core',
164
+ assignSpecificProjectsToUser: 'core',
165
+ assignAllProjectsToUser: 'core'
114
166
  }
@@ -1,7 +1,7 @@
1
1
  import config from '../config/environment.js'
2
2
 
3
3
  const DEFAULT_OPTIONS = {
4
- baseUrl: config.baseUrl
4
+ apiUrl: config.apiUrl
5
5
  }
6
6
 
7
7
  class SymstoryClient {
@@ -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}/symstory/${this.appKey}${path}`
33
+ const url = `${this.options.apiUrl}/symstory/${this.appKey}${path}`
34
34
  const response = await fetch(url, {
35
35
  ...options,
36
36
  headers: { ...this.headers, ...options.headers }
@@ -202,7 +202,7 @@ class SymstoryClient {
202
202
  * @param {string} [options.branch='main'] - The branch name. (Only if version number is provided)
203
203
  * @returns {Promise<any>} - The response data.
204
204
  */
205
- publishVersion (version, { branch = 'main' } = {}) {
205
+ publishVersion (version) {
206
206
  return this.request('/publish', {
207
207
  method: 'POST',
208
208
  body: JSON.stringify({ version })
@@ -219,12 +219,12 @@ class SymstoryClient {
219
219
  */
220
220
  getChanges ({ versionId, versionValue, branch } = {}) {
221
221
  return this.request(
222
- '/changes?' +
222
+ `/changes?${
223
223
  new URLSearchParams({
224
224
  ...(versionId ? { versionId } : {}),
225
225
  ...(versionValue ? { versionValue } : {}),
226
226
  ...(branch ? { branch } : {})
227
- }).toString(),
227
+ }).toString()}`,
228
228
  {}
229
229
  )
230
230
  }