@symbo.ls/sdk 3.1.1 → 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +174 -13
  2. package/dist/cjs/config/environment.js +32 -42
  3. package/dist/cjs/index.js +31 -24
  4. package/dist/cjs/services/AIService.js +3 -3
  5. package/dist/cjs/services/AuthService.js +44 -3
  6. package/dist/cjs/services/BasedService.js +530 -24
  7. package/dist/cjs/services/CollabService.js +420 -0
  8. package/dist/cjs/services/CoreService.js +2295 -0
  9. package/dist/cjs/services/SocketService.js +207 -59
  10. package/dist/cjs/services/SymstoryService.js +135 -49
  11. package/dist/cjs/services/index.js +8 -16
  12. package/dist/cjs/state/RootStateManager.js +86 -0
  13. package/dist/cjs/state/rootEventBus.js +65 -0
  14. package/dist/cjs/utils/CollabClient.js +157 -0
  15. package/dist/cjs/utils/TokenManager.js +409 -0
  16. package/dist/cjs/utils/basedQuerys.js +120 -0
  17. package/dist/cjs/utils/jsonDiff.js +103 -0
  18. package/dist/cjs/utils/permission.js +4 -4
  19. package/dist/cjs/utils/services.js +133 -69
  20. package/dist/cjs/utils/symstoryClient.js +33 -2
  21. package/dist/esm/config/environment.js +32 -42
  22. package/dist/esm/index.js +20586 -11525
  23. package/dist/esm/services/AIService.js +3 -3
  24. package/dist/esm/services/AuthService.js +48 -7
  25. package/dist/esm/services/BasedService.js +676 -65
  26. package/dist/esm/services/CollabService.js +18028 -0
  27. package/dist/esm/services/CoreService.js +2827 -0
  28. package/dist/esm/services/SocketService.js +323 -58
  29. package/dist/esm/services/SymstoryService.js +287 -111
  30. package/dist/esm/services/index.js +20456 -11470
  31. package/dist/esm/state/RootStateManager.js +102 -0
  32. package/dist/esm/state/rootEventBus.js +47 -0
  33. package/dist/esm/utils/CollabClient.js +17483 -0
  34. package/dist/esm/utils/TokenManager.js +395 -0
  35. package/dist/esm/utils/basedQuerys.js +120 -0
  36. package/dist/esm/utils/jsonDiff.js +6096 -0
  37. package/dist/esm/utils/permission.js +4 -4
  38. package/dist/esm/utils/services.js +133 -69
  39. package/dist/esm/utils/symstoryClient.js +63 -43
  40. package/dist/esm/utils/validation.js +89 -19
  41. package/dist/node/config/environment.js +32 -42
  42. package/dist/node/index.js +37 -28
  43. package/dist/node/services/AIService.js +3 -3
  44. package/dist/node/services/AuthService.js +44 -3
  45. package/dist/node/services/BasedService.js +531 -25
  46. package/dist/node/services/CollabService.js +401 -0
  47. package/dist/node/services/CoreService.js +2266 -0
  48. package/dist/node/services/SocketService.js +197 -59
  49. package/dist/node/services/SymstoryService.js +135 -49
  50. package/dist/node/services/index.js +8 -16
  51. package/dist/node/state/RootStateManager.js +57 -0
  52. package/dist/node/state/rootEventBus.js +46 -0
  53. package/dist/node/utils/CollabClient.js +128 -0
  54. package/dist/node/utils/TokenManager.js +390 -0
  55. package/dist/node/utils/basedQuerys.js +120 -0
  56. package/dist/node/utils/jsonDiff.js +74 -0
  57. package/dist/node/utils/permission.js +4 -4
  58. package/dist/node/utils/services.js +133 -69
  59. package/dist/node/utils/symstoryClient.js +33 -2
  60. package/package.json +23 -14
  61. package/src/config/environment.js +33 -42
  62. package/src/index.js +45 -28
  63. package/src/services/AIService.js +3 -3
  64. package/src/services/AuthService.js +52 -3
  65. package/src/services/BasedService.js +603 -23
  66. package/src/services/CollabService.js +491 -0
  67. package/src/services/CoreService.js +2548 -0
  68. package/src/services/SocketService.js +227 -59
  69. package/src/services/SymstoryService.js +150 -64
  70. package/src/services/index.js +7 -14
  71. package/src/state/RootStateManager.js +71 -0
  72. package/src/state/rootEventBus.js +48 -0
  73. package/src/utils/CollabClient.js +161 -0
  74. package/src/utils/TokenManager.js +462 -0
  75. package/src/utils/basedQuerys.js +123 -0
  76. package/src/utils/jsonDiff.js +109 -0
  77. package/src/utils/permission.js +4 -4
  78. package/src/utils/services.js +144 -69
  79. package/src/utils/symstoryClient.js +36 -2
  80. package/dist/cjs/services/SocketIOService.js +0 -309
  81. package/dist/esm/services/SocketIOService.js +0 -467
  82. package/dist/node/services/SocketIOService.js +0 -280
  83. package/src/services/SocketIOService.js +0 -356
