@symbo.ls/sdk 3.4.6 → 3.4.11

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 (2) hide show
  1. package/README.md +529 -732
  2. package/package.json +5 -5
package/README.md CHANGED
@@ -1,5 +1,16 @@
1
- # SDK
1
+ # @symbo.ls/sdk
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@symbo.ls/sdk.svg)](https://www.npmjs.com/package/@symbo.ls/sdk)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@symbo.ls/sdk.svg)](https://www.npmjs.com/package/@symbo.ls/sdk)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@symbo.ls/sdk)](https://bundlephobia.com/package/@symbo.ls/sdk)
6
+ [![license](https://img.shields.io/npm/l/@symbo.ls/sdk.svg)](https://github.com/nicholasgasior/symbo.ls/blob/main/LICENSE)
7
+ [![node](https://img.shields.io/node/v/@symbo.ls/sdk.svg)](https://nodejs.org)
8
+ [![ESM](https://img.shields.io/badge/module-ESM%20%7C%20CJS-blue)](https://www.npmjs.com/package/@symbo.ls/sdk)
9
+
10
+ > Official SDK for the [Symbols](https://symbols.app) design platform — manage projects, collaborate in real-time, handle branches, pull requests, and more.
11
+
2
12
  ## Installation
13
+
3
14
  ```bash
4
15
  npm install @symbo.ls/sdk
5
16
  ```
@@ -7,11 +18,11 @@ npm install @symbo.ls/sdk
7
18
  ## Basic Usage
8
19
 
9
20
  ### Initialize SDK
21
+
10
22
  ```javascript
11
23
  import { SDK } from '@symbo.ls/sdk'
12
24
 
13
25
  const sdk = new SDK({
14
- useNewServices: true, // Use new service implementations
15
26
  apiUrl: 'https://api.symbols.app',
16
27
  socketUrl: 'https://api.symbols.app',
17
28
  timeout: 30000,
@@ -19,902 +30,688 @@ const sdk = new SDK({
19
30
  debug: false
20
31
  })
21
32
 
22
- // Initialize with context
23
33
  await sdk.initialize({
24
34
  authToken: 'your-auth-token',
25
- appKey: 'your-app-key',
35
+ appKey: 'your-app-key'
26
36
  })
27
37
  ```
28
38
 
29
39
  ### Service Access
40
+
30
41
  ```javascript
31
- // Get service instances
32
42
  const auth = sdk.getService('auth')
33
- const socket = sdk.getService('socket')
34
- const symstory = sdk.getService('symstory')
35
- const based = sdk.getService('based')
36
- const ai = sdk.getService('ai')
43
+ const project = sdk.getService('project')
44
+ const branch = sdk.getService('branch')
45
+ const pullRequest = sdk.getService('pullRequest')
46
+ const collab = sdk.getService('collab')
47
+ const file = sdk.getService('file')
48
+ const payment = sdk.getService('payment')
49
+ const plan = sdk.getService('plan')
50
+ const subscription = sdk.getService('subscription')
51
+ const dns = sdk.getService('dns')
52
+ const admin = sdk.getService('admin')
53
+ const screenshot = sdk.getService('screenshot')
54
+ const tracking = sdk.getService('tracking')
55
+ const waitlist = sdk.getService('waitlist')
56
+ const metrics = sdk.getService('metrics')
57
+ const integration = sdk.getService('integration')
58
+ const featureFlag = sdk.getService('featureFlag')
59
+ ```
60
+
61
+ All service methods are also available directly on the SDK instance via proxy methods:
62
+
63
+ ```javascript
64
+ // These are equivalent:
65
+ sdk.getService('project').getProject(projectId)
66
+ sdk.getProject(projectId)
37
67
  ```
38
68
 
39
- ### Status Checking
69
+ ### Status & Context
70
+
40
71
  ```javascript
41
72
  // Check if SDK is ready
42
- const ready = sdk.isReady()
73
+ sdk.isReady()
43
74
 
44
75
  // Get detailed status
45
76
  const status = sdk.getStatus()
46
- console.log(status)
47
- /* Output:
48
- {
49
- ready: true,
50
- services: [
51
- { name: 'auth', ready: true, ... },
52
- { name: 'socket', ready: true, ... },
53
- { name: 'symstory', ready: true, ... },
54
- { name: 'based', ready: true, ... },
55
- { name: 'ai', ready: true, ... }
56
- ],
57
- context: { ... }
58
- }
59
- */
77
+ // { ready: true, services: [...], context: {...} }
78
+
79
+ // Update context
80
+ sdk.updateContext({ ... })
81
+
82
+ // Cleanup
83
+ await sdk.destroy()
60
84
  ```
61
85
 
62
- ### Context Management
86
+ ### Root Event Bus
87
+
88
+ The SDK exposes a global event bus for cross-service communication:
89
+
63
90
  ```javascript
64
- // Update context
65
- sdk.updateContext({
66
- auth: {
67
- authToken: 'new-token'
68
- }
69
- })
91
+ sdk.rootBus.on('checkpoint:done', (payload) => { ... })
92
+ sdk.rootBus.on('clients:updated', (payload) => { ... })
93
+ sdk.rootBus.on('bundle:done', (payload) => { ... })
94
+ sdk.rootBus.on('bundle:error', (payload) => { ... })
70
95
  ```
71
96
 
72
- ## Service-Specific Usage
97
+ ---
98
+
99
+ ## Services
73
100
 
74
101
  ### Auth Service
102
+
103
+ **Authentication:**
104
+
75
105
  ```javascript
76
106
  const auth = sdk.getService('auth')
77
107
 
78
- /**
79
- * Login a user
80
- * @param {string} email - User's email
81
- * @param {string} password - User's password
82
- */
83
- await auth.login(email, password)
84
-
85
- /**
86
- * Register a new user
87
- * @param {Object} userData - User data
88
- */
89
- await auth.register(userData)
90
-
91
- /**
92
- * Logout the current user
93
- */
108
+ await auth.register(userData, options)
109
+ await auth.login(email, password, options)
94
110
  await auth.logout()
95
-
96
- /**
97
- * Request a password reset
98
- * @param {string} email - User's email
99
- */
111
+ await auth.refreshToken(refreshToken)
112
+ await auth.googleAuth(idToken, inviteToken, options)
113
+ await auth.googleAuthCallback(code, redirectUri, inviteToken, options)
114
+ await auth.githubAuth(code, inviteToken, options)
100
115
  await auth.requestPasswordReset(email)
101
-
102
- /**
103
- * Confirm a password reset
104
- * @param {string} token - Reset token
105
- * @param {string} newPassword - New password
106
- */
107
- await auth.confirmPasswordReset(token, newPassword)
108
-
109
- /**
110
- * Update a user's role
111
- * @param {string} userId - User ID
112
- * @param {string} newRole - New role
113
- */
114
- await auth.updateUserRole(userId, newRole)
115
-
116
- /**
117
- * Update a member's role in a project
118
- * @param {string} projectId - Project ID
119
- * @param {string} userId - User ID
120
- * @param {string} role - New role
121
- */
122
- await auth.updateMemberRole(projectId, userId, role)
123
-
124
- /**
125
- * Update a project's tier
126
- * @param {string} projectId - Project ID
127
- * @param {string} newTier - New tier
128
- */
129
- await auth.updateProjectTier(projectId, newTier)
130
-
131
- /**
132
- * Invite a member to a project
133
- * @param {string} projectId - Project ID
134
- * @param {string} email - Member's email
135
- * @param {string} role - Member's role
136
- * @param {string} name - Member's name
137
- */
138
- await auth.inviteMember(projectId, email, role, name)
139
-
140
- /**
141
- * Accept an invite
142
- * @param {string} token - Invite token
143
- */
144
- await auth.acceptInvite(token)
145
-
146
- /**
147
- * Confirm a user's registration
148
- * @param {string} token - Registration token
149
- */
116
+ await auth.confirmPasswordReset(token, password)
150
117
  await auth.confirmRegistration(token)
118
+ await auth.requestPasswordChange()
119
+ await auth.confirmPasswordChange(currentPassword, newPassword, code)
120
+ ```
151
121
 
152
- /**
153
- * Get members of a project
154
- * @param {string} projectId - Project ID
155
- */
156
- await auth.getProjectMembers(projectId)
157
-
158
- /**
159
- * Check if a user has a specific permission
160
- * @param {string} projectId - Project ID
161
- * @param {string} permission - Permission to check
162
- */
163
- const hasPermission = auth.hasPermission(projectId, 'edit')
164
-
165
- /**
166
- * Check if a user has a global permission
167
- * @param {string} globalRole - User's global role
168
- * @param {string} permission - Permission to check
169
- */
170
- const hasGlobalPermission = auth.hasGlobalPermission('admin', 'manage')
171
-
172
- /**
173
- * Check if a user has a project-specific permission
174
- * @param {Object} projectRoles - User's project roles
175
- * @param {string} projectId - Project ID
176
- * @param {string} requiredPermission - Permission to check
177
- */
178
- const hasProjectPermission = auth.checkProjectPermission(projectRoles, projectId, 'edit')
179
-
180
- /**
181
- * Check if a project has a specific feature
182
- * @param {string} projectTier - Project's tier
183
- * @param {string} feature - Feature to check
184
- */
185
- const hasProjectFeature = auth.checkProjectFeature('pro1', 'aiCopilot:5')
186
-
187
- /**
188
- * Check if a user can perform a specific operation
189
- * @param {string} projectId - Project ID
190
- * @param {string} operation - Operation to check
191
- * @param {Object} options - Additional options
192
- */
193
- const canPerformOperation = await auth.canPerformOperation(projectId, 'edit', { checkFeatures: true })
194
-
195
- /**
196
- * Execute an action with permission check
197
- * @param {string} projectId - Project ID
198
- * @param {string} operation - Operation to check
199
- * @param {function} action - Action to execute
200
- */
201
- await auth.withPermission(projectId, 'edit', () => {
202
- // Action to perform
203
- })
122
+ **User Data:**
204
123
 
205
- /**
206
- * Get project access information
207
- * @param {string} projectId - Project ID
208
- */
209
- const projectAccess = await auth.getProjectAccess(projectId)
124
+ ```javascript
125
+ await auth.getMe(options)
126
+ auth.getStoredAuthState()
127
+ auth.getAuthToken()
128
+ auth.getUserProfile()
129
+ await auth.updateUserProfile(profileData)
130
+ await auth.getUserProjects()
131
+ await auth.getUser(userId)
132
+ await auth.getUserByEmail(email)
133
+ auth.isAuthenticated()
134
+ auth.hasValidTokens()
135
+ auth.getCurrentUser()
210
136
  ```
211
137
 
212
- ### Socket Service
138
+ **Project Roles:**
139
+
213
140
  ```javascript
214
- const socket = sdk.getService('socket')
141
+ await auth.getMyProjectRole(projectId) // cached
142
+ await auth.getMyProjectRoleByKey(projectKey) // cached
143
+ auth.clearProjectRoleCache(projectId)
144
+ ```
215
145
 
216
- /**
217
- * Subscribe to events
218
- * @param {string} event - Event name
219
- * @param {function} callback - Callback function
220
- * @returns {function} Unsubscribe function
221
- */
222
- const unsubscribe = socket.subscribe('updates', (data) => {
223
- console.log('Received:', data)
224
- })
146
+ **Permissions:**
225
147
 
226
- /**
227
- * Send data
228
- * @param {string} event - Event name
229
- * @param {Object} data - Data to send
230
- */
231
- socket.send('update', {
232
- type: 'change',
233
- data: { ... }
234
- })
148
+ ```javascript
149
+ auth.hasPermission(requiredPermission)
150
+ auth.hasGlobalPermission(globalRole, requiredPermission)
151
+ auth.checkProjectPermission(projectRole, requiredPermission)
152
+ auth.checkProjectFeature(projectTier, feature)
153
+ await auth.canPerformOperation(projectId, operation, options)
154
+ await auth.withPermission(projectId, operation, action)
155
+ ```
235
156
 
236
- // Cleanup
237
- unsubscribe()
238
- ```
239
-
240
- ### Symstory Service
241
- ```javascript
242
- const symstory = sdk.getService('symstory')
243
-
244
- /**
245
- * Get data
246
- * @param {Object} query - Query object
247
- */
248
- await symstory.getData(query)
249
-
250
- /**
251
- * Update data
252
- * @param {Object} changes - Changes to apply
253
- */
254
- await symstory.updateData(changes)
255
-
256
- /**
257
- * Delete data
258
- * @param {string} path - Path to data
259
- */
260
- await symstory.deleteData(path)
261
-
262
- /**
263
- * Get an item
264
- * @param {string} type - Item type
265
- * @param {string} key - Item key
266
- */
267
- await symstory.getItem(type, key)
268
-
269
- /**
270
- * Add an item
271
- * @param {string} type - Item type
272
- * @param {Object} data - Item data
273
- */
274
- await symstory.addItem(type, data)
275
-
276
- /**
277
- * Update an item
278
- * @param {string} type - Item type
279
- * @param {Object} data - Item data
280
- */
281
- await symstory.updateItem(type, data)
282
-
283
- /**
284
- * Delete an item
285
- * @param {string} type - Item type
286
- * @param {string} key - Item key
287
- */
288
- await symstory.deleteItem(type, key)
289
-
290
- /**
291
- * Get branches
292
- */
293
- await symstory.getBranches()
294
-
295
- /**
296
- * Create a branch
297
- * @param {Object} branch - Branch data
298
- */
299
- await symstory.createBranch(branch)
300
-
301
- /**
302
- * Merge a branch
303
- * @param {Object} branch - Branch data
304
- */
305
- await symstory.mergeBranch(branch)
306
-
307
- /**
308
- * Restore a version
309
- * @param {Object} version - Version data
310
- */
311
- await symstory.restoreVersion(version)
312
- ```
313
-
314
- ### Based Service
315
- ```javascript
316
- const based = sdk.getService('based')
317
-
318
- /**
319
- * Query data
320
- * @param {string} collection - Collection name
321
- * @param {Object} query - Query object
322
- */
323
- const result = await based.query(collection, query)
324
-
325
- /**
326
- * Subscribe to changes
327
- * @param {string} collection - Collection name
328
- * @param {Object} query - Query object
329
- * @param {function} callback - Callback function
330
- * @returns {function} Unsubscribe function
331
- */
332
- const unsubscribe = based.subscribe(collection, query, (data) => {
333
- console.log('Data updated:', data)
334
- })
157
+ ### Project Service
335
158
 
336
- /**
337
- * Call a function
338
- * @param {string} functionName - Function name
339
- * @param {Object} params - Function parameters
340
- */
341
- await based.call('functionName', params)
159
+ **Project Management:**
342
160
 
343
- /**
344
- * Get a project
345
- * @param {string} projectId - Project ID
346
- */
347
- await based.getProject(projectId)
161
+ ```javascript
162
+ const project = sdk.getService('project')
348
163
 
349
- /**
350
- * Create a project
351
- * @param {Object} projectData - Project data
352
- */
353
- await based.createProject(projectData)
164
+ await project.createProject(projectData)
165
+ await project.getProjects(params)
166
+ await project.getProject(projectId)
167
+ await project.getProjectByKey(key)
168
+ await project.getProjectDataByKey(key, options)
169
+ await project.getPublicProject(projectId)
170
+ await project.listPublicProjects(params)
171
+ await project.updateProject(projectId, data)
172
+ await project.updateProjectComponents(projectId, components)
173
+ await project.updateProjectSettings(projectId, settings)
174
+ await project.updateProjectName(projectId, name)
175
+ await project.updateProjectPackage(projectId, pkg)
176
+ await project.duplicateProject(projectId, newName, newKey, targetUserId)
177
+ await project.removeProject(projectId)
178
+ await project.checkProjectKeyAvailability(key)
179
+ ```
354
180
 
355
- /**
356
- * Fetch a user
357
- * @param {string} userId - User ID
358
- */
359
- await based.fetchUser(userId)
181
+ **Role Permissions Config:**
360
182
 
361
- /**
362
- * Fetch a project
363
- * @param {string} projectId - Project ID
364
- */
365
- await based.fetchProject(projectId)
183
+ ```javascript
184
+ await project.getProjectRolePermissionsConfig(projectId, options)
185
+ await project.updateProjectRolePermissionsConfig(projectId, rolePermissions, options)
366
186
  ```
367
187
 
368
- ### AI Service
188
+ **Members:**
189
+
369
190
  ```javascript
370
- const ai = sdk.getService('ai')
191
+ await project.getProjectMembers(projectId)
192
+ await project.inviteMember(projectId, email, role, options)
193
+ await project.createMagicInviteLink(projectId, options)
194
+ await project.acceptInvite(token)
195
+ await project.updateMemberRole(projectId, memberId, role)
196
+ await project.removeMember(projectId, memberId)
197
+ ```
198
+
199
+ **Libraries:**
371
200
 
372
- /**
373
- * Prompt the AI
374
- * @param {string} query - Query string
375
- * @param {Object} options - Query options
376
- */
377
- const response = await ai.prompt(query, options)
201
+ ```javascript
202
+ await project.getAvailableLibraries(params)
203
+ await project.getProjectLibraries(projectId)
204
+ await project.addProjectLibraries(projectId, libraryIds)
205
+ await project.removeProjectLibraries(projectId, libraryIds)
378
206
  ```
379
207
 
380
- ### Tracking Service (Grafana Faro)
208
+ **Project Data (Version Control):**
209
+
381
210
  ```javascript
382
- // 1) Initialize SDK with tracking config (early in app startup)
383
- const sdk = new SDK({
384
- useNewServices: true,
385
- apiUrl: 'https://api.symbols.app',
386
- // Tracking configuration mirrors TrackingService options
387
- tracking: {
388
- url: 'https://<your-faro-receiver-url>', // FO ingest/collector URL
389
- appName: 'Symbols Platform',
390
- environment: 'development', // 'production' | 'staging' | 'testing' | 'development'
391
- appVersion: '1.0.0',
392
- sessionTracking: true,
393
- enableTracing: true, // adds browser tracing when available
394
- globalAttributes: { region: 'us-east-1' }
395
- }
396
- })
397
- await sdk.initialize()
211
+ await project.applyProjectChanges(projectId, changes, options)
212
+ // changes: [['update', ['components', 'Button'], { color: 'blue' }], ['delete', ['pages', 'old']]]
213
+ // options: { message: 'Update button', type: 'patch' }
398
214
 
399
- // 2) Get the tracking service
400
- const tracking = sdk.getService('tracking')
215
+ await project.getProjectData(projectId, options)
216
+ await project.getProjectVersions(projectId, options)
217
+ await project.restoreProjectVersion(projectId, version, options)
218
+ await project.updateProjectItem(projectId, path, value, options)
219
+ await project.deleteProjectItem(projectId, path, options)
220
+ await project.setProjectValue(projectId, path, value, options)
221
+ await project.addProjectItems(projectId, items, options)
222
+ await project.getProjectItemByPath(projectId, path, options)
223
+ ```
224
+
225
+ **Environments:**
401
226
 
402
- // 3) Send signals
403
- tracking.trackEvent('purchase', { amount: 42, currency: 'USD' })
404
- tracking.trackMeasurement('cart_value', { value: 42 })
405
- tracking.logError('checkout failed', { step: 'payment' })
406
- tracking.trackView('Checkout', { stage: 'payment' })
407
- tracking.setUser({ id: 'u_123', email: 'user@example.com' })
408
- ```
409
-
410
- #### Configuration
411
- Provide these under `tracking` when creating the `SDK` (or later via `tracking.configureTracking()`):
412
-
413
- - `url` string: Frontend Observability/Faro ingestion URL. If omitted and no custom transports are provided, tracking is disabled.
414
- - `appName` string: Logical application name used in Grafana dashboards.
415
- - `appVersion` string: App version shown in Grafana.
416
- - `environment` string: One of your environments; default resolves from runtime (`production`, `staging`, `testing`, `development`).
417
- - `sessionTracking` boolean: Enable Faro session tracking. Default: `true`.
418
- - `enableTracing` boolean: Enable web tracing and send to Tempo (if collector configured). Default: `true`.
419
- - `globalAttributes` object: Key/values merged into every signal.
420
- - `user` object: Initial user attributes.
421
- - `maxQueueSize` number: Max queued calls before client setup. Default: `100`.
422
- - `isolate` boolean: Create an isolated Faro instance.
423
- - `transports` array | `transport` any: Custom transports (advanced).
424
- - `instrumentations` array | `instrumentationsFactory(runtime) => Promise<array>` | `webInstrumentationOptions` object: Control Faro web instrumentations.
425
-
426
- Note:
427
- - Tracking is automatically disabled in non‑browser environments.
428
- - Calls are queued until the Faro client is ready. For specific calls, pass `{ queue: false }` to skip queuing.
429
-
430
- #### Method reference
431
- The following methods are available via `sdk.getService('tracking')` and map to `utils/services.js`:
432
-
433
- - `configureTracking(trackingOptions)` / `configure(trackingOptions)`: Merge/override runtime tracking options (supports all config keys above).
434
- - `trackEvent(name, attributes?, options?)`
435
- - `name` string (required)
436
- - `attributes` object merged with global attributes
437
- - `options` object:
438
- - `domain` string | null
439
- - `queue` boolean (whether to queue if client not ready)
440
- - Additional transport options are forwarded to Faro
441
- - Example:
442
- ```javascript
443
- tracking.trackEvent('signup_attempt', { method: 'email' }, { domain: 'auth' })
444
- ```
445
- - `trackError(error, options?)` / `captureException(error, options?)`
446
- - `error` Error | string
447
- - `options` can be:
448
- - object with Faro error options (`context`, `type`, `stackFrames`, `skipDedupe`, `timestampOverwriteMs`, etc.)
449
- - or a plain context object (shorthand)
450
- - `queue` boolean supported
451
- - Example:
452
- ```javascript
453
- tracking.trackError(new Error('Login failed'), { context: { screen: 'Login' } })
454
- ```
455
- - `logMessage(message, level='info', context?)`
456
- - Convenience wrappers: `logDebug`, `logInfo`, `logWarning`/`logWarn`, `logErrorMessage`/`logError`
457
- - `message` string | string[]
458
- - `context` object merged with global attributes
459
- - Example:
460
- ```javascript
461
- tracking.logWarning('Slow response', { route: '/checkout', ttfbMs: 900 })
462
- ```
463
- - `addBreadcrumb(message, attributes?)`
464
- - Adds a low‑cost breadcrumb via `trackEvent('breadcrumb', ...)`
465
- - Example:
466
- ```javascript
467
- tracking.addBreadcrumb('Open modal', { id: 'planLimits' })
468
- ```
469
- - `trackMeasurement(type, values, options?)`
470
- - `type` string (required)
471
- - `values` object | number. If number, it becomes `{ value: <number> }`.
472
- - `options`:
473
- - `attributes` object (merged into payload.attributes)
474
- - `context` object (transport context)
475
- - `queue` boolean
476
- - Any additional transport options
477
- - Example:
478
- ```javascript
479
- tracking.trackMeasurement('cart_value', 42, { context: { currency: 'USD' } })
480
- ```
481
- - `trackView(name, attributes?)`
482
- - Sets the current view/page in Faro
483
- - Example:
484
- ```javascript
485
- tracking.trackView('Dashboard', { section: 'Analytics' })
486
- ```
487
- - `setUser(user, options?)` / `clearUser()`
488
- - `user` object with arbitrary attributes; supports `{ queue: boolean }`
489
- - Example:
490
- ```javascript
491
- tracking.setUser({ id: 'u_123', role: 'admin' })
492
- ```
493
- - `setSession(session, options?)` / `clearSession()`
494
- - Attach custom session data; supports `{ queue: boolean, ...sessionOptions }`
495
- - `setGlobalAttributes(attributes)` / `setGlobalAttribute(key, value)` / `removeGlobalAttribute(key)`
496
- - Manage the global attributes merged into every signal
497
- - `flushQueue()`
498
- - Immediately runs all queued calls (no‑op if client not ready)
499
- - `getClient()`
500
- - Returns the underlying Faro client (or `null` if not ready)
501
- - `isEnabled()` / `isInitialized()`
502
- - Status helpers
503
-
504
- #### Example: auth error tracking from services
505
- The SDK’s services automatically send errors to tracking:
506
227
  ```javascript
507
- try {
508
- await auth.login(email, password)
509
- } catch (error) {
510
- // BaseService forwards details to tracking.trackError(...)
511
- }
228
+ await project.listEnvironments(projectId, options)
229
+ await project.activateMultipleEnvironments(projectId, options)
230
+ await project.upsertEnvironment(projectId, envKey, config, options)
231
+ await project.updateEnvironment(projectId, envKey, updates, options)
232
+ await project.publishToEnvironment(projectId, envKey, payload, options)
233
+ await project.deleteEnvironment(projectId, envKey, options)
234
+ await project.promoteEnvironment(projectId, fromEnvKey, toEnvKey, options)
512
235
  ```
513
236
 
514
- #### Visualizing in Grafana
515
- - Use the Frontend Observability (Faro) data source and pick:
516
- - Service = your `appName`
517
- - Environment = your `environment`
518
- - Panels for page loads and Web Vitals require web instrumentations and real page traffic.
519
- - If self‑hosting with a Faro collector → Loki/Tempo, ensure the FO app is installed and the dashboard uses the FO data source; otherwise create custom panels with LogQL over Loki.
237
+ **Favorites & Recent:**
520
238
 
521
- ## Error Handling
522
239
  ```javascript
523
- try {
524
- await sdk.initialize()
525
- } catch (error) {
526
- console.error('SDK initialization failed:', error.message)
527
- }
240
+ await project.getFavoriteProjects()
241
+ await project.addFavoriteProject(projectId)
242
+ await project.removeFavoriteProject(projectId)
243
+ await project.getRecentProjects(options)
528
244
  ```
529
245
 
530
- ## Cleanup
246
+ **Access Control:**
247
+
531
248
  ```javascript
532
- // Services are automatically cleaned up when SDK is destroyed
533
- sdk.destroy()
249
+ await project.setProjectAccess(projectId, access) // account/team/organization/public
250
+ await project.setProjectVisibility(projectId, visibility) // public/private/password-protected
534
251
  ```
535
252
 
536
- ## Configuration Options
253
+ ### Branch Service
254
+
537
255
  ```javascript
538
- const options = {
539
- useNewServices: true,
540
- apiUrl: 'https://api.symbols.app',
541
- socketUrl: 'https://api.symbols.app',
542
- timeout: 30000,
543
- retryAttempts: 3,
544
- debug: false
545
- }
256
+ const branch = sdk.getService('branch')
546
257
 
547
- const sdk = new SDK(options)
548
- ```
258
+ await branch.listBranches(projectId)
259
+ await branch.createBranch(projectId, branchData)
260
+ await branch.deleteBranch(projectId, branchName)
261
+ await branch.renameBranch(projectId, branchName, newName)
262
+ await branch.getBranchChanges(projectId, branchName, options)
263
+ await branch.mergeBranch(projectId, branchName, mergeData)
264
+ await branch.resetBranch(projectId, branchName)
265
+ await branch.publishVersion(projectId, publishData)
549
266
 
550
- # Permissions System
267
+ // Helper methods
268
+ await branch.createFeatureBranch(projectId, featureName) // creates 'feature/<name>'
269
+ await branch.createHotfixBranch(projectId, hotfixName)
270
+ await branch.branchExists(projectId, branchName)
271
+ await branch.previewMerge(projectId, sourceBranch, targetBranch)
272
+ await branch.commitMerge(projectId, sourceBranch, options)
273
+ await branch.getBranchStatus(projectId, branchName)
274
+ await branch.deleteBranchSafely(projectId, branchName, options)
275
+ await branch.getBranchesWithStatus(projectId)
276
+ branch.validateBranchName(branchName)
277
+ branch.sanitizeBranchName(branchName)
278
+ ```
551
279
 
552
- ## Quick Start
280
+ ### Pull Request Service
553
281
 
554
282
  ```javascript
555
- // Check if user can edit a project
556
- const canEdit = sdk.hasPermission(projectId, 'edit')
283
+ const pr = sdk.getService('pullRequest')
557
284
 
558
- // Check if project has AI Copilot feature
559
- const hasCopilot = sdk.checkProjectFeature(projectTier, 'aiCopilot')
285
+ await pr.createPullRequest(projectId, pullRequestData)
286
+ await pr.listPullRequests(projectId, options)
287
+ await pr.getPullRequest(projectId, prId)
288
+ await pr.reviewPullRequest(projectId, prId, reviewData)
289
+ await pr.addPullRequestComment(projectId, prId, commentData)
290
+ await pr.mergePullRequest(projectId, prId)
291
+ await pr.getPullRequestDiff(projectId, prId)
292
+ await pr.closePullRequest(projectId, prId)
293
+ await pr.reopenPullRequest(projectId, prId)
560
294
 
561
- // Check if user has global admin access
562
- const isAdmin = sdk.hasGlobalPermission('admin', 'governance')
295
+ // Helper methods
296
+ await pr.approvePullRequest(projectId, prId, comment)
297
+ await pr.requestPullRequestChanges(projectId, prId, threads)
298
+ await pr.getOpenPullRequests(projectId, options)
299
+ await pr.getClosedPullRequests(projectId, options)
300
+ await pr.getMergedPullRequests(projectId, options)
301
+ await pr.isPullRequestMergeable(projectId, prId)
302
+ await pr.getPullRequestStatusSummary(projectId, prId)
303
+ await pr.getPullRequestStats(projectId, options)
563
304
  ```
564
305
 
565
- ## Permission Types
306
+ ### Collab Service
307
+
308
+ Real-time collaboration via WebSocket and Yjs.
566
309
 
567
- ### Core Permissions
568
- | Permission | Use Case | Required Permissions | Features |
569
- |------------|----------|---------------------|-----------|
570
- | edit | Edit content | editMode, showCode | editMode |
571
- | view | View content | showContent | canvasPages |
572
- | design | Access design tools | editMode, showCode | accessToSymbolsLibrary |
573
- | manage | Project settings | projectSettings, iam | workspaceAdministration |
310
+ ```javascript
311
+ const collab = sdk.getService('collab')
574
312
 
575
- ### Version Control
576
- | Permission | Use Case | Required Permissions | Features |
577
- |------------|----------|---------------------|-----------|
578
- | branch | Manage branches | versions | branching, versionHistory |
579
- | merge | Merge changes | versions | branching |
313
+ // Connection
314
+ await collab.connect({ projectId, branch, authToken })
315
+ collab.disconnect()
316
+ collab.isConnected()
317
+ collab.getConnectionInfo()
580
318
 
581
- ### AI Features
582
- | Permission | Use Case | Tier Limits |
583
- |------------|----------|-------------|
584
- | aiCopilot | AI assistance | Free: 3, Pro1: 5, Pro2: 15 |
585
- | aiChatbot | Chat support | Free: 3, Pro1: 5, Pro2: 15 |
319
+ // Data updates
320
+ collab.updateData(tuples, options)
321
+ collab.addItem(type, data, opts)
322
+ collab.addMultipleItems(items, opts)
323
+ collab.updateItem(type, data, opts)
324
+ collab.deleteItem(type, key, opts)
586
325
 
587
- ## Examples
326
+ // Undo/Redo
327
+ collab.undo()
328
+ collab.redo()
329
+ collab.canUndo()
330
+ collab.canRedo()
331
+ collab.getUndoStackSize()
332
+ collab.getRedoStackSize()
333
+ collab.clearUndoHistory()
334
+ collab.checkpoint()
588
335
 
589
- ### 1. Basic Permission Check
590
- ```javascript
591
- const projectId = '123'
592
- const canUserEdit = sdk.hasPermission(projectId, 'edit')
336
+ // Presence
337
+ collab.sendCursor(data)
338
+ collab.sendPresence(data)
339
+ collab.toggleLive(flag)
593
340
 
594
- if (canUserEdit) {
595
- // Allow editing
596
- sdk.enableEditMode()
597
- } else {
598
- // Show view-only mode
599
- sdk.enableViewMode()
600
- }
341
+ // Accessors
342
+ collab.ydoc // Yjs document
343
+ collab.socket // Socket instance
601
344
  ```
602
345
 
603
- ### 2. Feature Access by Tier
346
+ ### File Service
347
+
604
348
  ```javascript
605
- // Check AI feature access
606
- const projectTier = 'pro1'
607
- const copilotTokens = sdk.checkProjectFeature(projectTier, 'aiCopilot')
349
+ const file = sdk.getService('file')
608
350
 
609
- if (copilotTokens) {
610
- console.log(`User has access to ${copilotTokens} AI tokens`)
611
- } else {
612
- console.log('No AI access available')
613
- }
351
+ await file.uploadFile(file, options)
352
+ await file.updateProjectIcon(projectId, iconFile)
353
+ await file.uploadImage(imageFile, options)
354
+ await file.uploadDocument(documentFile, options)
355
+ await file.uploadMultipleFiles(files, options)
356
+ file.getFileUrl(fileId)
357
+ file.validateFile(file, options)
614
358
  ```
615
359
 
616
- ### 3. Role-Based Access
360
+ ### Payment Service
361
+
617
362
  ```javascript
618
- // Admin checking multiple permissions
619
- const isProjectAdmin = sdk.checkProjectPermission(
620
- userRoles,
621
- projectId,
622
- 'admin'
623
- )
363
+ const payment = sdk.getService('payment')
624
364
 
625
- if (isProjectAdmin) {
626
- // Can access admin features
627
- const canInvite = sdk.hasPermission(projectId, 'invite')
628
- const canManage = sdk.hasPermission(projectId, 'manage')
629
- }
365
+ await payment.checkout(options)
366
+ await payment.getSubscriptionStatus(projectId)
367
+ await payment.hasActiveSubscription(projectId)
368
+ await payment.getSubscriptionDetails(projectId)
369
+ await payment.checkoutForPlan(options)
370
+ await payment.checkoutForTeam(options)
371
+ await payment.getSubscriptionSummary(projectId)
630
372
  ```
631
373
 
632
- ### 4. Complex Permission Scenarios
374
+ ### Plan Service
375
+
633
376
  ```javascript
634
- // Check if user can perform branch merge
635
- const canMerge = projectId => {
636
- const hasPermission = sdk.hasPermission(projectId, 'merge')
637
- const isProtectedBranch = sdk.getBranchProtection(projectId)
377
+ const plan = sdk.getService('plan')
638
378
 
639
- if (isProtectedBranch) {
640
- return hasPermission && sdk.hasPermission(projectId, 'manage')
641
- }
379
+ // Public (no auth required)
380
+ await plan.getPlans()
381
+ await plan.getPlan(planId)
382
+ await plan.getPlansWithPricing()
383
+ await plan.getPlanByKey(key)
384
+ await plan.getActivePlans()
385
+ await plan.getPlansByPriceRange(minPrice, maxPrice)
642
386
 
643
- return hasPermission
644
- }
387
+ // Admin
388
+ await plan.getAdminPlans()
389
+ await plan.createPlan(planData)
390
+ await plan.updatePlan(planId, planData)
391
+ await plan.deletePlan(planId)
392
+ await plan.initializePlans()
645
393
  ```
646
394
 
647
- ## Role Permissions
395
+ ### Subscription Service
648
396
 
649
- ### Global Roles
650
397
  ```javascript
651
- const ROLE_PERMISSIONS = {
652
- guest: ['viewPublicProjects'],
653
- user: ['viewPublicProjects', 'createProject'],
654
- admin: ['viewPublicProjects', 'createProject', 'governance'],
655
- superAdmin: ['viewPublicProjects', 'createProject', 'governance', 'managePlatform']
656
- }
398
+ const subscription = sdk.getService('subscription')
399
+
400
+ await subscription.createSubscription(subscriptionData)
401
+ await subscription.getProjectStatus(projectId)
402
+ await subscription.getUsage(subscriptionId)
403
+ await subscription.cancelSubscription(subscriptionId)
404
+ await subscription.listInvoices(subscriptionId, options)
405
+ await subscription.getPortalUrl(projectId)
406
+ await subscription.getProjectSubscription(projectId)
407
+ await subscription.getProjectUsage(projectId)
408
+ await subscription.hasActiveSubscription(projectId)
409
+ await subscription.changeSubscription(projectId, planId)
410
+ await subscription.downgrade(projectId)
657
411
  ```
658
412
 
659
- ### Project Roles
413
+ ### DNS Service
414
+
660
415
  ```javascript
661
- const PROJECT_ROLE_PERMISSIONS = {
662
- guest: ['platformSettings', 'showContent'],
663
- editor: ['platformSettings', 'showContent', 'showCode', 'editMode', 'versions'],
664
- admin: [
665
- // Editor permissions +
666
- 'inviteMembers',
667
- 'branchProtection',
668
- 'projectSettings'
669
- ],
670
- owner: [
671
- // Admin permissions +
672
- 'copyPasteAllowanceSetting',
673
- 'iam'
674
- ]
675
- }
416
+ const dns = sdk.getService('dns')
417
+
418
+ await dns.createDnsRecord(domain, options)
419
+ await dns.getDnsRecord(domain)
420
+ await dns.removeDnsRecord(domain)
421
+ await dns.getCustomHost(hostname)
422
+ await dns.addProjectCustomDomains(projectId, customDomains, options)
423
+ await dns.isDomainAvailable(domain)
424
+ await dns.getDomainStatus(domain)
425
+ await dns.verifyDomainOwnership(domain)
426
+ await dns.getProjectDomains(projectId)
427
+ await dns.removeProjectCustomDomain(projectId, domain)
428
+ dns.validateDomain(domain)
429
+ dns.formatDomain(domain)
430
+ dns.extractDomainFromUrl(url)
676
431
  ```
677
432
 
678
- ## Common Use Cases
433
+ ### Admin Service
679
434
 
680
- 1. **Creating New Project**
681
435
  ```javascript
682
- if (sdk.hasGlobalPermission(userRole, 'createProject')) {
683
- const projectId = await sdk.createProject()
684
- await sdk.assignUserRole(userId, projectId, 'owner')
685
- }
436
+ const admin = sdk.getService('admin')
437
+
438
+ await admin.getAdminUsers(params)
439
+ await admin.updateUser(userId, userData)
440
+ await admin.searchAdminUsers(searchQuery, options)
441
+ await admin.getAdminUsersByEmails(emails)
442
+ await admin.getAdminUsersByIds(ids)
443
+ await admin.assignProjectsToUser(userId, options)
444
+ await admin.assignSpecificProjectsToUser(userId, projectIds, role)
445
+ await admin.assignAllProjectsToUser(userId, role)
446
+ await admin.getUserStats(userId)
447
+ await admin.bulkUpdateUsers(updates)
448
+ await admin.getUsersByRole(role)
449
+ await admin.getUsersByStatus(status)
450
+ await admin.activateUser(userId)
451
+ await admin.deactivateUser(userId)
452
+ await admin.suspendUser(userId)
453
+ await admin.promoteToAdmin(userId)
454
+ await admin.demoteFromAdmin(userId)
686
455
  ```
687
456
 
688
- 2. **Inviting Team Members**
457
+ ### Screenshot Service
458
+
689
459
  ```javascript
690
- const canInvite = sdk.hasPermission(projectId, 'invite')
691
- if (canInvite) {
692
- const tier = sdk.getProjectTier(projectId)
693
- const maxMembers = TIER_LIMITS[tier].teamMembers
694
- const currentSize = await sdk.getCurrentTeamSize()
460
+ const screenshot = sdk.getService('screenshot')
695
461
 
696
- if (currentSize < maxMembers) {
697
- await sdk.inviteTeamMember(email, 'editor')
698
- }
699
- }
462
+ await screenshot.createScreenshotProject(payload)
463
+ await screenshot.getProjectScreenshots(projectKey, params)
464
+ await screenshot.reprocessProjectScreenshots(projectKey, body)
465
+ await screenshot.recreateProjectScreenshots(projectKey, body)
466
+ await screenshot.deleteProjectScreenshots(projectKey)
467
+ await screenshot.getThumbnailCandidate(projectKey, options)
468
+ await screenshot.updateProjectThumbnail(projectKey, body)
469
+ await screenshot.refreshThumbnail(projectKey)
470
+ await screenshot.getPageScreenshot(screenshotId, format)
471
+ await screenshot.getComponentScreenshot(screenshotId, format)
472
+ await screenshot.getScreenshotByKey(key)
473
+ await screenshot.getQueueStatistics()
700
474
  ```
701
475
 
702
- 3. **Managing AI Features**
476
+ ### Tracking Service (Grafana Faro)
477
+
703
478
  ```javascript
704
- const handleAIFeature = async (projectId) => {
705
- const tier = await sdk.getProjectTier(projectId)
706
- const copilotAccess = sdk.checkProjectFeature(tier, 'aiCopilot')
479
+ const tracking = sdk.getService('tracking')
707
480
 
708
- if (copilotAccess) {
709
- const tokensLeft = await sdk.getAITokensRemaining(projectId)
710
- return {
711
- hasAccess: true,
712
- tokens: tokensLeft,
713
- maxTokens: copilotAccess
714
- }
715
- }
481
+ // Events
482
+ tracking.trackEvent('purchase', { amount: 42, currency: 'USD' })
483
+ tracking.trackError(new Error('Login failed'), { context: { screen: 'Login' } })
484
+ tracking.captureException(error, context)
485
+ tracking.trackMeasurement('cart_value', 42, { context: { currency: 'USD' } })
486
+ tracking.trackView('Dashboard', { section: 'Analytics' })
716
487
 
717
- return { hasAccess: false }
718
- }
719
- ```
488
+ // Logging
489
+ tracking.logMessage(message)
490
+ tracking.logDebug(message)
491
+ tracking.logInfo(message)
492
+ tracking.logWarning(message)
493
+ tracking.logError(message)
494
+ tracking.addBreadcrumb('Open modal', { id: 'planLimits' })
720
495
 
721
- 4. **Branch Protection**
722
- ```javascript
723
- const protectBranch = async (projectId, branchName) => {
724
- const canManage = sdk.hasPermission(projectId, 'manage')
725
- if (!canManage) return false
496
+ // User & Session
497
+ tracking.setUser({ id: 'u_123', role: 'admin' })
498
+ tracking.clearUser()
499
+ tracking.setSession(session)
500
+ tracking.clearSession()
726
501
 
727
- return sdk.setBranchProtection(projectId, branchName, {
728
- requireReview: true,
729
- requiredApprovals: 2,
730
- enforceAdminReview: true
731
- })
732
- }
502
+ // Global Attributes
503
+ tracking.setGlobalAttributes(attributes)
504
+ tracking.setGlobalAttribute(key, value)
505
+ tracking.removeGlobalAttribute(key)
506
+
507
+ // Queue & Status
508
+ tracking.flushQueue()
509
+ tracking.getClient()
510
+ tracking.isEnabled()
511
+ tracking.isInitialized()
733
512
  ```
734
513
 
735
- ## Error Handling
514
+ #### Tracking Configuration
736
515
 
737
516
  ```javascript
738
- try {
739
- const hasAccess = await sdk.hasPermission(projectId, 'edit')
740
- if (!hasAccess) {
741
- throw new Error('PERMISSION_DENIED')
742
- }
743
- await sdk.editProject(projectId)
744
- } catch (error) {
745
- switch (error.code) {
746
- case 'PERMISSION_DENIED':
747
- console.error('User lacks required permissions')
748
- break
749
- case 'TIER_LIMIT_EXCEEDED':
750
- console.error('Upgrade required for this feature')
751
- break
752
- default:
753
- console.error('Unexpected error:', error)
517
+ const sdk = new SDK({
518
+ tracking: {
519
+ url: 'https://<your-faro-receiver-url>',
520
+ appName: 'Symbols Platform',
521
+ environment: 'production',
522
+ appVersion: '1.0.0',
523
+ sessionTracking: true,
524
+ enableTracing: true,
525
+ globalAttributes: { region: 'us-east-1' }
754
526
  }
755
- }
527
+ })
756
528
  ```
757
529
 
758
- ## Token Management
530
+ Tracking is automatically disabled on localhost and in non-browser environments.
531
+
532
+ ### Waitlist Service
533
+
534
+ ```javascript
535
+ const waitlist = sdk.getService('waitlist')
759
536
 
760
- The SDK now includes automatic token management with persistence and refresh capabilities:
537
+ await waitlist.joinWaitlist(data) // public
538
+ await waitlist.listWaitlistEntries(options) // admin
539
+ await waitlist.updateWaitlistEntry(id, update) // admin
540
+ await waitlist.inviteWaitlistEntry(id) // admin
541
+ ```
761
542
 
762
- ### Features
763
- - **Automatic Token Refresh**: Tokens are refreshed automatically before expiration
764
- - **Persistent Storage**: Tokens persist across page refreshes using localStorage
765
- - **Secure Handling**: Automatic cleanup on logout and error handling
766
- - **Flexible Storage**: Supports localStorage, sessionStorage, or memory storage
543
+ ### Metrics Service
767
544
 
768
- ### Configuration
769
545
  ```javascript
770
- import { getTokenManager } from '@symbols/sdk'
546
+ const metrics = sdk.getService('metrics')
771
547
 
772
- // Configure token management (optional - handled automatically by CoreService)
773
- const tokenManager = getTokenManager({
774
- storageType: 'localStorage', // 'localStorage' | 'sessionStorage' | 'memory'
775
- refreshBuffer: 60 * 1000, // Refresh 1 minute before expiry
776
- apiUrl: '/api',
777
- onTokenRefresh: (tokens) => console.log('Token refreshed'),
778
- onTokenExpired: () => console.log('Session expired'),
779
- onTokenError: (error) => console.error('Token error:', error)
780
- })
548
+ await metrics.getContributions(options) // contribution heat-map stats
781
549
  ```
782
550
 
783
- ### Usage with CoreService
551
+ ### Integration Service
552
+
784
553
  ```javascript
785
- // Initialize SDK - token management is automatic
786
- const symbols = new Symbols({
787
- appKey: 'your-app-key',
788
- authToken: 'your-initial-token' // Optional
789
- })
554
+ const integration = sdk.getService('integration')
555
+
556
+ // Integration management
557
+ await integration.integrationWhoami(apiKey, options)
558
+ await integration.listIntegrations(options)
559
+ await integration.createIntegration(data)
560
+ await integration.updateIntegration(integrationId, update)
790
561
 
791
- // Login - tokens are automatically managed
792
- const loginResult = await symbols.login('user@example.com', 'password')
562
+ // API Keys
563
+ await integration.createIntegrationApiKey(integrationId, data)
564
+ await integration.listIntegrationApiKeys(integrationId)
565
+ await integration.revokeIntegrationApiKey(integrationId, keyId)
793
566
 
794
- // All subsequent API calls automatically use fresh tokens
795
- const projects = await symbols.getProjects()
796
- const projectData = await symbols.getProjectData('projectId123')
567
+ // Webhooks
568
+ await integration.createIntegrationWebhook(integrationId, data)
569
+ await integration.listIntegrationWebhooks(integrationId)
570
+ await integration.updateIntegrationWebhook(integrationId, webhookId, update)
571
+ await integration.deleteIntegrationWebhook(integrationId, webhookId)
572
+ await integration.listIntegrationWebhookDeliveries(integrationId, webhookId, options)
573
+ await integration.replayIntegrationWebhookDelivery(integrationId, webhookId, deliveryId)
797
574
 
798
- // Logout - tokens are automatically cleared
799
- await symbols.logout()
575
+ // GitHub Connectors
576
+ await integration.listGitHubConnectors()
577
+ await integration.createGitHubConnector(data)
578
+ await integration.updateGitHubConnector(connectorId, update)
579
+ await integration.deleteGitHubConnector(connectorId)
800
580
  ```
801
581
 
802
- ## New Core Service Methods
582
+ ### Feature Flag Service
803
583
 
804
- ### Project Data Management (Symstory Replacement)
805
584
  ```javascript
806
- // Apply changes to project
807
- await symbols.applyProjectChanges('projectId', [
808
- ['update', ['components', 'Button'], { color: 'blue' }],
809
- ['delete', ['pages', 'oldPage']]
810
- ], { message: 'Update button color', type: 'patch' })
585
+ const featureFlag = sdk.getService('featureFlag')
811
586
 
812
- // Get current project data
813
- const projectData = await symbols.getProjectData('projectId', {
814
- branch: 'main',
815
- includeHistory: true
816
- })
817
-
818
- // Restore to previous version
819
- await symbols.restoreProjectVersion('projectId', '1.2.0', {
820
- message: 'Rollback to stable version'
821
- })
587
+ // User-facing
588
+ await featureFlag.getFeatureFlags(params)
589
+ await featureFlag.getFeatureFlag(key)
822
590
 
823
- // Helper methods for single operations
824
- await symbols.updateProjectItem('projectId', ['components', 'Button'], { color: 'red' })
825
- await symbols.deleteProjectItem('projectId', ['pages', 'unused'])
826
- await symbols.setProjectValue('projectId', ['settings', 'theme'], 'dark')
591
+ // Admin
592
+ await featureFlag.getAdminFeatureFlags(params)
593
+ await featureFlag.createFeatureFlag(flagData)
594
+ await featureFlag.updateFeatureFlag(id, patch)
595
+ await featureFlag.archiveFeatureFlag(id)
827
596
  ```
828
597
 
829
- ### Pull Request Management
598
+ ---
599
+
600
+ ## Token Management
601
+
602
+ The SDK includes automatic token management with persistence and refresh:
603
+
604
+ - **Automatic Token Refresh** - tokens are refreshed before expiration
605
+ - **Persistent Storage** - tokens persist across page refreshes via localStorage
606
+ - **Secure Handling** - automatic cleanup on logout
607
+ - **Flexible Storage** - supports localStorage, sessionStorage, or memory
608
+
830
609
  ```javascript
831
- // Create pull request
832
- const pr = await symbols.createPullRequest('projectId', {
833
- source: 'feature/new-ui',
834
- target: 'main',
835
- title: 'Add new UI components',
836
- description: 'Modern UI overhaul'
837
- })
610
+ import { getTokenManager } from '@symbo.ls/sdk'
838
611
 
839
- // List pull requests
840
- const { pullRequests, pagination } = await symbols.listPullRequests('projectId', {
841
- status: 'open',
842
- page: 1,
843
- limit: 10
612
+ const tokenManager = getTokenManager({
613
+ storageType: 'localStorage', // 'localStorage' | 'sessionStorage' | 'memory'
614
+ refreshBuffer: 60 * 1000, // refresh 1 minute before expiry
615
+ apiUrl: '/api',
616
+ onTokenRefresh: (tokens) => { ... },
617
+ onTokenExpired: () => { ... },
618
+ onTokenError: (error) => { ... }
844
619
  })
620
+ ```
845
621
 
846
- // Review pull request
847
- await symbols.approvePullRequest('projectId', 'pr_123', 'Great work!')
622
+ All authenticated API calls automatically use fresh tokens via the BaseService `_request()` method.
848
623
 
849
- // Request changes
850
- await symbols.requestPullRequestChanges('projectId', 'pr_123', [
851
- {
852
- file: 'src/Button.js',
853
- line: 25,
854
- comment: 'Add error handling',
855
- type: 'issue'
856
- }
857
- ])
624
+ ---
625
+
626
+ ## Permissions
627
+
628
+ ### Role Permissions
858
629
 
859
- // Merge pull request
860
- await symbols.mergePullRequest('projectId', 'pr_123')
630
+ ```javascript
631
+ // Global roles
632
+ const ROLE_PERMISSIONS = {
633
+ guest: ['viewPublicProjects'],
634
+ user: ['viewPublicProjects', 'createProject'],
635
+ admin: ['viewPublicProjects', 'createProject', 'governance'],
636
+ superAdmin: ['viewPublicProjects', 'createProject', 'governance', 'managePlatform']
637
+ }
861
638
 
862
- // Get diff
863
- const diff = await symbols.getPullRequestDiff('projectId', 'pr_123')
639
+ // Project roles
640
+ const PROJECT_ROLE_PERMISSIONS = {
641
+ guest: ['platformSettings', 'showContent'],
642
+ editor: ['platformSettings', 'showContent', 'showCode', 'editMode', 'versions'],
643
+ admin: [/* editor + */ 'inviteMembers', 'branchProtection', 'projectSettings'],
644
+ owner: [/* admin + */ 'copyPasteAllowanceSetting', 'iam']
645
+ }
864
646
  ```
865
647
 
866
- ### Branch Management
648
+ ### Permission Checks
649
+
867
650
  ```javascript
868
- // List branches
869
- const branches = await symbols.listBranches('projectId')
651
+ auth.hasPermission('edit')
652
+ auth.hasGlobalPermission('admin', 'governance')
653
+ auth.checkProjectPermission('editor', 'editMode')
654
+ auth.checkProjectFeature('pro1', 'aiCopilot') // returns tier limit (e.g. 5)
655
+ await auth.canPerformOperation(projectId, 'edit', { checkFeatures: true })
656
+ await auth.withPermission(projectId, 'edit', () => { /* action */ })
657
+ ```
870
658
 
871
- // Create branch
872
- await symbols.createFeatureBranch('projectId', 'user authentication')
873
- // Creates: 'feature/user-authentication'
659
+ ### Tier Features
874
660
 
875
- // Check branch status
876
- const status = await symbols.getBranchStatus('projectId', 'feature/new-ui')
661
+ | Feature | Free | Pro1 | Pro2 |
662
+ |---------|------|------|------|
663
+ | aiCopilot | 3 | 5 | 15 |
664
+ | aiChatbot | 3 | 5 | 15 |
877
665
 
878
- // Preview merge
879
- const preview = await symbols.previewMerge('projectId', 'feature/new-ui', 'main')
880
- if (preview.data.conflicts.length === 0) {
881
- // Commit merge if no conflicts
882
- await symbols.commitMerge('projectId', 'feature/new-ui', {
883
- message: 'Add new UI features',
884
- type: 'minor'
885
- })
886
- }
666
+ ---
887
667
 
888
- // Delete branch safely
889
- await symbols.deleteBranchSafely('projectId', 'feature/old-feature')
668
+ ## Environment Configuration
890
669
 
891
- // Publish version
892
- await symbols.publishVersion('projectId', {
893
- version: '1.2.0',
894
- branch: 'main'
895
- })
670
+ The SDK auto-detects environment from hostname and provides:
671
+
672
+ ```javascript
673
+ import { environment } from '@symbo.ls/sdk'
674
+
675
+ environment.apiUrl // HTTP API URL
676
+ environment.socketUrl // WebSocket URL
677
+ environment.features // { trackingEnabled, betaFeatures, newUserOnboarding }
896
678
  ```
897
679
 
898
- ## Error Handling
680
+ Supported environments: local, development, testing, upcoming, staging, preview, production.
681
+
682
+ ---
899
683
 
900
- The SDK provides comprehensive error handling for all scenarios:
684
+ ## Error Handling
901
685
 
902
686
  ```javascript
903
687
  try {
904
- await symbols.mergePullRequest('projectId', 'pr_123')
688
+ await sdk.initialize()
689
+ } catch (error) {
690
+ console.error('SDK initialization failed:', error.message)
691
+ }
692
+
693
+ try {
694
+ await project.mergePullRequest(projectId, prId)
905
695
  } catch (error) {
906
696
  if (error.message.includes('conflicts')) {
907
697
  // Handle merge conflicts
908
- console.log('Manual conflict resolution required')
909
698
  } else if (error.message.includes('403')) {
910
699
  // Handle permission errors
911
- console.log('Insufficient permissions')
912
- } else {
913
- // Handle other errors
914
- console.error('Operation failed:', error.message)
915
700
  }
916
701
  }
917
702
  ```
918
703
 
704
+ ---
919
705
 
706
+ ## Direct Service Creation
707
+
708
+ Services can be created independently without the SDK class:
709
+
710
+ ```javascript
711
+ import { createAuthService, createProjectService } from '@symbo.ls/sdk'
712
+
713
+ const auth = createAuthService({ context, options })
714
+ await auth.init({ context, options })
715
+ ```
920
716
 
717
+ Available factory functions: `createAuthService`, `createCollabService`, `createProjectService`, `createPlanService`, `createFileService`, `createPaymentService`, `createDnsService`, `createBranchService`, `createPullRequestService`, `createAdminService`, `createSubscriptionService`, `createScreenshotService`, `createTrackingService`, `createWaitlistService`, `createMetricsService`, `createIntegrationService`, `createFeatureFlagService`.