@symbo.ls/sdk 3.1.2 → 3.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +143 -2
  2. package/dist/cjs/config/environment.js +98 -30
  3. package/dist/cjs/index.js +144 -24
  4. package/dist/cjs/services/AdminService.js +351 -0
  5. package/dist/cjs/services/AuthService.js +738 -305
  6. package/dist/cjs/services/BaseService.js +158 -6
  7. package/dist/cjs/services/BranchService.js +484 -0
  8. package/dist/cjs/services/CollabService.js +743 -0
  9. package/dist/cjs/services/DnsService.js +340 -0
  10. package/dist/cjs/services/FeatureFlagService.js +175 -0
  11. package/dist/cjs/services/FileService.js +201 -0
  12. package/dist/cjs/services/IntegrationService.js +538 -0
  13. package/dist/cjs/services/MetricsService.js +62 -0
  14. package/dist/cjs/services/PaymentService.js +271 -0
  15. package/dist/cjs/services/PlanService.js +426 -0
  16. package/dist/cjs/services/ProjectService.js +1207 -0
  17. package/dist/cjs/services/PullRequestService.js +503 -0
  18. package/dist/cjs/services/ScreenshotService.js +304 -0
  19. package/dist/cjs/services/SubscriptionService.js +396 -0
  20. package/dist/cjs/services/TrackingService.js +661 -0
  21. package/dist/cjs/services/WaitlistService.js +148 -0
  22. package/dist/cjs/services/index.js +64 -16
  23. package/dist/cjs/state/RootStateManager.js +65 -0
  24. package/dist/cjs/state/rootEventBus.js +74 -0
  25. package/dist/cjs/utils/CollabClient.js +223 -0
  26. package/dist/cjs/utils/TokenManager.js +78 -30
  27. package/dist/cjs/utils/changePreprocessor.js +199 -0
  28. package/dist/cjs/utils/jsonDiff.js +145 -0
  29. package/dist/cjs/utils/ordering.js +309 -0
  30. package/dist/cjs/utils/services.js +301 -103
  31. package/dist/cjs/utils/validation.js +0 -3
  32. package/dist/esm/config/environment.js +98 -30
  33. package/dist/esm/index.js +49505 -8718
  34. package/dist/esm/services/AdminService.js +1132 -0
  35. package/dist/esm/services/AuthService.js +1493 -386
  36. package/dist/esm/services/BaseService.js +757 -6
  37. package/dist/esm/services/BranchService.js +1265 -0
  38. package/dist/esm/services/CollabService.js +26895 -0
  39. package/dist/esm/services/DnsService.js +1121 -0
  40. package/dist/esm/services/FeatureFlagService.js +956 -0
  41. package/dist/esm/services/FileService.js +982 -0
  42. package/dist/esm/services/IntegrationService.js +1319 -0
  43. package/dist/esm/services/MetricsService.js +843 -0
  44. package/dist/esm/services/PaymentService.js +1052 -0
  45. package/dist/esm/services/PlanService.js +1207 -0
  46. package/dist/esm/services/ProjectService.js +2526 -0
  47. package/dist/esm/services/PullRequestService.js +1284 -0
  48. package/dist/esm/services/ScreenshotService.js +1085 -0
  49. package/dist/esm/services/SubscriptionService.js +1177 -0
  50. package/dist/esm/services/TrackingService.js +18454 -0
  51. package/dist/esm/services/WaitlistService.js +929 -0
  52. package/dist/esm/services/index.js +49062 -8569
  53. package/dist/esm/state/RootStateManager.js +90 -0
  54. package/dist/esm/state/rootEventBus.js +56 -0
  55. package/dist/esm/utils/CollabClient.js +18889 -0
  56. package/dist/esm/utils/TokenManager.js +78 -30
  57. package/dist/esm/utils/changePreprocessor.js +542 -0
  58. package/dist/esm/utils/jsonDiff.js +7011 -0
  59. package/dist/esm/utils/ordering.js +291 -0
  60. package/dist/esm/utils/services.js +301 -103
  61. package/dist/esm/utils/validation.js +116 -50
  62. package/dist/node/config/environment.js +98 -30
  63. package/dist/node/index.js +175 -32
  64. package/dist/node/services/AdminService.js +332 -0
  65. package/dist/node/services/AuthService.js +742 -310
  66. package/dist/node/services/BaseService.js +148 -6
  67. package/dist/node/services/BranchService.js +465 -0
  68. package/dist/node/services/CollabService.js +724 -0
  69. package/dist/node/services/DnsService.js +321 -0
  70. package/dist/node/services/FeatureFlagService.js +156 -0
  71. package/dist/node/services/FileService.js +182 -0
  72. package/dist/node/services/IntegrationService.js +519 -0
  73. package/dist/node/services/MetricsService.js +43 -0
  74. package/dist/node/services/PaymentService.js +252 -0
  75. package/dist/node/services/PlanService.js +407 -0
  76. package/dist/node/services/ProjectService.js +1188 -0
  77. package/dist/node/services/PullRequestService.js +484 -0
  78. package/dist/node/services/ScreenshotService.js +285 -0
  79. package/dist/node/services/SubscriptionService.js +377 -0
  80. package/dist/node/services/TrackingService.js +632 -0
  81. package/dist/node/services/WaitlistService.js +129 -0
  82. package/dist/node/services/index.js +64 -16
  83. package/dist/node/state/RootStateManager.js +36 -0
  84. package/dist/node/state/rootEventBus.js +55 -0
  85. package/dist/node/utils/CollabClient.js +194 -0
  86. package/dist/node/utils/TokenManager.js +78 -30
  87. package/dist/node/utils/changePreprocessor.js +180 -0
  88. package/dist/node/utils/jsonDiff.js +116 -0
  89. package/dist/node/utils/ordering.js +290 -0
  90. package/dist/node/utils/services.js +301 -103
  91. package/dist/node/utils/validation.js +0 -3
  92. package/package.json +39 -21
  93. package/src/config/environment.js +99 -28
  94. package/src/index.js +181 -36
  95. package/src/services/AdminService.js +374 -0
  96. package/src/services/AuthService.js +874 -328
  97. package/src/services/BaseService.js +166 -6
  98. package/src/services/BranchService.js +536 -0
  99. package/src/services/CollabService.js +900 -0
  100. package/src/services/DnsService.js +366 -0
  101. package/src/services/FeatureFlagService.js +174 -0
  102. package/src/services/FileService.js +213 -0
  103. package/src/services/IntegrationService.js +548 -0
  104. package/src/services/MetricsService.js +40 -0
  105. package/src/services/PaymentService.js +287 -0
  106. package/src/services/PlanService.js +468 -0
  107. package/src/services/ProjectService.js +1366 -0
  108. package/src/services/PullRequestService.js +537 -0
  109. package/src/services/ScreenshotService.js +258 -0
  110. package/src/services/SubscriptionService.js +425 -0
  111. package/src/services/TrackingService.js +853 -0
  112. package/src/services/WaitlistService.js +130 -0
  113. package/src/services/index.js +80 -13
  114. package/src/services/tests/BranchService/createBranch.test.js +153 -0
  115. package/src/services/tests/BranchService/deleteBranch.test.js +173 -0
  116. package/src/services/tests/BranchService/getBranchChanges.test.js +146 -0
  117. package/src/services/tests/BranchService/listBranches.test.js +87 -0
  118. package/src/services/tests/BranchService/mergeBranch.test.js +210 -0
  119. package/src/services/tests/BranchService/publishVersion.test.js +183 -0
  120. package/src/services/tests/BranchService/renameBranch.test.js +240 -0
  121. package/src/services/tests/BranchService/resetBranch.test.js +152 -0
  122. package/src/services/tests/FeatureFlagService/adminFeatureFlags.test.js +67 -0
  123. package/src/services/tests/FeatureFlagService/getFeatureFlags.test.js +75 -0
  124. package/src/services/tests/FileService/createFileFormData.test.js +74 -0
  125. package/src/services/tests/FileService/getFileUrl.test.js +69 -0
  126. package/src/services/tests/FileService/updateProjectIcon.test.js +109 -0
  127. package/src/services/tests/FileService/uploadDocument.test.js +36 -0
  128. package/src/services/tests/FileService/uploadFile.test.js +78 -0
  129. package/src/services/tests/FileService/uploadFileWithValidation.test.js +114 -0
  130. package/src/services/tests/FileService/uploadImage.test.js +36 -0
  131. package/src/services/tests/FileService/uploadMultipleFiles.test.js +111 -0
  132. package/src/services/tests/FileService/validateFile.test.js +63 -0
  133. package/src/services/tests/PlanService/createPlan.test.js +104 -0
  134. package/src/services/tests/PlanService/createPlanWithValidation.test.js +523 -0
  135. package/src/services/tests/PlanService/deletePlan.test.js +92 -0
  136. package/src/services/tests/PlanService/getActivePlans.test.js +123 -0
  137. package/src/services/tests/PlanService/getAdminPlans.test.js +84 -0
  138. package/src/services/tests/PlanService/getPlan.test.js +50 -0
  139. package/src/services/tests/PlanService/getPlanByKey.test.js +109 -0
  140. package/src/services/tests/PlanService/getPlanWithValidation.test.js +85 -0
  141. package/src/services/tests/PlanService/getPlans.test.js +53 -0
  142. package/src/services/tests/PlanService/getPlansByPriceRange.test.js +109 -0
  143. package/src/services/tests/PlanService/getPlansWithValidation.test.js +48 -0
  144. package/src/services/tests/PlanService/initializePlans.test.js +75 -0
  145. package/src/services/tests/PlanService/updatePlan.test.js +111 -0
  146. package/src/services/tests/PlanService/updatePlanWithValidation.test.js +556 -0
  147. package/src/state/RootStateManager.js +76 -0
  148. package/src/state/rootEventBus.js +67 -0
  149. package/src/utils/CollabClient.js +248 -0
  150. package/src/utils/TokenManager.js +88 -33
  151. package/src/utils/changePreprocessor.js +239 -0
  152. package/src/utils/jsonDiff.js +144 -0
  153. package/src/utils/ordering.js +271 -0
  154. package/src/utils/services.js +326 -107
  155. package/src/utils/validation.js +0 -3
  156. package/dist/cjs/services/AIService.js +0 -155
  157. package/dist/cjs/services/BasedService.js +0 -1185
  158. package/dist/cjs/services/CoreService.js +0 -1751
  159. package/dist/cjs/services/SocketIOService.js +0 -307
  160. package/dist/cjs/services/SocketService.js +0 -161
  161. package/dist/cjs/services/SymstoryService.js +0 -571
  162. package/dist/cjs/utils/basedQuerys.js +0 -181
  163. package/dist/cjs/utils/symstoryClient.js +0 -259
  164. package/dist/esm/services/AIService.js +0 -185
  165. package/dist/esm/services/BasedService.js +0 -5278
  166. package/dist/esm/services/CoreService.js +0 -2264
  167. package/dist/esm/services/SocketIOService.js +0 -470
  168. package/dist/esm/services/SocketService.js +0 -191
  169. package/dist/esm/services/SymstoryService.js +0 -7041
  170. package/dist/esm/utils/basedQuerys.js +0 -163
  171. package/dist/esm/utils/symstoryClient.js +0 -370
  172. package/dist/node/services/AIService.js +0 -136
  173. package/dist/node/services/BasedService.js +0 -1156
  174. package/dist/node/services/CoreService.js +0 -1722
  175. package/dist/node/services/SocketIOService.js +0 -278
  176. package/dist/node/services/SocketService.js +0 -142
  177. package/dist/node/services/SymstoryService.js +0 -542
  178. package/dist/node/utils/basedQuerys.js +0 -162
  179. package/dist/node/utils/symstoryClient.js +0 -230
  180. package/src/services/AIService.js +0 -150
  181. package/src/services/BasedService.js +0 -1301
  182. package/src/services/CoreService.js +0 -1943
  183. package/src/services/SocketIOService.js +0 -334
  184. package/src/services/SocketService.js +0 -168
  185. package/src/services/SymstoryService.js +0 -649
  186. package/src/utils/basedQuerys.js +0 -164
  187. package/src/utils/symstoryClient.js +0 -252