@@ -1,9 +1,10 @@
1
1
  import { BaseService } from './BaseService.js'
2
- import { buildProjectQuery, buildUserQuery } from '../utils/basedQuerys.js'
2
+ import { buildProjectQuery, buildUserQuery, buildGetProjectsByKeysQuery, buildGetUserDataQuery, buildGetProjectDataQuery, buildGetProjectByKeyDataQuery } from '../utils/basedQuerys.js'
3
3
  import Based from '@based/client'
4
4
  import { isFunction, isString } from '@domql/utils'
5
5
  import environment from '../config/environment.js'
6
6
 
7
+ // DEPRECATED
7
8
  export class BasedService extends BaseService {
8
9
  constructor (config) {
9
10
  super(config)
@@ -122,7 +123,7 @@ export class BasedService extends BaseService {
122
123
  joinedAt: Date.now()
123
124
  })
124
125
 
125
- await Promise.all([
126
+ return await Promise.all([
126
127
  this._client.call('db:set', {
127
128
  $id: userId,
128
129
  memberProjects: { $add: membershipId }
@@ -132,7 +133,6 @@ export class BasedService extends BaseService {
132
133
  members: { $add: membershipId }
133
134
  })
134
135
  ])
135
- return await this._client.call('db:set', obj)
136
136
  } catch (error) {
137
137
  throw new Error(`Failed to set bucket: ${error.message}`)
138
138
  }
@@ -176,6 +176,7 @@ export class BasedService extends BaseService {
176
176
  }
177
177
  }
178
178
 
179
+ // DEPRECATED
179
180
  async fetchUser (userId) {
180
181
  if (!userId) {
181
182
  throw new Error('User Id is required')
@@ -184,15 +185,158 @@ export class BasedService extends BaseService {
184
185
  }
185
186
 
186
187
  async getUser (userId) {
187
- this._requireReady()
188
188
  if (!userId) {
189
- throw new Error('User Id is required')
189
+ throw new Error("UserId is required")
190
+ }
191
+
192
+ // Get user data and memberProjects in a single query
193
+ const userData = await this._client
194
+ .query("db", buildGetUserDataQuery(userId))
195
+ .get()
196
+
197
+ // Extract user data
198
+ const user = {
199
+ id: userData.id,
200
+ name: userData.name,
201
+ email: userData.email,
202
+ username: userData.username,
203
+ globalRole: userData.globalRole,
204
+ createdAt: userData.createdAt,
205
+ updatedAt: userData.updatedAt
206
+ }
207
+
208
+ if (!user) {
209
+ throw new Error("User not found")
210
+ }
211
+
212
+ let memberProjects = []
213
+
214
+ if (userData.memberProjects && userData.memberProjects.length > 0) {
215
+ // Extract project keys from the direct query
216
+ const projectKeys = userData.memberProjects
217
+ .filter(membership => membership.project && membership.project.key)
218
+ .map(membership => membership.project.key)
219
+
220
+ if (projectKeys.length > 0) {
221
+ // Fetch projects in chunks to avoid PayloadTooLarge errors
222
+ const allProjects = await this._fetchProjectsByKeysInChunks(projectKeys)
223
+
224
+ // Map the project data with original membership data
225
+ memberProjects = userData.memberProjects
226
+ .filter(membership => membership.project && membership.project.key)
227
+ .map(membership => {
228
+ const projectKey = membership.project.key
229
+ const correctProject = allProjects.find(
230
+ p => p.key === projectKey
231
+ )
232
+
233
+ return {
234
+ project: correctProject || membership.project,
235
+ role: membership.role,
236
+ updatedAt: membership.updatedAt,
237
+ createdAt: membership.createdAt
238
+ }
239
+ })
240
+ }
241
+ } else {
242
+ console.log(`[getUser] No member projects found with ID: ${userId}`)
243
+ }
244
+
245
+ // Format projects data
246
+ const formattedProjects =
247
+ memberProjects
248
+ .filter(membership => membership.project)
249
+ .map(membership => ({
250
+ id: membership.project.id,
251
+ name: membership.project.name,
252
+ key: membership.project.key,
253
+ thumbnail: membership.project.thumbnail,
254
+ icon: membership.project.icon,
255
+ tier: membership.project.tier,
256
+ visibility: membership.project.visibility,
257
+ access: membership.project.access,
258
+ role: membership.role,
259
+ joinedAt: membership.createdAt,
260
+ updatedAt: membership.updatedAt,
261
+ members: membership.project.members
262
+ })) || []
263
+
264
+ return {
265
+ id: userId,
266
+ name: user.name,
267
+ email: user.email,
268
+ username: user.username,
269
+ globalRole: user.globalRole,
270
+ projects: formattedProjects,
271
+ createdAt: user.createdAt,
272
+ updatedAt: user.updatedAt
190
273
  }
191
- try {
192
- return await this.call('users:get', { userId })
193
- } catch (error) {
194
- throw new Error(`Failed to get user: ${error.message}`)
274
+ }
275
+
276
+ /**
277
+ * Fetches projects by keys in chunks with adaptive chunk sizing
278
+ * @param {Array<string>} allKeys - All project keys to fetch
279
+ * @returns {Promise<Array>} - All projects found
280
+ */
281
+ async _fetchProjectsByKeysInChunks(allKeys) {
282
+ if (!allKeys.length) {return []}
283
+
284
+ // Initial chunk size and limits
285
+ const INITIAL_CHUNK_SIZE = 50
286
+ const MAX_CHUNK_SIZE = 1000
287
+ const MIN_CHUNK_SIZE = 10
288
+
289
+ // Track the optimal chunk size as we learn from responses
290
+ const optimalChunkSize = INITIAL_CHUNK_SIZE
291
+
292
+ /**
293
+ * Recursively process chunks of keys with adaptive sizing
294
+ * @param {Array<string>} keys - Keys to process
295
+ * @param {number} size - Current chunk size
296
+ * @returns {Promise<Array>} - Projects found
297
+ */
298
+ const processChunks = async (keys, size) => {
299
+ if (!keys.length) {return []}
300
+
301
+ const chunks = []
302
+ for (let i = 0; i < keys.length; i += size) {
303
+ chunks.push(keys.slice(i, i + size))
304
+ }
305
+
306
+ try {
307
+ const result = await this._client
308
+ .query("db", buildGetProjectsByKeysQuery(chunks[0]))
309
+ .get()
310
+
311
+ const newSize = Math.min(Math.floor(size * 1.5), MAX_CHUNK_SIZE)
312
+
313
+ const firstChunkProjects = result.projects || []
314
+
315
+ const remainingKeys = keys.slice(chunks[0].length)
316
+ const remainingProjects = await processChunks(remainingKeys, newSize)
317
+
318
+ return [...firstChunkProjects, ...remainingProjects]
319
+ } catch (error) {
320
+ if (error.message && error.message.includes('PayloadTooLarge')) {
321
+ const newSize = Math.max(Math.floor(size / 2), MIN_CHUNK_SIZE)
322
+ console.warn(`Reducing chunk size to ${newSize} due to PayloadTooLarge error`)
323
+
324
+ if (newSize === MIN_CHUNK_SIZE && chunks[0].length <= MIN_CHUNK_SIZE) {
325
+ console.error(`Cannot process chunk, skipping ${chunks[0].length} keys`)
326
+ const remainingKeys = keys.slice(chunks[0].length)
327
+ return processChunks(remainingKeys, newSize)
328
+ }
329
+
330
+ return processChunks(keys, newSize)
331
+ }
332
+ console.error(`Error fetching projects: ${error.message}`)
333
+ const remainingKeys = keys.slice(chunks[0].length)
334
+ return processChunks(remainingKeys, size)
335
+
336
+ }
195
337
  }
338
+
339
+ return await processChunks(allKeys, optimalChunkSize)
196
340
  }
197
341
 
198
342
  async getUserByEmail (email) {
@@ -368,6 +512,7 @@ export class BasedService extends BaseService {
368
512
 
369
513
  async createProject (projectData) {
370
514
  this._requireReady()
515
+
371
516
  try {
372
517
  return await this.call('projects:create', projectData)
373
518
  } catch (error) {
@@ -375,33 +520,32 @@ export class BasedService extends BaseService {
375
520
  }
376
521
  }
377
522
 
378
- async getProject (projectId, fields = { $all: true }) {
523
+ async getProject (projectId) {
379
524
  this._requireReady()
380
525
  if (!projectId) {
381
526
  throw new Error('Project ID is required')
382
527
  }
383
528
  try {
384
- return await this.call('projects:get', {
385
- projectId,
386
- fields: { ...fields }
387
- })
529
+ return await this._client
530
+ .query("db", buildGetProjectDataQuery(projectId))
531
+ .get()
388
532
  } catch (error) {
389
533
  throw new Error(`Failed to get project: ${error.message}`)
390
534
  }
391
535
  }
392
536
 
393
- async getProjectByKey (key, fields = { $all: true }) {
537
+ async getProjectByKey (key) {
394
538
  this._requireReady()
395
539
  try {
396
- return await this.call('projects:get', {
397
- key,
398
- fields: { ...fields }
399
- })
540
+ return await this._client
541
+ .query("db", buildGetProjectByKeyDataQuery(key))
542
+ .get()
400
543
  } catch (error) {
401
544
  throw new Error(`Failed to get project by key: ${error.message}`)
402
545
  }
403
546
  }
404
547
 
548
+ // DEPRECATED
405
549
  async fetchProject (projectId) {
406
550
  this._requireReady()
407
551
  if (!projectId) {
@@ -489,7 +633,9 @@ export class BasedService extends BaseService {
489
633
  pkg = 2,
490
634
  seats = 1,
491
635
  interval = 'monthly',
492
- plan = 'startup'
636
+ plan = 'startup',
637
+ successUrl = `${window.location.origin}/success`,
638
+ cancelUrl = `${window.location.origin}/pricing`
493
639
  }) {
494
640
  this._requireReady()
495
641
 
@@ -516,7 +662,9 @@ export class BasedService extends BaseService {
516
662
  return await this.call('checkout', {
517
663
  price,
518
664
  seats,
519
- projectId
665
+ projectId,
666
+ successUrl,
667
+ cancelUrl
520
668
  })
521
669
  } catch (error) {
522
670
  throw new Error(`Failed to checkout: ${error.message}`)
@@ -707,16 +855,448 @@ export class BasedService extends BaseService {
707
855
  * @param {string} newKey - The new key of the project (optional)
708
856
  * @returns {Promise<Object>} The duplicated project
709
857
  */
710
- async duplicateProject (projectId, newName, newKey) {
858
+ async duplicateProject (projectId, newName, newKey, targetUserId) {
711
859
  this._requireReady()
712
860
  try {
713
861
  return await this.call('projects:duplicate', {
714
862
  projectId,
715
863
  newName,
716
- newKey
864
+ newKey,
865
+ targetUserId
717
866
  })
718
867
  } catch (error) {
719
868
  throw new Error(`Failed to duplicate project: ${error.message}`)
720
869
  }
721
870
  }
871
+
872
+ /**
873
+ * List available subscription plans
874
+ * @param {Object} options - Options for filtering plans
875
+ * @param {number} options.page - Page number (default: 1)
876
+ * @param {number} options.limit - Number of plans per page (default: 20)
877
+ * @param {string} options.status - Filter plans by status (admin only)
878
+ * @param {boolean} options.includeSubscriptionCounts - Include subscription counts (admin only)
879
+ * @param {string} options.sortBy - Field to sort by (default: 'displayOrder')
880
+ * @param {string} options.sortOrder - Sort order ('asc' or 'desc', default: 'asc')
881
+ * @returns {Promise<Object>} List of plans with pagination info
882
+ */
883
+ async listPlans(options = {}) {
884
+ this._requireReady();
885
+ try {
886
+ return await this.call('plans:list', options);
887
+ } catch (error) {
888
+ throw new Error(`Failed to list plans: ${error.message}`);
889
+ }
890
+ }
891
+
892
+ /**
893
+ * Subscribe to a plan
894
+ * @param {string} planId - ID of the plan to subscribe to
895
+ * @param {Object} options - Options for the subscription
896
+ * @param {string} options.paymentMethod - Payment method
897
+ * @param {Object} options.billingAddress - Billing address
898
+ * @param {string} options.stripeCustomerId - Stripe customer ID
899
+ * @param {string} options.stripeSubscriptionId - Stripe subscription ID
900
+ * @returns {Promise<Object>} Subscription details
901
+ */
902
+ async subscribeToPlan(planId, options = {}) {
903
+ this._requireReady()
904
+ try {
905
+ return await this.call('subscriptions:create', { planId, ...options })
906
+ } catch (error) {
907
+ throw new Error(`Failed to subscribe to plan: ${error.message}`);
908
+ }
909
+ }
910
+
911
+ /**
912
+ * Get details of user's current subscription
913
+ * @param {string} subscriptionId - ID of the subscription to get details for
914
+ * @returns {Promise<Object>} Subscription details
915
+ */
916
+ async getSubscriptionDetails(subscriptionId) {
917
+ this._requireReady();
918
+ try {
919
+ return await this.call('subscriptions:details', {
920
+ subscriptionId
921
+ });
922
+ } catch (error) {
923
+ throw new Error(`Failed to get subscription details: ${error.message}`);
924
+ }
925
+ }
926
+
927
+ /**
928
+ * Check if the current subscription is active
929
+ * @param {string} subscriptionId - ID of the subscription to check
930
+ * @returns {Promise<Object>} Subscription status info
931
+ */
932
+ async checkSubscriptionStatus(subscriptionId) {
933
+ this._requireReady();
934
+ try {
935
+ return await this.call('subscriptions:check-status', {
936
+ subscriptionId
937
+ });
938
+ } catch (error) {
939
+ throw new Error(`Failed to check subscription status: ${error.message}`);
940
+ }
941
+ }
942
+
943
+ /**
944
+ * Upgrade the current subscription to a new plan
945
+ * @param {string} planId - ID of the plan to upgrade to
946
+ * @param {string} stripeSubscriptionId - ID of the Stripe subscription to upgrade
947
+ * @returns {Promise<Object>} Updated subscription details
948
+ */
949
+ async upgradeSubscription(planId, stripeSubscriptionId) {
950
+ this._requireReady();
951
+ try {
952
+ return await this.call('subscriptions:upgrade', {
953
+ planId,
954
+ stripeSubscriptionId
955
+ });
956
+ } catch (error) {
957
+ throw new Error(`Failed to upgrade subscription: ${error.message}`);
958
+ }
959
+ }
960
+
961
+ /**
962
+ * Downgrade the current subscription to a new plan
963
+ * @param {string} planId - ID of the plan to downgrade to
964
+ * @param {boolean} applyImmediately - Whether to apply the downgrade immediately
965
+ * @param {string} stripeSubscriptionId - ID of the Stripe subscription to downgrade
966
+ * @returns {Promise<Object>} Updated subscription details
967
+ */
968
+ async downgradeSubscription(planId, stripeSubscriptionId, applyImmediately = false) {
969
+ this._requireReady();
970
+ try {
971
+ return await this.call('subscriptions:downgrade', {
972
+ planId,
973
+ applyImmediately,
974
+ stripeSubscriptionId
975
+ });
976
+ } catch (error) {
977
+ throw new Error(`Failed to downgrade subscription: ${error.message}`);
978
+ }
979
+ }
980
+
981
+ /**
982
+ * Cancel the current subscription
983
+ * @param {boolean} cancelImmediately - Whether to cancel immediately or at period end
984
+ * @param {string} reason - Reason for cancellation
985
+ * @returns {Promise<Object>} Result of cancellation
986
+ */
987
+ async cancelSubscription(cancelImmediately = false, reason = '') {
988
+ this._requireReady();
989
+ try {
990
+ return await this.call('subscriptions:cancel', {
991
+ cancelImmediately,
992
+ reason
993
+ });
994
+ } catch (error) {
995
+ throw new Error(`Failed to cancel subscription: ${error.message}`);
996
+ }
997
+ }
998
+
999
+ /**
1000
+ * Reactivate a subscription that was scheduled for cancellation
1001
+ * @returns {Promise<Object>} Updated subscription details
1002
+ */
1003
+ async reactivateSubscription(stripeSubscriptionId) {
1004
+ this._requireReady();
1005
+ try {
1006
+ return await this.call('subscriptions:reactivate', {
1007
+ stripeSubscriptionId
1008
+ });
1009
+ } catch (error) {
1010
+ throw new Error(`Failed to reactivate subscription: ${error.message}`);
1011
+ }
1012
+ }
1013
+
1014
+ /**
1015
+ * Generate an invoice for the current subscription
1016
+ * @param {string} subscriptionId - ID of the subscription to generate the invoice for
1017
+ * @param {boolean} forceGenerate - Whether to force the generation of the invoice
1018
+ * @param {Array} customItems - Custom items to add to the invoice
1019
+ * @returns {Promise<Object>} Generated invoice
1020
+ */
1021
+ async generateInvoice(subscriptionId, forceGenerate = false, customItems = []) {
1022
+ this._requireReady();
1023
+ try {
1024
+ return await this.call('billing:generate-invoice', {
1025
+ subscriptionId,
1026
+ forceGenerate,
1027
+ customItems
1028
+ });
1029
+ } catch (error) {
1030
+ throw new Error(`Failed to generate invoice: ${error.message}`);
1031
+ }
1032
+ }
1033
+
1034
+ /**
1035
+ * Get usage report for the current subscription
1036
+ * @param {string} subscriptionId - ID of the subscription to get the usage report for
1037
+ * @param {boolean} forceRefresh - Whether to force the refresh of the usage report
1038
+ * @returns {Promise<Object>} Usage report
1039
+ */
1040
+ async getUsageReport(subscriptionId, forceRefresh = false) {
1041
+ this._requireReady();
1042
+ try {
1043
+ return await this.call('subscriptions:get-usage-report', {
1044
+ subscriptionId,
1045
+ forceRefresh
1046
+ });
1047
+ } catch (error) {
1048
+ throw new Error(`Failed to get usage report: ${error.message}`);
1049
+ }
1050
+ }
1051
+
1052
+ /**
1053
+ * Invite a user to be an account owner for the current subscription
1054
+ * @param {string} email - Email of the user to invite
1055
+ * @param {string} name - Name of the user to invite
1056
+ * @param {string} callbackUrl - URL to redirect the user to after accepting the invitation
1057
+ * @returns {Promise<Object>} Result of invitation
1058
+ */
1059
+ async inviteAccountOwner(email, name, callbackUrl) {
1060
+ this._requireReady();
1061
+ try {
1062
+ return await this.call('subscriptions:invite', {
1063
+ email,
1064
+ name,
1065
+ callbackUrl
1066
+ });
1067
+ } catch (error) {
1068
+ throw new Error(`Failed to invite account owner: ${error.message}`);
1069
+ }
1070
+ }
1071
+
1072
+ /**
1073
+ * Accept an invitation to become an account owner
1074
+ * @param {string} token - Invitation token
1075
+ * @returns {Promise<Object>} Result of accepting invitation
1076
+ */
1077
+ async acceptOwnerInvitation(token) {
1078
+ this._requireReady();
1079
+ try {
1080
+ return await this.call('subscriptions:accept-owner-invitation', {
1081
+ token
1082
+ });
1083
+ } catch (error) {
1084
+ throw new Error(`Failed to accept owner invitation: ${error.message}`);
1085
+ }
1086
+ }
1087
+
1088
+ /**
1089
+ * Remove an account owner from the current subscription
1090
+ * @param {string} userId - ID of the user to remove
1091
+ * @returns {Promise<Object>} Result of removal
1092
+ */
1093
+ async removeAccountOwner(userId) {
1094
+ this._requireReady();
1095
+ try {
1096
+ return await this.call('subscriptions:remove-account-owner', {
1097
+ targetUserId: userId
1098
+ });
1099
+ } catch (error) {
1100
+ throw new Error(`Failed to remove account owner: ${error.message}`);
1101
+ }
1102
+ }
1103
+
1104
+ /**
1105
+ * Check if a resource limit has been reached
1106
+ * @param {string} resourceType - Type of resource to check
1107
+ * @param {string} projectId - ID of the project to check the resource limit for
1108
+ * @param {number} quantity - Amount being requested
1109
+ * @param {string} userId - ID of user to check (admin only, defaults to current user)
1110
+ * @returns {Promise<Object>} Result of check
1111
+ */
1112
+ async checkResourceLimit(resourceType, projectId, quantity = 1, userId = '') {
1113
+ this._requireReady();
1114
+ try {
1115
+ return await this.call('subscriptions:check-resource-limit', {
1116
+ resourceType,
1117
+ projectId,
1118
+ quantity,
1119
+ userId
1120
+ });
1121
+ } catch (error) {
1122
+ throw new Error(`Failed to check resource limit: ${error.message}`);
1123
+ }
1124
+ }
1125
+
1126
+ /**
1127
+ * Check if a user has access to a feature
1128
+ * @param {string} featureKey - Key of the feature to check
1129
+ * @param {string} userId - ID of user to check (admin only, defaults to current user)
1130
+ * @returns {Promise<Object>} Access check result
1131
+ */
1132
+ async checkFeatureAccess(featureKey, userId) {
1133
+ this._requireReady();
1134
+ try {
1135
+ return await this.call('features:check-access', {
1136
+ featureKey,
1137
+ userId
1138
+ });
1139
+ } catch (error) {
1140
+ throw new Error(`Failed to check feature access: ${error.message}`);
1141
+ }
1142
+ }
1143
+
1144
+ /**
1145
+ * Get all features available to the current user
1146
+ * @returns {Promise<Object>} Available features
1147
+ */
1148
+ async getUserFeatures() {
1149
+ this._requireReady();
1150
+ try {
1151
+ return await this.call('subscriptions:get-user-features', {});
1152
+ } catch (error) {
1153
+ throw new Error(`Failed to get user features: ${error.message}`);
1154
+ }
1155
+ }
1156
+
1157
+ /**
1158
+ * Get all available features across all plans
1159
+ * @param {string} subscriptionId - ID of the subscription to get the available features for
1160
+ * @param {string} userId - ID of user to get the available features for (admin only, defaults to current user)
1161
+ * @param {boolean} includeDetails - Whether to include details about the features
1162
+ * @returns {Promise<Object>} Available features
1163
+ */
1164
+ async getAvailableFeatures(subscriptionId, userId = '', includeDetails = false) {
1165
+ this._requireReady();
1166
+ try {
1167
+ return await this.call('features:get-available', {
1168
+ subscriptionId,
1169
+ userId,
1170
+ includeDetails
1171
+ });
1172
+ } catch (error) {
1173
+ throw new Error(`Failed to get available features: ${error.message}`);
1174
+ }
1175
+ }
1176
+
1177
+ /**
1178
+ * List all feature flags (admin only)
1179
+ * @param {Object} options - Options for listing feature flags
1180
+ * @param {number} options.page - Page number
1181
+ * @param {number} options.limit - Number of items per page
1182
+ * @param {string} options.status - Filter by status
1183
+ * @returns {Promise<Object>} List of feature flags
1184
+ */
1185
+ async listFeatureFlags(options = {}) {
1186
+ this._requireReady();
1187
+ try {
1188
+ return await this.call('features:list', options);
1189
+ } catch (error) {
1190
+ throw new Error(`Failed to list feature flags: ${error.message}`);
1191
+ }
1192
+ }
1193
+
1194
+ /**
1195
+ * Update a feature flag (admin only)
1196
+ * @param {string} flagId - ID of the feature flag to update
1197
+ * @param {Object} options - Update data
1198
+ * @param {string} options.key - Key of the feature flag
1199
+ * @param {string} options.name - Name of the feature flag
1200
+ * @param {string} options.description - Description of the feature flag
1201
+ * @param {string} options.defaultValue - Default value of the feature flag
1202
+ * @param {Object} options.planOverrides - Plan overrides for the feature flag
1203
+ * @param {Object} options.userOverrides - User overrides for the feature flag
1204
+ * @param {string} options.status - Status of the feature flag
1205
+ * @returns {Promise<Object>} Updated feature flag
1206
+ */
1207
+ async updateFeatureFlag(flagId, options) {
1208
+ this._requireReady();
1209
+ try {
1210
+ return await this.call('features:update', {
1211
+ flagId,
1212
+ ...options
1213
+ });
1214
+ } catch (error) {
1215
+ throw new Error(`Failed to update feature flag: ${error.message}`);
1216
+ }
1217
+ }
1218
+
1219
+ /**
1220
+ * Remove a feature flag (admin only)
1221
+ * @param {string} flagId - ID of the feature flag to remove
1222
+ * @returns {Promise<Object>} Result of removal
1223
+ */
1224
+ async removeFeatureFlag(flagId) {
1225
+ this._requireReady();
1226
+ try {
1227
+ return await this.call('features:remove', {
1228
+ flagId
1229
+ });
1230
+ } catch (error) {
1231
+ throw new Error(`Failed to remove feature flag: ${error.message}`);
1232
+ }
1233
+ }
1234
+
1235
+ /**
1236
+ * Batch update feature flags (admin only)
1237
+ * @param {Array} operations - Array of feature flag operations
1238
+ * @param {string} operations.flagId - ID of the feature flag to update
1239
+ * @param {string} operations.key - Key of the feature flag
1240
+ * @param {string} operations.name - Name of the feature flag
1241
+ * @param {string} operations.description - Description of the feature flag
1242
+ * @param {boolean} operations.defaultValue - Default value of the feature flag
1243
+ * @param {Object} operations.planOverrides - Plan overrides for the feature flag
1244
+ * @param {Object} operations.userOverrides - User overrides for the feature flag
1245
+ * @param {string} operations.status - Status of the feature flag
1246
+ * @returns {Promise<Object>} Result of batch update
1247
+ */
1248
+ async batchUpdateFeatureFlags(operations) {
1249
+ this._requireReady();
1250
+ try {
1251
+ return await this.call('features:batch-update', {
1252
+ operations
1253
+ });
1254
+ } catch (error) {
1255
+ throw new Error(`Failed to batch update feature flags: ${error.message}`);
1256
+ }
1257
+ }
1258
+
1259
+ /**
1260
+ * Update plan details (admin only)
1261
+ * @param {string} planId - ID of the plan to update
1262
+ * @param {Object} data - Update data
1263
+ * @param {string} data.name - Display name (e.g., "Pro", "Team")
1264
+ * @param {string} data.description - Marketing description
1265
+ * @param {number} data.price - Monthly price in cents
1266
+ * @param {number} data.annualPrice - Annual price in cents (discounted)
1267
+ * @param {number} data.lifetimePrice - One-time lifetime price in cents
1268
+ * @param {string} data.billingType - Payment type
1269
+ * @param {string} data.status - Status
1270
+ * @param {Object} data.features - Detailed feature configuration
1271
+ * @returns {Promise<Object>} Updated plan
1272
+ */
1273
+ async updatePlanDetails(planId, data) {
1274
+ this._requireReady();
1275
+ try {
1276
+ return await this.call('plans:update-details', {
1277
+ planId,
1278
+ data
1279
+ });
1280
+ } catch (error) {
1281
+ throw new Error(`Failed to update plan details: ${error.message}`);
1282
+ }
1283
+ }
1284
+
1285
+ /**
1286
+ * Update plan status (admin only)
1287
+ * @param {string} planId - ID of the plan to update
1288
+ * @param {string} status - New status
1289
+ * @returns {Promise<Object>} Updated plan
1290
+ */
1291
+ async updatePlanStatus(planId, status) {
1292
+ this._requireReady();
1293
+ try {
1294
+ return await this.call('plans:update-status', {
1295
+ planId,
1296
+ status
1297
+ });
1298
+ } catch (error) {
1299
+ throw new Error(`Failed to update plan status: ${error.message}`);
1300
+ }
1301
+ }
722
1302
  }