@@ -1,1301 +0,0 @@
1
- import { BaseService } from './BaseService.js'
2
- import { buildProjectQuery, buildUserQuery, buildGetProjectsByKeysQuery, buildGetUserDataQuery, buildGetProjectDataQuery, buildGetProjectByKeyDataQuery } from '../utils/basedQuerys.js'
3
- import Based from '@based/client'
4
- import { isFunction, isString } from '@domql/utils'
5
- import environment from '../config/environment.js'
6
-
7
- export class BasedService extends BaseService {
8
- constructor (config) {
9
- super(config)
10
- this._client = null
11
- this._subscriptions = new Map()
12
- }
13
-
14
- init ({ context }) {
15
- try {
16
- const { env, org, project } = context.based || {
17
- env: environment.basedEnv,
18
- org: environment.basedOrg,
19
- project: environment.basedProject
20
- }
21
-
22
- if (!env || !org || !project) {
23
- throw new Error('Based configuration missing required parameters')
24
- }
25
-
26
- this._client = new Based({
27
- env,
28
- org,
29
- project
30
- })
31
-
32
- this._info = {
33
- config: {
34
- env,
35
- org: this._maskString(org),
36
- project: this._maskString(project)
37
- }
38
- }
39
-
40
- this._setReady()
41
- } catch (error) {
42
- this._setError(error)
43
- throw error
44
- }
45
- }
46
-
47
- // Helper method to mask sensitive strings
48
-
49
- _maskString (str) {
50
- if (!str) {
51
- return ''
52
- }
53
- return `${str.substr(0, 4)}...${str.substr(-4)}`
54
- }
55
-
56
- updateContext (context) {
57
- this._context = { ...this._context, ...context }
58
- if (this._context.authToken && this._context.user?.id) {
59
- this.setAuthState(this._context.authToken, this._context.user.id)
60
- }
61
- }
62
-
63
- async setAuthState (authState) {
64
- this._requireReady()
65
- const newAuthState = {
66
- ...authState,
67
- persistent: true
68
- }
69
- try {
70
- return await this._client.setAuthState(newAuthState)
71
- } catch (error) {
72
- throw new Error(`Failed to set auth state: ${error.message}`)
73
- }
74
- }
75
-
76
- async setBucket (bucketId, callback) {
77
- this._requireReady()
78
-
79
- if (!isString(bucketId)) {
80
- throw new Error('Invalid type of bucket ID', bucketId)
81
- }
82
- if (!this._context.project) {
83
- throw new Error('Project is undefined')
84
- }
85
- try {
86
- const obj = {
87
- $id: this._context.project.id, // TODO: change to getProjectId
88
- bucket: bucketId
89
- }
90
- if (isFunction(callback)) {
91
- const data = await this._client.call('db:set', obj)
92
- return callback(data)
93
- }
94
- return await this._client.call('db:set', obj)
95
- } catch (error) {
96
- throw new Error(`Failed to set bucket: ${error.message}`)
97
- }
98
- }
99
-
100
- async setUserForced (userId, obj) {
101
- this._requireReady()
102
-
103
- try {
104
- await this._client
105
- .query('db', {
106
- $id: obj.projectId,
107
- members: {
108
- $list: true,
109
- user: {
110
- id: true
111
- }
112
- }
113
- })
114
- .get()
115
-
116
- // Create ProjectMember entry
117
- const { id: membershipId } = await this._client.call('db:set', {
118
- type: 'projectMember',
119
- user: userId,
120
- project: obj.projectId,
121
- role: obj.role,
122
- joinedAt: Date.now()
123
- })
124
-
125
- return await Promise.all([
126
- this._client.call('db:set', {
127
- $id: userId,
128
- memberProjects: { $add: membershipId }
129
- }),
130
- this._client.call('db:set', {
131
- $id: obj.projectId,
132
- members: { $add: membershipId }
133
- })
134
- ])
135
- } catch (error) {
136
- throw new Error(`Failed to set bucket: ${error.message}`)
137
- }
138
- }
139
-
140
- async query (collection, query, options = {}) {
141
- this._requireReady()
142
- try {
143
- return await this._client
144
- .query(collection, query, {
145
- ...options,
146
- context: this._context
147
- })
148
- .get()
149
- } catch (error) {
150
- throw new Error(`Query failed: ${error.message}`)
151
- }
152
- }
153
-
154
- async setProject (params) {
155
- this._requireReady()
156
- try {
157
- return await this._client.call('db:set', {
158
- type: 'project',
159
- ...params
160
- })
161
- } catch (error) {
162
- throw new Error(`Query failed: ${error.message}`)
163
- }
164
- }
165
-
166
- async setUser (params) {
167
- this._requireReady()
168
- try {
169
- return await this._client.call('db:set', {
170
- type: 'user',
171
- ...params
172
- })
173
- } catch (error) {
174
- throw new Error(`Query failed: ${error.message}`)
175
- }
176
- }
177
-
178
- // DEPRECATED
179
- async fetchUser (userId) {
180
- if (!userId) {
181
- throw new Error('User Id is required')
182
- }
183
- return await this.query('db', buildUserQuery(userId))
184
- }
185
-
186
- async getUser (userId) {
187
- if (!userId) {
188
- throw new Error("UserId is required")
189
- }
190
-
191
- // Get user data and memberProjects in a single query
192
- const userData = await this._client
193
- .query("db", buildGetUserDataQuery(userId))
194
- .get()
195
-
196
- // Extract user data
197
- const user = {
198
- id: userData.id,
199
- name: userData.name,
200
- email: userData.email,
201
- username: userData.username,
202
- globalRole: userData.globalRole,
203
- createdAt: userData.createdAt,
204
- updatedAt: userData.updatedAt
205
- }
206
-
207
- if (!user) {
208
- throw new Error("User not found")
209
- }
210
-
211
- let memberProjects = []
212
-
213
- if (userData.memberProjects && userData.memberProjects.length > 0) {
214
- // Extract project keys from the direct query
215
- const projectKeys = userData.memberProjects
216
- .filter(membership => membership.project && membership.project.key)
217
- .map(membership => membership.project.key)
218
-
219
- if (projectKeys.length > 0) {
220
- // Fetch projects in chunks to avoid PayloadTooLarge errors
221
- const allProjects = await this._fetchProjectsByKeysInChunks(projectKeys)
222
-
223
- // Map the project data with original membership data
224
- memberProjects = userData.memberProjects
225
- .filter(membership => membership.project && membership.project.key)
226
- .map(membership => {
227
- const projectKey = membership.project.key
228
- const correctProject = allProjects.find(
229
- p => p.key === projectKey
230
- )
231
-
232
- return {
233
- project: correctProject || membership.project,
234
- role: membership.role,
235
- updatedAt: membership.updatedAt,
236
- createdAt: membership.createdAt
237
- }
238
- })
239
- }
240
- } else {
241
- console.log(`[getUser] No member projects found with ID: ${userId}`)
242
- }
243
-
244
- // Format projects data
245
- const formattedProjects =
246
- memberProjects
247
- .filter(membership => membership.project)
248
- .map(membership => ({
249
- id: membership.project.id,
250
- name: membership.project.name,
251
- key: membership.project.key,
252
- thumbnail: membership.project.thumbnail,
253
- icon: membership.project.icon,
254
- tier: membership.project.tier,
255
- visibility: membership.project.visibility,
256
- access: membership.project.access,
257
- role: membership.role,
258
- joinedAt: membership.createdAt,
259
- updatedAt: membership.updatedAt,
260
- members: membership.project.members
261
- })) || []
262
-
263
- return {
264
- id: userId,
265
- name: user.name,
266
- email: user.email,
267
- username: user.username,
268
- globalRole: user.globalRole,
269
- projects: formattedProjects,
270
- createdAt: user.createdAt,
271
- updatedAt: user.updatedAt
272
- }
273
- }
274
-
275
- /**
276
- * Fetches projects by keys in chunks with adaptive chunk sizing
277
- * @param {Array<string>} allKeys - All project keys to fetch
278
- * @returns {Promise<Array>} - All projects found
279
- */
280
- async _fetchProjectsByKeysInChunks(allKeys) {
281
- if (!allKeys.length) {return []}
282
-
283
- // Initial chunk size and limits
284
- const INITIAL_CHUNK_SIZE = 50
285
- const MAX_CHUNK_SIZE = 1000
286
- const MIN_CHUNK_SIZE = 10
287
-
288
- // Track the optimal chunk size as we learn from responses
289
- const optimalChunkSize = INITIAL_CHUNK_SIZE
290
-
291
- /**
292
- * Recursively process chunks of keys with adaptive sizing
293
- * @param {Array<string>} keys - Keys to process
294
- * @param {number} size - Current chunk size
295
- * @returns {Promise<Array>} - Projects found
296
- */
297
- const processChunks = async (keys, size) => {
298
- if (!keys.length) {return []}
299
-
300
- const chunks = []
301
- for (let i = 0; i < keys.length; i += size) {
302
- chunks.push(keys.slice(i, i + size))
303
- }
304
-
305
- try {
306
- const result = await this._client
307
- .query("db", buildGetProjectsByKeysQuery(chunks[0]))
308
- .get()
309
-
310
- const newSize = Math.min(Math.floor(size * 1.5), MAX_CHUNK_SIZE)
311
-
312
- const firstChunkProjects = result.projects || []
313
-
314
- const remainingKeys = keys.slice(chunks[0].length)
315
- const remainingProjects = await processChunks(remainingKeys, newSize)
316
-
317
- return [...firstChunkProjects, ...remainingProjects]
318
- } catch (error) {
319
- if (error.message && error.message.includes('PayloadTooLarge')) {
320
- const newSize = Math.max(Math.floor(size / 2), MIN_CHUNK_SIZE)
321
- console.warn(`Reducing chunk size to ${newSize} due to PayloadTooLarge error`)
322
-
323
- if (newSize === MIN_CHUNK_SIZE && chunks[0].length <= MIN_CHUNK_SIZE) {
324
- console.error(`Cannot process chunk, skipping ${chunks[0].length} keys`)
325
- const remainingKeys = keys.slice(chunks[0].length)
326
- return processChunks(remainingKeys, newSize)
327
- }
328
-
329
- return processChunks(keys, newSize)
330
- }
331
- console.error(`Error fetching projects: ${error.message}`)
332
- const remainingKeys = keys.slice(chunks[0].length)
333
- return processChunks(remainingKeys, size)
334
-
335
- }
336
- }
337
-
338
- return await processChunks(allKeys, optimalChunkSize)
339
- }
340
-
341
- async getUserByEmail (email) {
342
- this._requireReady()
343
- if (!email) {
344
- throw new Error('Email is required')
345
- }
346
- try {
347
- return await this.call('users:get-by', { email })
348
- } catch (error) {
349
- throw new Error(`Failed to get user: ${error.message}`)
350
- }
351
- }
352
-
353
- async setProjectDomains (projectId, domains) {
354
- this._requireReady()
355
- if (!projectId) {
356
- throw new Error('Project ID is required')
357
- }
358
- try {
359
- return await this.call('projects:update-domains', { projectId, domains })
360
- } catch (error) {
361
- throw new Error(`Failed to set project domains: ${error.message}`)
362
- }
363
- }
364
-
365
- async checkProjectKeyAvailability (key) {
366
- this._requireReady()
367
- try {
368
- return await this.call('projects:check-key', { key })
369
- } catch (error) {
370
- throw new Error(
371
- `Failed to check project key availability: ${error.message}`
372
- )
373
- }
374
- }
375
-
376
- async removeProject (projectId) {
377
- this._requireReady()
378
- if (!projectId) {
379
- throw new Error('Project ID is required')
380
- }
381
- try {
382
- return await this.call('projects:remove', { projectId })
383
- } catch (error) {
384
- throw new Error(`Failed to remove project: ${error.message}`)
385
- }
386
- }
387
-
388
- async getAvailableLibraries (params) {
389
- this._requireReady()
390
- const defaultParams = {
391
- page: 1,
392
- limit: 20,
393
- search: '',
394
- framework: '',
395
- language: ''
396
- }
397
- try {
398
- return await this.call('projects:get-available-libraries', {
399
- ...defaultParams,
400
- ...params
401
- })
402
- } catch (error) {
403
- throw new Error(`Failed to get available libraries: ${error.message}`)
404
- }
405
- }
406
-
407
- async addProjectLibraries (projectId, libraryIds) {
408
- this._requireReady()
409
- if (!projectId) {
410
- throw new Error('Project ID is required')
411
- }
412
- try {
413
- return await this.call('projects:add-libraries', {
414
- projectId,
415
- libraryIds
416
- })
417
- } catch (error) {
418
- throw new Error(`Failed to add project libraries: ${error.message}`)
419
- }
420
- }
421
-
422
- async removeProjectLibraries (projectId, libraryIds) {
423
- this._requireReady()
424
- if (!projectId) {
425
- throw new Error('Project ID is required')
426
- }
427
- try {
428
- return await this.call('projects:remove-libraries', {
429
- projectId,
430
- libraryIds
431
- })
432
- } catch (error) {
433
- throw new Error(`Failed to remove project libraries: ${error.message}`)
434
- }
435
- }
436
-
437
- async getProjectLibraries (projectId) {
438
- this._requireReady()
439
- if (!projectId) {
440
- throw new Error('Project ID is required')
441
- }
442
- try {
443
- return await this.call('projects:get-libraries', { projectId })
444
- } catch (error) {
445
- throw new Error(`Failed to get project libraries: ${error.message}`)
446
- }
447
- }
448
-
449
- subscribe (collection, query, callback, options = {}) {
450
- this._requireReady()
451
- const subscriptionKey = JSON.stringify({ collection, query })
452
-
453
- if (this._subscriptions.has(subscriptionKey)) {
454
- return this._subscriptions.get(subscriptionKey)
455
- }
456
-
457
- try {
458
- const unsubscribe = this._client
459
- .query(collection, query, {
460
- ...options,
461
- context: this._context
462
- })
463
- .subscribe(callback)
464
-
465
- this._subscriptions.set(subscriptionKey, unsubscribe)
466
-
467
- return () => {
468
- unsubscribe()
469
- this._subscriptions.delete(subscriptionKey)
470
- }
471
- } catch (error) {
472
- throw new Error(`Subscription failed: ${error.message}`)
473
- }
474
- }
475
-
476
- subscribeChannel (name, params, callback) {
477
- this._requireReady()
478
- try {
479
- return this._client.channel(name, params).subscribe(callback)
480
- } catch (error) {
481
- throw new Error(`Channel subscription failed: ${error.message}`)
482
- }
483
- }
484
-
485
- publishToChannel (name, params, data) {
486
- this._requireReady()
487
- try {
488
- return this._client.channel(name, params).publish(data)
489
- } catch (error) {
490
- throw new Error(`Channel publish failed: ${error.message}`)
491
- }
492
- }
493
-
494
- async call (functionName, params = {}) {
495
- this._requireReady()
496
- try {
497
- return await this._client.call(functionName, params)
498
- } catch (error) {
499
- throw new Error(`Function call failed: ${error.message}`)
500
- }
501
- }
502
-
503
- async updateSchema (schema) {
504
- this._requireReady()
505
- try {
506
- return await this._client.call('db:update-schema', schema)
507
- } catch (error) {
508
- throw new Error(`Schema update failed: ${error.message}`)
509
- }
510
- }
511
-
512
- async createProject (projectData) {
513
- this._requireReady()
514
-
515
- try {
516
- return await this.call('projects:create', projectData)
517
- } catch (error) {
518
- throw new Error(`Failed to create project: ${error.message}`)
519
- }
520
- }
521
-
522
- async getProject (projectId) {
523
- this._requireReady()
524
- if (!projectId) {
525
- throw new Error('Project ID is required')
526
- }
527
- try {
528
- return await this._client
529
- .query("db", buildGetProjectDataQuery(projectId))
530
- .get()
531
- } catch (error) {
532
- throw new Error(`Failed to get project: ${error.message}`)
533
- }
534
- }
535
-
536
- async getProjectByKey (key) {
537
- this._requireReady()
538
- try {
539
- return await this._client
540
- .query("db", buildGetProjectByKeyDataQuery(key))
541
- .get()
542
- } catch (error) {
543
- throw new Error(`Failed to get project by key: ${error.message}`)
544
- }
545
- }
546
-
547
- // DEPRECATED
548
- async fetchProject (projectId) {
549
- this._requireReady()
550
- if (!projectId) {
551
- throw new Error('Project ID is required')
552
- }
553
- return await this.query('db', buildProjectQuery(projectId))
554
- }
555
-
556
- async chooseProject ({
557
- username = this._context.user?.username,
558
- projectId,
559
- activeProject
560
- }) {
561
- this._requireReady()
562
- try {
563
- await this.call('fetchProject', {
564
- username,
565
- projectId,
566
- activeProject
567
- })
568
- await this._client.call('setCookie', 'activeProject', activeProject)
569
- } catch (error) {
570
- throw new Error(`Failed to choose project: ${error.message}`)
571
- }
572
- }
573
-
574
- destroy () {
575
- for (const unsubscribe of this._subscriptions.values()) {
576
- unsubscribe()
577
- }
578
- this._subscriptions.clear()
579
- this._client = null
580
- this._setReady(false)
581
- }
582
-
583
- // New helper methods for state management
584
- async updateState (changes) {
585
- if (!changes || Object.keys(changes).length === 0) {
586
- return
587
- }
588
-
589
- try {
590
- await this._client.call('state:update', changes)
591
- } catch (error) {
592
- throw new Error(`Failed to update state: ${error.message}`)
593
- }
594
- }
595
-
596
- async getState () {
597
- try {
598
- return await this._client.call('state:get')
599
- } catch (error) {
600
- throw new Error(`Failed to get state: ${error.message}`)
601
- }
602
- }
603
-
604
- /**
605
- * Upload a file to the database
606
- * @param {File} file - The file to upload
607
- * @param {Object} options - The options for the upload
608
- * @returns {Promise<string>} The source of the uploaded file
609
- * @example
610
- * const fileInput = document.querySelector('input[type="file"]')
611
- * const file = fileInput.files[0]
612
- * const { id, src } = await basedService.uploadFile(file)
613
- */
614
- async uploadFile (file, options = {}) {
615
- this._requireReady()
616
- if (!file) {
617
- throw new Error('File is required for upload')
618
- }
619
- try {
620
- const { id, src } = await this._client.stream('db:file-upload', {
621
- contents: file,
622
- ...options
623
- })
624
- return { id, src }
625
- } catch (error) {
626
- throw new Error(`File upload failed: ${error.message}`)
627
- }
628
- }
629
-
630
- async checkout ({
631
- projectId,
632
- pkg = 2,
633
- seats = 1,
634
- interval = 'monthly',
635
- plan = 'startup',
636
- successUrl = `${window.location.origin}/success`,
637
- cancelUrl = `${window.location.origin}/pricing`
638
- }) {
639
- this._requireReady()
640
-
641
- const prices = {
642
- 999: { startup: 'unlimited_startup', agency: 'unlimited_agency' },
643
- 2: { monthly: 'starter_monthly', yearly: 'starter_yearly' },
644
- 3: { monthly: 'growth_monthly', yearly: 'growth_yearly' }
645
- }
646
-
647
- if (!projectId) {
648
- throw new Error('Project ID is required for checkout')
649
- }
650
-
651
- if (!prices[pkg]) {
652
- throw new Error(`Invalid package type: ${pkg}`)
653
- }
654
-
655
- try {
656
- const price = prices[pkg][interval] || prices[pkg][plan]
657
- if (!price) {
658
- throw new Error(`Invalid interval/plan combination for package ${pkg}`)
659
- }
660
-
661
- return await this.call('checkout', {
662
- price,
663
- seats,
664
- projectId,
665
- successUrl,
666
- cancelUrl
667
- })
668
- } catch (error) {
669
- throw new Error(`Failed to checkout: ${error.message}`)
670
- }
671
- }
672
-
673
- async updateProject (projectId, data) {
674
- this._requireReady()
675
- try {
676
- return await this.call('projects:update', { projectId, data })
677
- } catch (error) {
678
- throw new Error(`Failed to update project: ${error.message}`)
679
- }
680
- }
681
-
682
- async updateProjectComponents (projectId, components) {
683
- this._requireReady()
684
- try {
685
- return await this.call('projects:update-components', {
686
- projectId,
687
- components
688
- })
689
- } catch (error) {
690
- throw new Error(`Failed to update project components: ${error.message}`)
691
- }
692
- }
693
-
694
- async updateProjectSettings (projectId, settings) {
695
- this._requireReady()
696
- try {
697
- return await this.call('projects:update-settings', {
698
- projectId,
699
- settings
700
- })
701
- } catch (error) {
702
- throw new Error(`Failed to update project settings: ${error.message}`)
703
- }
704
- }
705
-
706
- async updateProjectName (projectId, name) {
707
- this._requireReady()
708
- try {
709
- return await this.call('projects:update', { projectId, name })
710
- } catch (error) {
711
- throw new Error(`Failed to update project name: ${error.message}`)
712
- }
713
- }
714
-
715
- async updateProjectPackage (projectId, pkg) {
716
- this._requireReady()
717
- try {
718
- return await this.call('projects:update', {
719
- projectId,
720
- data: { package: pkg }
721
- })
722
- } catch (error) {
723
- throw new Error(`Failed to update project package: ${error.message}`)
724
- }
725
- }
726
-
727
- /**
728
- * Update the icon of a project
729
- * @param {string} projectId - The ID of the project to update
730
- * @param {id, src} icon - The icon to update the project with
731
- * @returns {Promise<Object>} The updated project
732
- */
733
- async updateProjectIcon (projectId, icon) {
734
- this._requireReady()
735
- try {
736
- return await this.call('projects:update', {
737
- projectId,
738
- data: {
739
- icon: {
740
- $id: icon.id,
741
- src: icon.src
742
- }
743
- }
744
- })
745
- } catch (error) {
746
- throw new Error(`Failed to update project icon: ${error.message}`)
747
- }
748
- }
749
-
750
- async createDnsRecord (domain) {
751
- this._requireReady()
752
- try {
753
- return await this.call('dns:create-record', { domain })
754
- } catch (error) {
755
- throw new Error(`Failed to create DNS record: ${error.message}`)
756
- }
757
- }
758
-
759
- async getDnsRecord (domain) {
760
- this._requireReady()
761
- try {
762
- return await this.call('dns:get-record', { domain })
763
- } catch (error) {
764
- throw new Error(`Failed to get DNS records: ${error.message}`)
765
- }
766
- }
767
-
768
- async removeDnsRecord (domain) {
769
- this._requireReady()
770
- try {
771
- return await this.call('dns:remove-record', { domain })
772
- } catch (error) {
773
- throw new Error(`Failed to delete DNS record: ${error.message}`)
774
- }
775
- }
776
-
777
- async createStorageBucket (key) {
778
- this._requireReady()
779
- try {
780
- const randomString = Math.random().toString(36).slice(2, 15)
781
- const bucket = `symbols-bucket-${key}-${randomString}`
782
- return await this.call('storage:create-bucket', {
783
- bucketName: bucket,
784
- clientName: key.split('.')[0]
785
- })
786
- } catch (error) {
787
- throw new Error(`Failed to create storage bucket: ${error.message}`)
788
- }
789
- }
790
-
791
- async getStorageBucket (bucketName) {
792
- this._requireReady()
793
- try {
794
- return await this.call('storage:get-bucket', { bucketName })
795
- } catch (error) {
796
- throw new Error(`Failed to get storage bucket: ${error.message}`)
797
- }
798
- }
799
-
800
- async removeStorageBucket (bucketName) {
801
- this._requireReady()
802
- try {
803
- return await this.call('storage:remove-bucket', { bucketName })
804
- } catch (error) {
805
- throw new Error(`Failed to remove storage bucket: ${error.message}`)
806
- }
807
- }
808
-
809
- /**
810
- * Request a password change
811
- * @returns {Promise<boolean>} True if the request was successful, false otherwise
812
- */
813
- async requestPasswordChange () {
814
- this._requireReady()
815
- try {
816
- return await this.call('users:request-password-change', {})
817
- } catch (error) {
818
- throw new Error(`Failed to request password change: ${error.message}`)
819
- }
820
- }
821
-
822
- /**
823
- * Confirm a password change
824
- * @param {string} verificationCode - The verification code
825
- * @param {string} newPassword - The new password
826
- * @param {string} confirmPassword - The confirmation password
827
- * @returns {Promise<boolean>} True if the password was changed, false otherwise
828
- */
829
- async confirmPasswordChange (verificationCode, newPassword, confirmPassword) {
830
- try {
831
- return await this.call('users:confirm-password-change', {
832
- verificationCode,
833
- newPassword,
834
- confirmPassword
835
- })
836
- } catch (error) {
837
- throw new Error(`Failed to confirm password change: ${error.message}`)
838
- }
839
- }
840
-
841
- async updateUserProfile (profileData) {
842
- this._requireReady()
843
- try {
844
- return await this.call('users:update-profile', profileData)
845
- } catch (error) {
846
- throw new Error(`Failed to update user profile: ${error.message}`)
847
- }
848
- }
849
-
850
- /**
851
- * Duplicate a project
852
- * @param {string} projectId - The ID of the project to duplicate
853
- * @param {string} newName - The new name of the project (optional)
854
- * @param {string} newKey - The new key of the project (optional)
855
- * @returns {Promise<Object>} The duplicated project
856
- */
857
- async duplicateProject (projectId, newName, newKey, targetUserId) {
858
- this._requireReady()
859
- try {
860
- return await this.call('projects:duplicate', {
861
- projectId,
862
- newName,
863
- newKey,
864
- targetUserId
865
- })
866
- } catch (error) {
867
- throw new Error(`Failed to duplicate project: ${error.message}`)
868
- }
869
- }
870
-
871
- /**
872
- * List available subscription plans
873
- * @param {Object} options - Options for filtering plans
874
- * @param {number} options.page - Page number (default: 1)
875
- * @param {number} options.limit - Number of plans per page (default: 20)
876
- * @param {string} options.status - Filter plans by status (admin only)
877
- * @param {boolean} options.includeSubscriptionCounts - Include subscription counts (admin only)
878
- * @param {string} options.sortBy - Field to sort by (default: 'displayOrder')
879
- * @param {string} options.sortOrder - Sort order ('asc' or 'desc', default: 'asc')
880
- * @returns {Promise<Object>} List of plans with pagination info
881
- */
882
- async listPlans(options = {}) {
883
- this._requireReady();
884
- try {
885
- return await this.call('plans:list', options);
886
- } catch (error) {
887
- throw new Error(`Failed to list plans: ${error.message}`);
888
- }
889
- }
890
-
891
- /**
892
- * Subscribe to a plan
893
- * @param {string} planId - ID of the plan to subscribe to
894
- * @param {Object} options - Options for the subscription
895
- * @param {string} options.paymentMethod - Payment method
896
- * @param {Object} options.billingAddress - Billing address
897
- * @param {string} options.stripeCustomerId - Stripe customer ID
898
- * @param {string} options.stripeSubscriptionId - Stripe subscription ID
899
- * @returns {Promise<Object>} Subscription details
900
- */
901
- async subscribeToPlan(planId, options = {}) {
902
- this._requireReady()
903
- try {
904
- return await this.call('subscriptions:create', { planId, ...options })
905
- } catch (error) {
906
- throw new Error(`Failed to subscribe to plan: ${error.message}`);
907
- }
908
- }
909
-
910
- /**
911
- * Get details of user's current subscription
912
- * @param {string} subscriptionId - ID of the subscription to get details for
913
- * @returns {Promise<Object>} Subscription details
914
- */
915
- async getSubscriptionDetails(subscriptionId) {
916
- this._requireReady();
917
- try {
918
- return await this.call('subscriptions:details', {
919
- subscriptionId
920
- });
921
- } catch (error) {
922
- throw new Error(`Failed to get subscription details: ${error.message}`);
923
- }
924
- }
925
-
926
- /**
927
- * Check if the current subscription is active
928
- * @param {string} subscriptionId - ID of the subscription to check
929
- * @returns {Promise<Object>} Subscription status info
930
- */
931
- async checkSubscriptionStatus(subscriptionId) {
932
- this._requireReady();
933
- try {
934
- return await this.call('subscriptions:check-status', {
935
- subscriptionId
936
- });
937
- } catch (error) {
938
- throw new Error(`Failed to check subscription status: ${error.message}`);
939
- }
940
- }
941
-
942
- /**
943
- * Upgrade the current subscription to a new plan
944
- * @param {string} planId - ID of the plan to upgrade to
945
- * @param {string} stripeSubscriptionId - ID of the Stripe subscription to upgrade
946
- * @returns {Promise<Object>} Updated subscription details
947
- */
948
- async upgradeSubscription(planId, stripeSubscriptionId) {
949
- this._requireReady();
950
- try {
951
- return await this.call('subscriptions:upgrade', {
952
- planId,
953
- stripeSubscriptionId
954
- });
955
- } catch (error) {
956
- throw new Error(`Failed to upgrade subscription: ${error.message}`);
957
- }
958
- }
959
-
960
- /**
961
- * Downgrade the current subscription to a new plan
962
- * @param {string} planId - ID of the plan to downgrade to
963
- * @param {boolean} applyImmediately - Whether to apply the downgrade immediately
964
- * @param {string} stripeSubscriptionId - ID of the Stripe subscription to downgrade
965
- * @returns {Promise<Object>} Updated subscription details
966
- */
967
- async downgradeSubscription(planId, stripeSubscriptionId, applyImmediately = false) {
968
- this._requireReady();
969
- try {
970
- return await this.call('subscriptions:downgrade', {
971
- planId,
972
- applyImmediately,
973
- stripeSubscriptionId
974
- });
975
- } catch (error) {
976
- throw new Error(`Failed to downgrade subscription: ${error.message}`);
977
- }
978
- }
979
-
980
- /**
981
- * Cancel the current subscription
982
- * @param {boolean} cancelImmediately - Whether to cancel immediately or at period end
983
- * @param {string} reason - Reason for cancellation
984
- * @returns {Promise<Object>} Result of cancellation
985
- */
986
- async cancelSubscription(cancelImmediately = false, reason = '') {
987
- this._requireReady();
988
- try {
989
- return await this.call('subscriptions:cancel', {
990
- cancelImmediately,
991
- reason
992
- });
993
- } catch (error) {
994
- throw new Error(`Failed to cancel subscription: ${error.message}`);
995
- }
996
- }
997
-
998
- /**
999
- * Reactivate a subscription that was scheduled for cancellation
1000
- * @returns {Promise<Object>} Updated subscription details
1001
- */
1002
- async reactivateSubscription(stripeSubscriptionId) {
1003
- this._requireReady();
1004
- try {
1005
- return await this.call('subscriptions:reactivate', {
1006
- stripeSubscriptionId
1007
- });
1008
- } catch (error) {
1009
- throw new Error(`Failed to reactivate subscription: ${error.message}`);
1010
- }
1011
- }
1012
-
1013
- /**
1014
- * Generate an invoice for the current subscription
1015
- * @param {string} subscriptionId - ID of the subscription to generate the invoice for
1016
- * @param {boolean} forceGenerate - Whether to force the generation of the invoice
1017
- * @param {Array} customItems - Custom items to add to the invoice
1018
- * @returns {Promise<Object>} Generated invoice
1019
- */
1020
- async generateInvoice(subscriptionId, forceGenerate = false, customItems = []) {
1021
- this._requireReady();
1022
- try {
1023
- return await this.call('billing:generate-invoice', {
1024
- subscriptionId,
1025
- forceGenerate,
1026
- customItems
1027
- });
1028
- } catch (error) {
1029
- throw new Error(`Failed to generate invoice: ${error.message}`);
1030
- }
1031
- }
1032
-
1033
- /**
1034
- * Get usage report for the current subscription
1035
- * @param {string} subscriptionId - ID of the subscription to get the usage report for
1036
- * @param {boolean} forceRefresh - Whether to force the refresh of the usage report
1037
- * @returns {Promise<Object>} Usage report
1038
- */
1039
- async getUsageReport(subscriptionId, forceRefresh = false) {
1040
- this._requireReady();
1041
- try {
1042
- return await this.call('subscriptions:get-usage-report', {
1043
- subscriptionId,
1044
- forceRefresh
1045
- });
1046
- } catch (error) {
1047
- throw new Error(`Failed to get usage report: ${error.message}`);
1048
- }
1049
- }
1050
-
1051
- /**
1052
- * Invite a user to be an account owner for the current subscription
1053
- * @param {string} email - Email of the user to invite
1054
- * @param {string} name - Name of the user to invite
1055
- * @param {string} callbackUrl - URL to redirect the user to after accepting the invitation
1056
- * @returns {Promise<Object>} Result of invitation
1057
- */
1058
- async inviteAccountOwner(email, name, callbackUrl) {
1059
- this._requireReady();
1060
- try {
1061
- return await this.call('subscriptions:invite', {
1062
- email,
1063
- name,
1064
- callbackUrl
1065
- });
1066
- } catch (error) {
1067
- throw new Error(`Failed to invite account owner: ${error.message}`);
1068
- }
1069
- }
1070
-
1071
- /**
1072
- * Accept an invitation to become an account owner
1073
- * @param {string} token - Invitation token
1074
- * @returns {Promise<Object>} Result of accepting invitation
1075
- */
1076
- async acceptOwnerInvitation(token) {
1077
- this._requireReady();
1078
- try {
1079
- return await this.call('subscriptions:accept-owner-invitation', {
1080
- token
1081
- });
1082
- } catch (error) {
1083
- throw new Error(`Failed to accept owner invitation: ${error.message}`);
1084
- }
1085
- }
1086
-
1087
- /**
1088
- * Remove an account owner from the current subscription
1089
- * @param {string} userId - ID of the user to remove
1090
- * @returns {Promise<Object>} Result of removal
1091
- */
1092
- async removeAccountOwner(userId) {
1093
- this._requireReady();
1094
- try {
1095
- return await this.call('subscriptions:remove-account-owner', {
1096
- targetUserId: userId
1097
- });
1098
- } catch (error) {
1099
- throw new Error(`Failed to remove account owner: ${error.message}`);
1100
- }
1101
- }
1102
-
1103
- /**
1104
- * Check if a resource limit has been reached
1105
- * @param {string} resourceType - Type of resource to check
1106
- * @param {string} projectId - ID of the project to check the resource limit for
1107
- * @param {number} quantity - Amount being requested
1108
- * @param {string} userId - ID of user to check (admin only, defaults to current user)
1109
- * @returns {Promise<Object>} Result of check
1110
- */
1111
- async checkResourceLimit(resourceType, projectId, quantity = 1, userId = '') {
1112
- this._requireReady();
1113
- try {
1114
- return await this.call('subscriptions:check-resource-limit', {
1115
- resourceType,
1116
- projectId,
1117
- quantity,
1118
- userId
1119
- });
1120
- } catch (error) {
1121
- throw new Error(`Failed to check resource limit: ${error.message}`);
1122
- }
1123
- }
1124
-
1125
- /**
1126
- * Check if a user has access to a feature
1127
- * @param {string} featureKey - Key of the feature to check
1128
- * @param {string} userId - ID of user to check (admin only, defaults to current user)
1129
- * @returns {Promise<Object>} Access check result
1130
- */
1131
- async checkFeatureAccess(featureKey, userId) {
1132
- this._requireReady();
1133
- try {
1134
- return await this.call('features:check-access', {
1135
- featureKey,
1136
- userId
1137
- });
1138
- } catch (error) {
1139
- throw new Error(`Failed to check feature access: ${error.message}`);
1140
- }
1141
- }
1142
-
1143
- /**
1144
- * Get all features available to the current user
1145
- * @returns {Promise<Object>} Available features
1146
- */
1147
- async getUserFeatures() {
1148
- this._requireReady();
1149
- try {
1150
- return await this.call('subscriptions:get-user-features', {});
1151
- } catch (error) {
1152
- throw new Error(`Failed to get user features: ${error.message}`);
1153
- }
1154
- }
1155
-
1156
- /**
1157
- * Get all available features across all plans
1158
- * @param {string} subscriptionId - ID of the subscription to get the available features for
1159
- * @param {string} userId - ID of user to get the available features for (admin only, defaults to current user)
1160
- * @param {boolean} includeDetails - Whether to include details about the features
1161
- * @returns {Promise<Object>} Available features
1162
- */
1163
- async getAvailableFeatures(subscriptionId, userId = '', includeDetails = false) {
1164
- this._requireReady();
1165
- try {
1166
- return await this.call('features:get-available', {
1167
- subscriptionId,
1168
- userId,
1169
- includeDetails
1170
- });
1171
- } catch (error) {
1172
- throw new Error(`Failed to get available features: ${error.message}`);
1173
- }
1174
- }
1175
-
1176
- /**
1177
- * List all feature flags (admin only)
1178
- * @param {Object} options - Options for listing feature flags
1179
- * @param {number} options.page - Page number
1180
- * @param {number} options.limit - Number of items per page
1181
- * @param {string} options.status - Filter by status
1182
- * @returns {Promise<Object>} List of feature flags
1183
- */
1184
- async listFeatureFlags(options = {}) {
1185
- this._requireReady();
1186
- try {
1187
- return await this.call('features:list', options);
1188
- } catch (error) {
1189
- throw new Error(`Failed to list feature flags: ${error.message}`);
1190
- }
1191
- }
1192
-
1193
- /**
1194
- * Update a feature flag (admin only)
1195
- * @param {string} flagId - ID of the feature flag to update
1196
- * @param {Object} options - Update data
1197
- * @param {string} options.key - Key of the feature flag
1198
- * @param {string} options.name - Name of the feature flag
1199
- * @param {string} options.description - Description of the feature flag
1200
- * @param {string} options.defaultValue - Default value of the feature flag
1201
- * @param {Object} options.planOverrides - Plan overrides for the feature flag
1202
- * @param {Object} options.userOverrides - User overrides for the feature flag
1203
- * @param {string} options.status - Status of the feature flag
1204
- * @returns {Promise<Object>} Updated feature flag
1205
- */
1206
- async updateFeatureFlag(flagId, options) {
1207
- this._requireReady();
1208
- try {
1209
- return await this.call('features:update', {
1210
- flagId,
1211
- ...options
1212
- });
1213
- } catch (error) {
1214
- throw new Error(`Failed to update feature flag: ${error.message}`);
1215
- }
1216
- }
1217
-
1218
- /**
1219
- * Remove a feature flag (admin only)
1220
- * @param {string} flagId - ID of the feature flag to remove
1221
- * @returns {Promise<Object>} Result of removal
1222
- */
1223
- async removeFeatureFlag(flagId) {
1224
- this._requireReady();
1225
- try {
1226
- return await this.call('features:remove', {
1227
- flagId
1228
- });
1229
- } catch (error) {
1230
- throw new Error(`Failed to remove feature flag: ${error.message}`);
1231
- }
1232
- }
1233
-
1234
- /**
1235
- * Batch update feature flags (admin only)
1236
- * @param {Array} operations - Array of feature flag operations
1237
- * @param {string} operations.flagId - ID of the feature flag to update
1238
- * @param {string} operations.key - Key of the feature flag
1239
- * @param {string} operations.name - Name of the feature flag
1240
- * @param {string} operations.description - Description of the feature flag
1241
- * @param {boolean} operations.defaultValue - Default value of the feature flag
1242
- * @param {Object} operations.planOverrides - Plan overrides for the feature flag
1243
- * @param {Object} operations.userOverrides - User overrides for the feature flag
1244
- * @param {string} operations.status - Status of the feature flag
1245
- * @returns {Promise<Object>} Result of batch update
1246
- */
1247
- async batchUpdateFeatureFlags(operations) {
1248
- this._requireReady();
1249
- try {
1250
- return await this.call('features:batch-update', {
1251
- operations
1252
- });
1253
- } catch (error) {
1254
- throw new Error(`Failed to batch update feature flags: ${error.message}`);
1255
- }
1256
- }
1257
-
1258
- /**
1259
- * Update plan details (admin only)
1260
- * @param {string} planId - ID of the plan to update
1261
- * @param {Object} data - Update data
1262
- * @param {string} data.name - Display name (e.g., "Pro", "Team")
1263
- * @param {string} data.description - Marketing description
1264
- * @param {number} data.price - Monthly price in cents
1265
- * @param {number} data.annualPrice - Annual price in cents (discounted)
1266
- * @param {number} data.lifetimePrice - One-time lifetime price in cents
1267
- * @param {string} data.billingType - Payment type
1268
- * @param {string} data.status - Status
1269
- * @param {Object} data.features - Detailed feature configuration
1270
- * @returns {Promise<Object>} Updated plan
1271
- */
1272
- async updatePlanDetails(planId, data) {
1273
- this._requireReady();
1274
- try {
1275
- return await this.call('plans:update-details', {
1276
- planId,
1277
- data
1278
- });
1279
- } catch (error) {
1280
- throw new Error(`Failed to update plan details: ${error.message}`);
1281
- }
1282
- }
1283
-
1284
- /**
1285
- * Update plan status (admin only)
1286
- * @param {string} planId - ID of the plan to update
1287
- * @param {string} status - New status
1288
- * @returns {Promise<Object>} Updated plan
1289
- */
1290
- async updatePlanStatus(planId, status) {
1291
- this._requireReady();
1292
- try {
1293
- return await this.call('plans:update-status', {
1294
- planId,
1295
- status
1296
- });
1297
- } catch (error) {
1298
- throw new Error(`Failed to update plan status: ${error.message}`);
1299
- }
1300
- }
1301
- }